Compare commits

..
Author SHA1 Message Date
queao 7d4e752769 more wallpapers mwahahahahahahaha 2025-04-29 14:20:53 +10:00
queao d245ae12c7 Merge branch 'refs/heads/main' into pc-wallpapers 2025-04-29 13:27:19 +10:00
Test b6966c0e1f walls 2025-04-26 02:27:02 -04:00
Test c40b8ae7b1 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into pc-wallpapers 2025-04-26 02:07:40 -04:00
queao af49e14cfb cave wallpaper 2025-03-24 14:41:53 +10:00
8132 changed files with 128764 additions and 838291 deletions
+3 -14
View File
@@ -2,25 +2,14 @@
- Rebuilt Pokémon AI to use Minecraft's Brain system, allowing for more complex behaviours and interactions.
- As an early pass of this, pastured Pokémon will now sleep when appropriate, and may come up to you when they see you.
- Probably also added a whole bunch of bugs.
- Dog Pokémon (Growlithe, Arcanine, etc.) intimidate Skeletons.
- 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.
- Growlithe and Arcanine intimidate Skeletons.
- Meowth intimidate Creepers and Phantoms.
- 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.
### Developer
- 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.
- Added very many behaviours to choose from.
- Added many default behaviours.
- Removed many NPC presets that now exist as behaviours. This is a breaking change if you are using NPCs with these presets.
- NPCs with these presets should be modified to instead list under `"behaviours"` all the equivalent behaviours.
-24
View File
@@ -4,30 +4,6 @@
- There is a different pot for each apricorn colour because at this point I think we may have an addiction.
- Added Aprijuice, made from the Campfire Pot using apricorns with seasoning to give flavours.
- Aprijuice can be fed to ride-able Pokémon to improve their riding characteristics, depending on the flavour and the Pokémon's nature.
- Added Lure Cakes, made from the Campfire Pot using honey and berries.
- Lure Cakes can be used to modify the spawn rate of Pokémon in the wild.
- Added the Saccharine Tree, which can be found in the wild and is used to obtain Honey Bottles.
- The leaves of the tree can be harvested to obtain honey, which can be used in the Campfire Pot to make Lure Cakes.
- The Log of the tree can be interacted with using a honey bottle to create a Honey-Slathered Saccharine Log, which increases the change of spawning a Pokémon with a Hidden Ability.
- Added Hearty Grains
- Clicking on the Progress Arrow will show the JEI Recipe Book for the Campfire Pot, which will show all recipes that can be made with it.
- Added a Ton of Redstone Compatibility.
- Dispenser can now Honey and Unhoney:
- Saccharine Log
- Saccharine Leaves
- Redstone can Close and Open the Lid of a Cooking Pot
- Powered = Closed
- Unpowered = Open
- Hoppers can be used to insert items into a Cooking pot
- Top = Seasonals
- All Sides = Crafting
- Bottom = Extract the Result
- Observer will recognise when a Cooking pot is Closed or Opened
- Comparator Calculates a Signal Strength depending on the items inside the cooking pot
- Sculk Sensor
- Saccharine Leaves
- Opening and Closing the Cooking Pot
- Saccharine Log
### Datapack and Addon Changes
- Renamed the fishing_baits datapack folder to spawn_bait_effects.
+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.
+1 -17
View File
@@ -1,28 +1,12 @@
### 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.
- 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.
-17
View File
@@ -1,17 +0,0 @@
### Additions
- Pokémon can now spawn in herds, either as distinct herd groupings or simply several Pokémon spawning at once.
### 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.
- Renamed heaps of things in the spawning system to make more sense.
- SpawningContext is now SpawnablePosition
- WorldSlice is SpawningZone
- SpawningProspector is now SpawningZoneGenerator
- Renamed things in Spawn Rules to go with the other renames:
- contextSelector is now spawnablePositionSelector
- context is now spawnable_position
+20 -611
View File
@@ -2,102 +2,42 @@
## [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.
- Added `blacklisted_items_to_hold` and `whitelisted_items_to_hold` tags to allow for controlling which items players can give to their Pokémon. If the whitelist is empty, it will consider all item as allowed (unless they are in the blacklist).
- Pokémon now follow the mouse cursor on the Summary screen, with an option to disable this in the settings.
- Added Datapackable Item interactions with Pokemon
- 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.
- Added lang keys for all moves and abilities up to Generation 9.
- Fixed Moon Ball moon phase logic to actually work correctly
- 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 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
- Added Tatami blocks and Tatami Mat blocks, made from Hearty Grain, for decorating builds
- Added `/pcsearch <player> <pokemonProperties>` command that searches for a specific Pokémon within a player's PC.
- Added `/pctake <player> <box> <slot>` command that takes a specific Pokémon from a player's PC. Removes the pokemon if target is self or ran from the server.
- 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
- 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.
- Added new advancements Didn't Stop To Think, We Need To Cook, and Pokémon Jockey!.
- 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.
- Added [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) support for items held by Pokémon.
### Pokémon Added
#### Gen 2
- Marill
- Azumarill
- Hoppip
- Skiploom
- Jumpluff
- Dunsparce
- Togepi
- Togetic
- Unown
- Houndour
- Houndoom
- Ho-Oh
- Lugia
- Alola Bias Pichu (built-in resourcepack)
#### Gen 3
- Azurill
- Meditite
- Medicham
- Electrike
- Manectric
- Spoink
- Grumpig
- Swablu
- Altaria
- Snorunt
- Glalie
- Latias
- Latios
#### Gen 4
- Bronzor
- Bronzong
- Croagunk
- Toxicroak
- Froslass
- Glameow
- Purugly
- Skorupi
- Drapion
- Togekiss
#### Gen 5
- Pansage
@@ -110,194 +50,40 @@
- Musharna
- Blitzle
- Zebstrika
- Drilbur
- Excadrill
- Trubbish
- Garbodor
- Gothita
- Gothorita
- Gothitelle
- Solosis
- Duosion
- Reuniclus
- Tynamo
- Eelektrik
- Eelektross
- Axew
- Fraxure
- Haxorus
- Mienfoo
- Mienshao
- Sewaddle
- Swadloon
- Leavanny
- Druddigon
#### Gen 6
- Skiddo
- Gogoat
- Espurr
- Meowstic
- Inkay
- Malamar
- Hawlucha
- 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
- Gumshoos
- Drampa
- Alolan Grimer
- Alolan Muk
- Togedemaru
#### Gen 8
- Skwovet
- Greedent
- Chewtle
- Drednaw
- Toxel
- Toxtricity
- Morpeko
- Silicobra
- Sandaconda
- Sinistea
- Polteageist
- Galarian Corsola
- Cursola
- Mr. Rime
- Galarian Mr. Mime
- Clobbopus
- Grapploct
- Galarian Weezing
#### Gen 9
- Smoliv
- Dolliv
- Arboliva
- Tarountula
- Spidops
- Orthworm
- Dudunsparce
- Cyclizar
- Poltchageist
- Sinistcha
- Capsakid
- Scovillain
- Tadbulb
- Bellibolt
- Toedscool
- Toedscruel
- Rellor
- Rabsca
- Bramblin
- Brambleghast
### Animation updates for the following Pokémon
- Garchomp
- Tropius
- Torpius
- Nosepass
- Probopass
- Sneasel
- Weavile
- Sneasler
- Braixen
- Delphox
- Cinderace
- Kangaskhan
- Gossifleur
- Eldegoss
- Stonjourner
- Wailmer
- Lechonk
- Oinkologne
- Dratini
- Dragonair
- Dragonite
- G. Corsola
- Cursola
- Dunsparce
- Dudunsparce (both forms)
- 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
### Model updates for the following Pokémon
- Cleffa
- Clefairy
- Clefable
- Gyarados
- Dragonite
- Eevee
@@ -313,110 +99,27 @@
- Grovyle
- Sceptile
- Honchkrow
- Gible
- Gabite
- Garchomp
- Pidgeot
- Nosepass
- Probopass
- Kangaskhan
- Cinderace
- Magnemite
- Magneton
- Magnezone
- Beldum
- Metang
- Metagross
- Hoothoot
- Noctowl
- Teddiursa
- Ursaring
- Ursaluna
- Heatmor
- Bouffalant
- Sigilyph
- Sharpedo
- Maractus
- Clodsire
- Scyther
- Scizor
- Cacturne
- Taillow
- Swellow
- Seel
- Dewgong
- Honedge
- Doublade
- Aegislash
- Drowzee
- Hypno
- Mudkip
- Marshtomp
- Swampert
- Shelmet
- Escavalier
- 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
- Added Syrupy Apples.
### Changes
- Changed pokemon caught and seen count to update based on the current pokedex being looked
- Renamed `chargeGainedPerTick` config to `secondsToChargeHealingMachine`.
- Made Blocks of Gold count as Big Nuggets when held by a Pokémon (for Fling functionality)
- Players can now eat Sweet and Tart Apples, Whipped Dreams, and the Alcremie Sweets.
- Updated Sweet and Tart Apple sprites
- Updated the following recipes: Air Balloon, Assault Vest, Binding Band, Black Belt, Blunder Policy, Choice Band, Choice Scarf, Cleanse Tag, Covert Cloak, Destiny Knot, Eject Button, Expert Belt, Focus Band, Focus Sash, Magnet, Metronome, Muscle Band, Power Anklet, Power Band, Power Belt, Power Bracer, Power Lens, Power Weight, Protective Pads, Protein, Punching Glove, Reaper Cloth, Rocky Helmet, Room Service, Sachet, Safety Goggles, Silk Scarf, Spell Tag, Utility Umbrella, Weakness Policy, Zinc, Chipped Pot, Cracked Pot, Unremarkable Cup, Loaded Dice, Charcoal Stick, Dragon Fang, Miracle Seed, Mystic Water, Never Melt Ice, Twisted Spoon, Black Glasses, Fairy Feather, Hard Stone, Silver Powder, Soft Sand
- Added alternate ingredient options to the following recipes: Cell Battery, Damp Rock, Heat Rock, Icy Rock, Smooth Rock
- Updated the following recipes: Air Balloon, Assault Vest, Binding Band, Black Belt, Blunder Policy, Choice Band, Choice Scarf, Cleanse Tag, Covert Cloak, Destiny Knot, Eject Button, Expert Belt, Focus Band, Focus Sash, Magnet, Metronome, Muscle Band, Power Anklet, Power Band, Power Belt, Power Bracer, Power Lens, Power Weight, Protective Pads, Protein, Punching Glove, Reaper Cloth, Rocky Helmet, Room Service, Sachet, Safety Goggles, Silk Scarf, Spell Tag, Utility Umbrella, Weakness Policy, Zinc
- Added alternate ingredient options to the following recipes: Cell Battery, Charcoal Stick, Dragon Fang, Miracle Seed, Mystic Water, Never Melt Ice, Twisted Spoon, Damp Rock, Heat Rock, Icy Rock, Smooth Rock
- Grouped together some recipes within the Recipe Book. Groups include: the seven basic Poké Balls, the seven basic Ancient Poké Balls, Gilded Chests, Pokedexes, and the Weather Rocks.
- 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.
- Added herbs and snowballs to the consumable in PvE and Wild battle tags
- Added our seeds tag into `#c:seeds`, which is now made use of for the Miracle Seed recipe.
- Tweaked the Natural Materials Vanilla file to fit with the changes to tags
- Substantially optimised spawning checks mainly by front-loading biome filtering.
- When using the `cobblemon` or `generation_9` capture calculators a critical capture with a single shake will always play for successful captures when you've already registered the Pokémon as caught in your Pokédex.
@@ -431,45 +134,10 @@
- Improved parity with vanilla mobs' drop behavior; loot and XP drop on death instead of after the entire death sequence finishes.
- Quirk animations no longer play in the battle GUI since they were pretty distracting.
- A number of Pokemon that float above the ground visually (Gastly, Klingklang, etc.) are no longer considered to be touching the ground
- Changed the recipes for Level, Lure, and Moon Balls to be cleaner
- Renamed `pokemonId` and `type` to `species` in relevant Advancement triggers for conformity, this is a breaking change.
- Added a separate `species` argument to the `pick_starter`, `pasture_use` and `resurrect_pokemon` Advancement triggers.
- Made `CobblemonAgingDespawner` thresholds configurable via the config file.
- Tweaked Berry flavor data to balance cooking pot mechanics
- Made Red, Yellow, Green, Blue, Pink, Black, and White Apricorn Sprout and Saccharine Sapling able to be placed into flower pots.
- Added all Potted Apricorn Sprouts, Potted Saccharine Saplings, and Potted Pep-Up Flowers to the `#minecraft:flower_pots` block tag
- Corrected the healing values of the remedies and energy root, and reintroduced the friendship lowering mechanics for them, revivl herb, and heal powder
- Updated battle language keys for side effects to use position-specific formats, improving grammatical consistency
- Saccharine Leaves are now Collectable
- Saccharine Leaves Age 1 or higher will now show Yellow particles when broken
- Destroying a Saccharine Honey Log will now drop a Saccharine Log in stead of nothing
- Reworked some compost chances
- Updated interaction interface to include 4 more option spaces
- Made lecterns that hold a Pokédex emit light.
- Updated light levels for active PC, Pasture, Healing Machine, and Data Monitor.
- Vivichokes now always drop one seed when harvested, and converting a fully grown Vivichoke to seeds via crafting results in 1 seed.
- Healing Machine recipe rebalanced.
- Reorganised the `block` texture folder to be more organised, in line with the `item` texture folder.
- Offset in EmitterShape now ignores scale to be more like Blockbench by default. You can get this behaviour back by adding `"scale": true` in the `attachment_options` property in most EmitterShapes.
- Not specifying a dex in `/pokedex printcalculations {player} {dex}` will now print the National Dex statistics instead of showing all dexes. `/pokedex printcalculations {player} all` is how to view all dex statistics in one command.
- Removed Braised Vivichoke
- 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.
- 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
### Fixes
- Fixed game crashing when removing national pokedex using datapacks
- Fixed Particles sometimes facing the wrong direction (looking at you, Swords Dance)
- Fixed PCs always opening at box 2 instead of box 1.
- Fixed not being able to do complex item requirements aside from just NBT with evolution conditions, requirements and fossil items.
@@ -496,61 +164,11 @@
- Fixed Pokémon marked as silent still playing shiny sounds and effects.
- Fixed an issue with newer versions of Fabric API where underground Pokémon were spawning in The End.
- Fixed spawning not working well when you're at high points surrounded by lower altitude spawning areas, such as flying.
- Fixed some Pokémon having erroneous tutor moves if another move included a valid tutor move as a substring.
- Fixed certain Pokémon with forms not having appropriate stock Pokédex entries.
- Fixed issue with Pokédex Scanner that caused the open/close overlay to have the wrong opacity values
- Fixed dragon's breath not being usable on the restoration tank when it should be
- Fixed Moon Stones not interacting properly with dripstone blocks.
- Fixed some effects like particles from fishing rods appearing for players in the same coordinates in another world.
- Fixed an issue with Sketch where the Pokémon using Sketch would not properly learn moves with special characters in their name (e.g. King's Shield, Baby-Doll Eyes, etc.)
- Fixed wild Pokémon sometimes spawning with incorrect friendship values
- Fixed typo while saving/loading NPCEntity causes data loss
- Fixed an issue where catching a Pokémon while it was leashed to a fence would not update the fence.
- Fixed the `dimensions` spawning condition using the incorrect ResourceLocation, causing it to not function properly.
- Fix issue where locator X axis was not aligned with blockbench.
- Fix issue where particle effects that play on frame 1 on sendout would never play (Gastly)
- The Wiglett line will now sink in water again.
- Fixed the Sprigatito line's portraits being zoomed in too far.
- Fixed Floragato's battle cry animation from breaking.
- Fixed the block texture name for the Pep-Up Flower being inaccurate
- Fixed issue where the restoration tank would not accept valid items from a hopper.
- Fixed energy root not being shearable
- Fixed energy root always spreading into more energy roots instead of by chance (same as big root)
- Fixed issue where Pokémon spawned by the "spawnallpokemon" command potentially receiving a raft.
- Fixed logspam on NeoForge when adorn is not installed
- Fixed Cobblemon crashing if it tries to load a bedrock model not meant for cobblemon (example: Qlipoth Awakening)
- Fixed Berries (and thus mulches) not being plantable on Farmers delight rich soil farmland
- Fixed wild Pokémon vanishing when third party mods try to tame them the "vanilla" way
- Fixed Pokémon not being able to path over skulk veins, pressure plates, fence gates, signs, lanterns, chains, and many other short blocks.
- Fixed some cases in which Pokémon could not path over fence posts situations.
- Fixed flyers not being able to do vertical takeoff if surrounded by blocks.
- Fixed swimming Pokémon attempting to swim up through solid blocks.
- Fixed Pokémon surface swimming diving downward a block for the duration of the swim.
- Improved flyers avoiding getting stuck on fence posts.
- Fixed air balloon battle text not correctly displaying the Pokémon or item name
- Fixed an issue where items retrieved from a Display case would disappear if a player's inventory is full
- Fixed Pokédex Scanner not respecting the "Invert Mouse" option
- Fixed recoil eyes on Shellder & Cloyster.
- Fixed Shroomish spinning at lightning speed when fainting (no longer allowed to let it rip.)
- Fixed Pokédex Scanner not respecting the "Invert Mouse" option.
- Fixed a crash due to a ConcurrentModificationException that could occur during world generation.
- Fixed Quaxly's animations showing hidden wing sets
- Fixed Moon Ball moon phase logic to actually work correctly
- Fixed `/pokedex printcalculations` to now show the correct percentage completed of the Pokedex
- 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.
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
@@ -562,17 +180,13 @@
- Renamed Cobblemon's creative tabs to start with "Cobblemon: " to distinguish Cobblemon's tabs from tabs for other mods.
- Various items now have a rarity value.
- 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.
- 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`
- Added new item class, `WearableItem`. Instances of this class should have a corresponding 3D model. These models render when the items display context is `HEAD`.
- Added new LearnsetQuery types:
- `LEGAL` for moves that are innately compatible and learnable by the Pokémon.
- `LEGACY` for moves that were once officially learnable by the Pokémon but aren't due to GameFreak's re-balancing.
- `SPECIAL` for moves that are not learnable by the Pokémon but may have appeared in a special event or distribution.
- Pokemon now have a fireImmune attribute in their behaviour that can be set to true to ignore all fire damage (lava, magma blocks, etc.)
`JSON
{
@@ -581,18 +195,7 @@
}
}
`
- The IVs class has now been extended to include Hyper Trained values.
- Added `Pokemon#hyperTrainIV()` and `IVs#setHyperTrainedIV(Stat, Int)`.
- Added `HyperTrainedIvEvent.Pre` and `HyperTrainedIvEvent.Post`.
- Added `Pokemon#validateMoveSet()` to validate an existing Pokemon's moveset, clearing illegal moves.
- Added a `hoverText` option to PartySelectCallback, to display a tooltip on hovering over a Pokémon in the selection screen.
- `PokemonEntity` instances spawned into the world now appropriately finalize the spawn for mod compatibility.
- Added PokedexManager.obtain as a replacement for .catch which is not a friendly function name in Java.
- Added `Pokemon#hyperTrainIV()` and `IVs#setHyperTrainedIV(Stat, Int)`
- `ElementalType` now implements `ShowdownIdentifiable` to ensure the communcation with showdown stays consistent (also in regards to TeraTypes)
- Pokemon no longer have a change observable
### MoLang & Datapacks
- The following usages for item predicates can now use item conditions like advancements do, you can learn about them in the [Minecraft wiki](https://minecraft.wiki/w/Advancement_definition#minecraft:filled_bucket)
- The `requiredContext` for an item interaction evolution
@@ -610,200 +213,7 @@
- Added `pokemon` as an available Molang function for the `battleActor` functions.
- Fixed `heldItem` property inside spawn files not working and causing crashes
- Fixed `spawn_bedrock_particles` MoLang causing crashes when used in a server environment
- The following move sources are now valid for the `moves` array in species data:
- `legacy:{move}`
- `special:{move}`
- The Pokédex form lang key definition now follows `cobblemon.ui.pokedex.info.form.{species}-{formname}` instead of `cobblemon.ui.pokedex.info.form.{formname}`.
- Added `play_sound_on_server` as an available Molang function for the `worldHolder` & `player` functions.
- Added `run_molang_after` as an available Molang function for the `entity` functions when schedulable.
- Added an optional parameter for `run_molang` to schedule the function.
- Added `labels` & `has_label` as available Molang functions for the `speciesFunctions`
- Added datapack-defined starter categories via `data/<namespace>/starters/*.json`, with built-in fallback and `useConfigStarters` merge option.
- The format of the `remedies.json` file has changed to allow for individual friendshipDrop amounts per remedy
- Fixed `entity.find_nearby_block` causing crashes when attempting to use a block tag
- Spawn Filters can now access `v.spawn.class` to get the identifier of an NPC class for when trying to influence NPC spawns
- Added Molang functions for Party and PC: `set_pokemon`
- Added Molang functions for Pokémon: `pokeball`, `held_item`, `remove_held_item`, `hyper_train_iv`, `validate_moveset`, `initialize_moveset`, and `add_exp`.
- Added Molang functions for Pokémon: `aspects`, `form_aspects`, `unlearn_move`, `teach_learnable_moves`, `cosmetic_item`, and `remove_cosmetic_item`.
- Added Molang functions for Pokémon: `ability`, `set_iv`, `set_ev`, `teach_move`, and `can_learn_move`.
- Added Molang function `q.delete_variable(<struct>, <variable_name>)` to delete a value from a variable structure in MoLang data.
- Added Molang function `q.delete_variables(<struct>)` to delete all values from a variable structure in MoLang data.
- Adds Flows for `STARTER_CHOSEN`, `SHOULDER_MOUNTED`, `EV_GAINED`, `POKEMON_RELEASED`, `POKEMON_NICKNAMED`, `HELD_ITEM`, and `TRADE_COMPLETED` events
- Adds Flows for `POKEMON_HEALED`, `POKEMON_SCANNED`, `BERRY_HARVEST`, `LOOT_DROPPED`, `POKEMON_SEEN`, `COLLECT_EGG`, `HATCH_EGG`, and `EXPERIENCE_GAINED`.
- Adds Flows for `POKEMON_CATCH_RATE`, `BAIT_SET`, `BAIT_SET_PRE`, `BAIT_CONSUMED`, `POKEROD_CAST_PRE`, `POKEROD_CAST_POST`, `POKEROD_REEL`, and `BOBBER_SPAWN_POKEMON_PRE`.
- Adds Flows for `POKEMON_ASPECTS_CHANGED`, `FRIENDSHIP_UPDATED`, `CHANGE_PC_BOX_WALLPAPER_EVENT_PRE`, `CHANGE_PC_BOX_WALLPAPER_EVENT_POST`, and `FULLNESS_UPDATED`.
- MoLang triggered battles may now set the battle format, whether to clone the player's party, set level, or heal prior.
- Added Molang function for Player: `inventory`
- Adds Flows for `STARTER_CHOSEN`, `EV_GAINED`, `POKEMON_RELEASED`, `POKEMON_NICKNAMED`, `HELD_ITEM`, and `TRADE_COMPLETED` events
- Adds Pokemon functions for `pokeball`, `held_item`, `remove_held_item`, `add_aspects`, and `remove_aspects`
- Added `pokemon.hyper_train_iv` as an available Molang function.
- Added `prepare_{effect}` and `damage_{effect}` action effect hooks in battles for more battle particle effects.
- Added `q.has_argument(<argument_name>, [argument_value])` MoLang function to several battle-related action effect contexts.
- 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
### 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)
@@ -816,7 +226,7 @@ Added new/updated particles for the following moves:
- Added optional box argument to the /pc command.
- Pokédex and Dialogue screens now close when the inventory keybind is pressed.
- Added config setting `maxPokedexScanningDetectionRange` to control from what distance the player can scan Pokémon using the Pokédex.
- Added config setting `hideUnimplementedPokemonInThePokedex` which hides unimplemented Pokémon from the Pokédex when set to true.
- Added config setting `hideUnimplementedPokemonInThePokedex` which hides unimplemented Pokémon from the Pokédex when set to true.
- Added debug renderer for posable entity locators.
- Added crossover paintings from Close Combat, Premonition, Altar, Slumber, and Nomad.
- Added optional box argument to the /pc command
@@ -970,7 +380,7 @@ Added new/updated particles for the following moves:
- Fixed Vivichoke Dip and Leek & Potato Stew not returning a bowl upon consumption.
- Fixed Fossil Restoration Tank not accepting Hay Bales as organic material.
- Fixed Potion items applying double their intended healing value.
- Fixed Fast Ball capture bonuses applying to all Pokémon, making it the Best Ball instead of the Mediocre Edge-Case Ball.
- Fixed Fast Ball capture bonuses applying to all Pokémon, making it the Best Ball instead of the Mediocre Edge-Case Ball.
- Fixed "learned new move" messages appearing for already-known moves on Pokémon evolutions.
- Fixed Pokémon Item Models breaking shadows nearby when being placed in Display Cases or Item Frames.
- Fixed berries not giving bonus yields when planted in their preferred biomes. I'm sure we've fixed that 5 times now.
@@ -979,7 +389,7 @@ Added new/updated particles for the following moves:
- Fixed players with shouldered Pokémon not being able to rejoin their 1.5.2 worlds using 1.6.
- Fixed `PokemonProperties` utilizing `ability=<some ability>` being treated as a forced ability even when it is a legal ability for the Pokémon.
- Fixed type formatting in Pokédex scanner mode when dual types require two lines.
- Fixed trading sometimes crashing the game or server.
- Fixed trading sometimes crashing the game or server.
- Fixed Wild shiny sounds not respecting the `shinyNoticeParticlesDistance` config setting.
- Fixed Pokémon being able to evolve mid-battle.
- Fixed NPC held items being able to be stolen by players. Don't be a thief!
@@ -1073,7 +483,7 @@ Added new/updated particles for the following moves:
- Stonjourner Henge Ruins, Luna Henge Ruins, and Sol Henge Ruins.
- Submerged Shipwreck Cove and Lush Shipwreck Cove.
- Pokémon Centers to all 5 village types.
### Pokémon Added
#### Gen 1
- Alola Bias Cubone (built-in resource pack)
@@ -1396,7 +806,7 @@ Added new/updated particles for the following moves:
- Big Roots and Energy Roots now share the same sounds as intended.
- Fixed sounds made by blocks playing at a lower pitch than intended.
- Fixed Chimchar and Monferno comically T posing whenever they sleep.
- Fixed Cetitan's cry breaking its walk and sleep animations.
- Fixed Cetitan's cry breaking its walk and sleep animations.
- Fixed the Magby line not having any placeholder walk animations.
- Fixed Duskull and Dusclops using skylight levels for their nether spawn data. There is no sun in the Nether!
- Fixed Hisuian Zoroark using base Zoroark stats.
@@ -1412,7 +822,7 @@ Added new/updated particles for the following moves:
- Fixed Trevenant T-posing after 8 seconds of sleeping. I also do that.
- Fixed Bewear T-posing for a bit if a wild one faints where you can see.
- Fixed Noctowl's placeholder fly animation.
- Corrected Lotad blinking animation.
- Corrected Lotad blinking animation.
- Fixed reviving items causing errors when used in battle.
- Fixed messages for Focus Sash, Confusion, Mummy, Ice Face, Own Tempo, and Revive.
- Improve error handling when loading spawn-sets to ensure invalid configurations don't crash the server.
@@ -1502,7 +912,7 @@ Added new/updated particles for the following moves:
- Added a new universal locator called "top".
- Added `eggs_collected` and `eggs_hatched` Advancement triggers.
- Added missing `minYaw` configuration to the `q.look()` function for JSON posers.
- Some pose condition names have been changed:
- Some pose condition names have been changed:
- `isTouchingWaterOrRain` has been changed to `isInWaterOrRain`
- `isSubermegedInWater` has been changed to `isUnderWater`
@@ -1618,7 +1028,6 @@ Added new/updated particles for the following moves:
}
`
- Added support for custom stashes, similar to Gimmighoul's coin and scrap stashes.
- Added the ability to create custom brewing stand recipes.
### Localization
- Updated translations for:
@@ -2926,4 +2335,4 @@ Thank you so much to all of our community translators that bring the mod to the
- Added species, ability, and move translations for `ko_ko`, `jp_jp`, `fr_fr`, `es_es`, `it_it`, and `zh_cn`.
## [1.0.0 (November 12th, 2022)](#1-0-0)
- Initial release.
- Initial release.
+1
View File
@@ -1,5 +1,6 @@
plugins {
id("cobblemon.root-conventions")
id ("net.nemerosa.versioning") version "3.1.0"
}
version = "${project.property("mod_version")}+${project.property("mc_version")}"
+2 -8
View File
@@ -21,6 +21,7 @@ plugins {
id("net.kyori.blossom")
id("org.jetbrains.gradle.plugin.idea-ext")
id("net.nemerosa.versioning") version "3.1.0"
}
architectury {
@@ -31,7 +32,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 +45,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 +95,4 @@ sourceSets {
}
}
}
}
}
@@ -1,13 +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;
public interface DoubleJump {
boolean isDoubleJumping();
}
@@ -9,6 +9,15 @@
package com.cobblemon.mod.common;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import net.minecraft.util.Mth;
import net.minecraft.util.SmoothDouble;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.joml.Vector3f;
import java.util.function.Function;
public interface OrientationControllable {
OrientationController getOrientationController();
@@ -0,0 +1,96 @@
/*
* 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.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::resolvedShoulderLightLevel,
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 resolvedShoulderLightLevel(Player player) {
var underwater = !player.level().getFluidState(BlockPos.containing(player.getX(), player.getEyeY(), player.getZ())).isEmpty();
final int leftLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityLeft()), underwater);
final int rightLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityRight()), underwater);
return Math.max(leftLightLevel, rightLightLevel);
}
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 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,17 +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.duck;
public interface ChannelDuck {
void cobblemon$applyLowPassFilter(float gain, float hfGain);
void cobblemon$inverseAttenuation(float rolloffFactor);
void cobblemon$clearFilters();
// void cobblemon$exponentialAttenuation(float rolloff, float refDistance, float maxDistance);
}
@@ -1,18 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.duck;
import org.joml.Vector3f;
public interface PlayerDuck {
void setDriverInput(Vector3f driverInput);
Vector3f getDriverInput();
void setLastSentDriverInput(Vector3f driverInput);
Vector3f getLastSentDriverInput();
}
@@ -1,15 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.duck;
import net.minecraft.world.item.crafting.RecipeManager;
public interface RecipeAwareSlot {
void setRecipeManager(RecipeManager recipeManager);
}
@@ -12,12 +12,14 @@ import com.cobblemon.mod.common.block.SaccharineLeafBlock;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import org.objectweb.asm.Opcodes;
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.Desc;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.Slice;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@@ -40,9 +42,8 @@ public abstract class BeeEntityMixin {
if (block instanceof SaccharineLeafBlock) {
int age = blockState.getValue(SaccharineLeafBlock.Companion.getAGE());
boolean waterlogged = blockState.getValue(BlockStateProperties.WATERLOGGED);
if (age < 2 && !waterlogged) {
if (age < 2) {
this.cobblemon$result = blockState.setValue(SaccharineLeafBlock.Companion.getAGE(), age + 1);
}
}
@@ -1,69 +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.duck.ChannelDuck;
import com.mojang.blaze3d.audio.Channel;
import org.lwjgl.openal.AL10;
import org.lwjgl.openal.EXTEfx;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(Channel.class)
public abstract class ChannelMixin implements ChannelDuck {
@Shadow
private int source;
// Id of filter applied when sound is obstructed
private int cobblemon$lowPassFilterId = 0;
@Override
public void cobblemon$applyLowPassFilter(float gain, float hfGain) {
// Delete old filter if it exists
if (cobblemon$lowPassFilterId != 0) {
AL10.alSourcei(source, EXTEfx.AL_DIRECT_FILTER, 0); // Detach filter from source
EXTEfx.alDeleteFilters(cobblemon$lowPassFilterId); // Delete old filter
cobblemon$lowPassFilterId = 0;
}
// Generate a fresh filter
int filterId = EXTEfx.alGenFilters();
if (!EXTEfx.alIsFilter(filterId)) {
System.err.println("Failed to create OpenAL low-pass filter");
return;
}
EXTEfx.alFilteri(filterId, EXTEfx.AL_FILTER_TYPE, EXTEfx.AL_FILTER_LOWPASS);
EXTEfx.alFilterf(filterId, EXTEfx.AL_LOWPASS_GAIN, gain);
EXTEfx.alFilterf(filterId, EXTEfx.AL_LOWPASS_GAINHF, hfGain);
AL10.alSourcei(source, EXTEfx.AL_DIRECT_FILTER, filterId);
// Save the new filter ID
cobblemon$lowPassFilterId = filterId;
}
@Override
public void cobblemon$inverseAttenuation(float rolloffFactor) {
AL10.alSourcei(source, AL10.AL_DISTANCE_MODEL, AL10.AL_INVERSE_DISTANCE);
AL10.alSourcef(source, AL10.AL_ROLLOFF_FACTOR, rolloffFactor);
AL10.alSourcef(source, AL10.AL_REFERENCE_DISTANCE, 1.0f);
AL10.alSourcef(source, AL10.AL_MAX_DISTANCE, 0.0f);
}
@Override
public void cobblemon$clearFilters() {
AL10.alSourcei(source, EXTEfx.AL_DIRECT_FILTER, 0);
if (EXTEfx.alIsFilter(cobblemon$lowPassFilterId)) {
EXTEfx.alDeleteFilters(cobblemon$lowPassFilterId);
cobblemon$lowPassFilterId = 0;
}
}
}
@@ -1,54 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.network.protocol.game.ClientboundSetPassengersPacket;
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
@Mixin(ClientPacketListener.class)
public class ClientPacketListenerMixin {
@Shadow private ClientLevel level;
@Inject(method = "handleSetEntityPassengersPacket", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/world/entity/Entity;hasIndirectPassenger(Lnet/minecraft/world/entity/Entity;)Z", shift = At.Shift.BEFORE), cancellable = true)
private void cobblemon$handlePokemonPassengers(ClientboundSetPassengersPacket packet, CallbackInfo ci) {
Entity entity = this.level.getEntity(packet.getVehicle());
if (entity instanceof PokemonEntity pokemon) {
var passengers = new ArrayList<Entity>();
for (int passenger : packet.getPassengers()) {
passengers.add(this.level.getEntity(passenger));
}
Arrays
.stream(pokemon.getOccupiedSeats())
.filter(x -> !passengers.contains(x))
.filter(Objects::nonNull)
.forEach(Entity::stopRiding);
var occupiedSeats = Arrays.asList(pokemon.getOccupiedSeats());
passengers.stream()
.filter(x -> !occupiedSeats.contains(x))
.filter(Objects::nonNull)
.forEach(x -> x.startRiding(pokemon, true));
ci.cancel();
}
}
}
@@ -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)
)
);
}
}
@@ -0,0 +1,39 @@
/*
* 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 net.minecraft.world.entity.monster.Phantom;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(targets = "net/minecraft/world/entity/monster/Phantom$PhantomSweepAttackGoal")
public class EntityPhantomMixin {
// TODO: Figure out how to access the outer class that wraps SwoopMovementGoal
// Need to find the cat mons that are near the phantom
// @Inject(method = "shouldContinue", at = @At(value = "RETURN"), cancellable = true)
// public void cobblemon$shouldContinue(CallbackInfoReturnable<Boolean> ci) {
//// var goalSelectorField = PhantomEntity.class.getClasses();// PhantomEntity.class.getFields();// .class.getDeclaredField("goalSelector");
//
//// goalSelectorField.setAccessible(true);
//// GoalSelector goalSelector = (GoalSelector) goalSelectorField.get(mobEntity);
//// final SwoopMovementGoal phantom = (SwoopMovementGoal) (Object) this;
//// if(ci.getReturnValue() == false) {
//// var result = this$0.getWorld().getEntitiesByClass(
//// PokemonEntity.class,
//// this$0.getBoundingBox().expand(16.0),
//// entity -> {
//// return ((PokemonEntity) entity).getBehaviour().getEntityInteract().getAvoidedByPhantom();
//// }
//// ).isEmpty();
////
//// ci.setReturnValue(result);
//// }
// }
}
@@ -9,19 +9,20 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.CobblemonClient;
import com.cobblemon.mod.common.client.render.MatrixWrapper;
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel;
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState;
import com.cobblemon.mod.common.entity.PosableEntity;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.entity.EntityRenderDispatcher;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.resources.PlayerSkin;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Debug;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@@ -29,6 +30,11 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map;
import net.minecraft.client.renderer.entity.EntityRenderDispatcher;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.resources.PlayerSkin;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.world.entity.player.Player;
@Mixin(EntityRenderDispatcher.class)
public class EntityRenderDispatcherMixin {
@@ -71,23 +77,6 @@ public class EntityRenderDispatcherMixin {
0F,
1F
);
/*if (locator.contains("middle")){
PoseStack.Pose pose = poseStack.last();
Vector3f vec = matrix.getMatrix().getRotation(new AxisAngle4f()).transform(new Vector3f(0, 0, -1));
buffer.addVertex(pose, pos.toVector3f()).setColor(0xffff00ff).setNormal(pose, vec.x, vec.y, vec.z); //Forward, pink
buffer.addVertex(pose, (float)(pos.x() + vec.x), (float)(pos.y() + vec.y), (float)(pos.z() + vec.z)).setColor(0xffff00ff).setNormal(pose, vec.x, vec.y, vec.z);
vec = matrix.getMatrix().getRotation(new AxisAngle4f()).transform(new Vector3f(0, 1, 0));
buffer.addVertex(pose, pos.toVector3f()).setColor(0xffffff00).setNormal(pose, vec.x, vec.y, vec.z); //Up, yellow
buffer.addVertex(pose, (float)(pos.x() + vec.x), (float)(pos.y() + vec.y), (float)(pos.z() + vec.z)).setColor(0xffffff00).setNormal(pose, vec.x, vec.y, vec.z);
vec = matrix.getMatrix().getRotation(new AxisAngle4f()).transform(new Vector3f(-1, 0, 0));
buffer.addVertex(pose, pos.toVector3f()).setColor(0xff00ffff).setNormal(pose, vec.x, vec.y, vec.z); //Left, cyan
buffer.addVertex(pose, (float)(pos.x() + vec.x), (float)(pos.y() + vec.y), (float)(pos.z() + vec.z)).setColor(0xff00ffff).setNormal(pose, vec.x, vec.y, vec.z);
}*/
poseStack.popPose();
});
@@ -1,44 +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.entity.Entity;
import net.minecraft.world.entity.LightningBolt;
import net.minecraft.world.phys.AABB;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
@Mixin(LightningBolt.class)
public abstract class LightningBoltMixin {
@Inject(method = "spawnFire", at = @At(value = "Head"), cancellable = true)
private void cobblemon$spawnFire(int extraIgnitions, CallbackInfo ci) {
// This prevents fire from spawning around Pokemon who otherwise should be immune to lightning and effectively absorb the hit
final LightningBolt lightningBolt = (LightningBolt) (Object) this;
List<Entity> list = lightningBolt.level().getEntities(
lightningBolt,
new AABB(lightningBolt.getX() - 2.0, lightningBolt.getY() - 2.0, lightningBolt.getZ() - 2.0, lightningBolt.getX() + 2.0, lightningBolt.getY() + 2.0, lightningBolt.getZ() + 2.0),
it -> {
if (!it.isAlive() || !(it instanceof PokemonEntity)) {
return false;
}
String abilityName = ((PokemonEntity) it).getPokemon().getAbility().getName();
return abilityName.equals("lightningrod") || abilityName.equals("voltabsorb") || abilityName.equals("motordrive");
});
if (!list.isEmpty()) {
ci.cancel();
}
}
}
@@ -8,44 +8,17 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry;
import com.cobblemon.mod.common.api.riding.Seat;
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate;
import com.cobblemon.mod.common.client.render.MatrixWrapper;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LivingEntity.class)
public abstract class LivingEntityMixin extends Entity implements OrientationControllable {
public LivingEntityMixin(EntityType<?> entityType, Level level) {
super(entityType, level);
}
@Unique
private final OrientationController cobblemon$orientationController = new OrientationController((LivingEntity) (Object) this);
public OrientationController getOrientationController() {
return cobblemon$orientationController;
}
public abstract class LivingEntityMixin {
@Inject(method = "onEffectRemoved", at = @At(value = "TAIL"))
private void cobblemon$onEffectRemoved(MobEffectInstance effect, CallbackInfo ci) {
@@ -55,65 +28,4 @@ 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 && pokemonEntity.getDelegate() instanceof PokemonClientDelegate delegate) {
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
Seat seat = pokemonEntity.getSeats().get(seatIndex);
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();
}
@Override
public void absMoveTo(double x, double y, double z, float yaw, float pitch) {
if (cobblemon$orientationController.getActive()) {
this.absMoveTo(x, y, z);
this.setYRot(yaw % 360.0f);
this.setXRot(pitch % 360.0f);
this.yRotO = this.getYRot();
this.xRotO = this.getXRot();
} else {
this.absMoveTo(x, y, z);
this.absRotateTo(yaw, pitch);
}
}
}
@@ -0,0 +1,119 @@
/*
* 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.OrientationControllable;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.riding.Seat;
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate;
import com.cobblemon.mod.common.client.render.MatrixWrapper;
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState;
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel;
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.mojang.authlib.GameProfile;
import net.minecraft.client.CameraType;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.joml.Matrix3f;
import org.joml.Vector3f;
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.CallbackInfoReturnable;
import java.util.Map;
@Mixin(LocalPlayer.class)
public abstract class LocalPlayerMixin extends AbstractClientPlayer implements OrientationControllable {
@Shadow
private float jumpRidingScale;
public LocalPlayerMixin(ClientLevel clientLevel, GameProfile gameProfile) {
super(clientLevel, gameProfile);
}
@Inject(method = "getJumpRidingScale", at = @At("HEAD"), cancellable = true)
public void modifyJumpRidingScale(CallbackInfoReturnable<Float> cir) {
LocalPlayer player = (LocalPlayer) (Object) this;
if (player.isPassenger() && player.getVehicle() instanceof PokemonEntity pokemonEntity) {
var rideValue = pokemonEntity.<Float>ifRidingAvailableSupply(null, (behaviour, settings, state) -> {
if (behaviour.canJump(settings, state, pokemonEntity, player)) return null;
return behaviour.setRideBar(settings, state, pokemonEntity, player);
});
if (rideValue != null) {
cir.setReturnValue(rideValue);
}
}
}
@Override
public 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());
if (locator == null) {
super.pick(hitDistance, partialTicks, hitFluids);
}
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
OrientationController controller = this.getOrientationController();
float currEyeHeight = this.getEyeHeight();
Vector3f offset = new Vector3f(new Vector3f(0f, currEyeHeight - (this.getBbHeight() / 2), 0f));
if (Minecraft.getInstance().options.getCameraType() == CameraType.FIRST_PERSON) {
PosableModel model = VaryingModelRepository.INSTANCE.getPoser(pokemonEntity.getPokemon().getSpecies().getResourceIdentifier(), new FloatingState());
offset.add(cobblemon$getFirstPersonOffset(model, seat));
}
Matrix3f orientation = controller.isActive() && controller.getOrientation() != null ? controller.getOrientation() : new Matrix3f();
Vec3 rotatedEyeHeight = new Vec3(orientation.transform(offset));
Vec3 eyePosition = locatorOffset.add(pokemonEntity.position()).add(rotatedEyeHeight);
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 static @NotNull Vector3f cobblemon$getFirstPersonOffset(PosableModel model, Seat seat) {
Map<String, Vec3> cameraOffsets = model.getFirstPersonCameraOffset();
if (cameraOffsets.containsKey(seat.getLocator())) {
return cameraOffsets.get(seat.getLocator()).toVector3f();
} else {
return new Vector3f(0f, 0f, 0f);
}
}
}
@@ -1,57 +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.ai.goal.Goal;
import net.minecraft.world.entity.monster.Phantom;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(targets = "net.minecraft.world.entity.monster.Phantom$PhantomSweepAttackGoal")
public abstract class PhantomSweepAttackGoalMixin extends Goal {
@Unique
private Phantom phantomSelf; // Allows access to the outer class
@Inject(method = "<init>", at = @At("TAIL"))
private void onInit(Phantom phantom, CallbackInfo ci) {
this.phantomSelf = phantom;
}
@Inject(method = "canContinueToUse", at = @At("RETURN"), cancellable = true)
private void modifyCanContinueToUse(CallbackInfoReturnable<Boolean> cir) {
Phantom phantom = this.phantomSelf; // Outer class instance
if(phantom == null) return;
if (cir.getReturnValue()) {
var nearbyFearedEntities = phantom.level().getEntities(phantom, phantom.getBoundingBox().inflate(16.0),
(entity -> ((entity instanceof PokemonEntity && ((PokemonEntity) entity).getBeamMode() == 0 && ((PokemonEntity) entity).getBehaviour().getEntityInteract().getAvoidedByPhantom())
|| (entity instanceof ServerPlayer && EntityBehaviour.Companion.hasPhantomFearedShoulderMount((ServerPlayer)entity)))));
if (!nearbyFearedEntities.isEmpty()) {
nearbyFearedEntities.forEach(entity -> {
if (entity instanceof PokemonEntity) {
((PokemonEntity) entity).cry();
}
//TODO: Find a way to make shoulder mounted Pokemon cry
});
cir.setReturnValue(false);
}
}
}
}
@@ -18,7 +18,6 @@ import com.cobblemon.mod.common.api.pokemon.PokemonSpecies;
import com.cobblemon.mod.common.api.storage.party.PartyStore;
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore;
import com.cobblemon.mod.common.api.tags.CobblemonItemTags;
import com.cobblemon.mod.common.duck.PlayerDuck;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData;
import com.cobblemon.mod.common.pokedex.scanner.ScannableEntity;
@@ -48,7 +47,6 @@ import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
@@ -63,12 +61,7 @@ import java.util.*;
import java.util.stream.Collectors;
@Mixin(Player.class)
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable, PlayerDuck {
@Unique
private Vector3f cobblemon$driverInput;
@Unique
private Vector3f cobblemon$lastSentDriverInput;
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable {
@Shadow public abstract CompoundTag getShoulderEntityLeft();
@@ -88,8 +81,8 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
@Shadow @Final private static Map<Pose, EntityDimensions> POSES;
@Shadow @Final public static EntityDimensions STANDING_DIMENSIONS;
@Shadow protected int jumpTriggerTime;
@Unique private final OrientationController cobblemon$orientationController = new OrientationController(this);
@Unique
private final OrientationController cobblemon$orientationController = new OrientationController(this);
protected PlayerMixin(EntityType<? extends LivingEntity> p_20966_, Level p_20967_) {
super(p_20966_, p_20967_);
@@ -286,25 +279,4 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
public OrientationController getOrientationController() {
return cobblemon$orientationController;
}
@Override
public void setDriverInput(Vector3f driverInput) {
cobblemon$driverInput = driverInput;
}
@Override
public Vector3f getDriverInput() {
return cobblemon$driverInput;
}
@Override
public void setLastSentDriverInput(Vector3f lastSentDriverInput) {
cobblemon$lastSentDriverInput = lastSentDriverInput;
}
@Override
public Vector3f getLastSentDriverInput() {
return cobblemon$lastSentDriverInput;
}
}
@@ -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);
}
}
}
@@ -12,11 +12,9 @@ import com.cobblemon.mod.common.CobblemonNetwork;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.net.NetworkPacket;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.duck.PlayerDuck;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.mixin.accessor.ChunkMapAccessor;
import com.cobblemon.mod.common.mixin.accessor.TrackedEntityAccessor;
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket;
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket;
import com.cobblemon.mod.common.net.messages.client.pokemon.update.ClientboundUpdateRidingStatePacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@@ -28,7 +26,6 @@ import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerPlayerConnection;
import net.minecraft.world.entity.Entity;
import org.joml.Matrix3f;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -58,20 +55,17 @@ public abstract class ServerEntityMixin {
private void cobblemon$sendChanges(CallbackInfo ci) {
cobblemon$sendOrientationChanges();
cobblemon$sendRidingStateChanges();
cobblemon$sendDriverInput();
}
private void cobblemon$sendRidingStateChanges() {
if (!(this.entity instanceof PokemonEntity pokemonEntity)) return;
if (pokemonEntity.getRidingController() == null) return;
var ridingController = pokemonEntity.getRidingController();
if (ridingController.getContext() == null) return;
var context = ridingController.getContext();
var ridingBehaviour = context.getBehaviour();
var ridingState = context.getState();
if (pokemonEntity.getRidingState() == null) return;
if (pokemonEntity.getRiding() == null) return;
var ridingBehaviour = pokemonEntity.getRiding();
var ridingState = pokemonEntity.getRidingState();
var previousRidingState = pokemonEntity.getPreviousRidingState();
if (previousRidingState != null && !ridingState.shouldSync(previousRidingState)) return;
cobblemon$broadcast(new ClientboundUpdateRidingStatePacket(pokemonEntity.getId(), ridingBehaviour, ridingState, null));
cobblemon$broadcast(new ClientboundUpdateRidingStatePacket(pokemonEntity.getId(), ridingBehaviour.getKey(), pokemonEntity.getRidingState(), null));
}
private void cobblemon$sendOrientationChanges() {
@@ -88,19 +82,6 @@ public abstract class ServerEntityMixin {
cobblemon$broadcast(new ClientboundUpdateOrientationPacket(currOrientation, currActive, entity.getId()));
}
private void cobblemon$sendDriverInput() {
if (!(this.entity instanceof ServerPlayer serverPlayer)) return;
if(!(serverPlayer.getVehicle() instanceof PokemonEntity)) return;
Vector3f driverInput = ((PlayerDuck)serverPlayer).getDriverInput();
if(driverInput == null) return;
// If no change in input is detected then don't send a new packet
Vector3f lastSentDriverInput = ((PlayerDuck)serverPlayer).getLastSentDriverInput();
if (driverInput.equals(lastSentDriverInput)) return;
((PlayerDuck)serverPlayer).setLastSentDriverInput(driverInput);
cobblemon$broadcast(new ClientboundUpdateDriverInputPacket(driverInput, entity.getId()));
}
private void cobblemon$broadcast(NetworkPacket<?> packet) {
if (!(entity.level() instanceof ServerLevel level)) return;
ChunkMap chunkMap = level.getChunkSource().chunkMap;
@@ -116,4 +97,5 @@ public abstract class ServerEntityMixin {
}
}
}
@@ -1,44 +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.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.AABB;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.List;
import java.util.Optional;
@Mixin(ServerLevel.class)
public abstract class ServerLevelMixin {
@Inject(method = "findLightningRod", at = @At(value = "RETURN"), cancellable = true)
private void cobblemon$findLightningRod(BlockPos blockPos, CallbackInfoReturnable<Optional<BlockPos>> ci) {
final ServerLevel serverLevel = (ServerLevel) (Object) this;
if (ci.getReturnValue().isEmpty()) {
// Search for Pokemon with the ability Lightning Rod and redirect to them
AABB aABB = AABB.encapsulatingFullBlocks(blockPos, new BlockPos(blockPos.atY(serverLevel.getMaxBuildHeight()))).inflate(64.0);
List<LivingEntity> list = serverLevel.getEntitiesOfClass(LivingEntity.class, aABB, (livingEntity) -> livingEntity != null
&& livingEntity.isAlive()
&& livingEntity instanceof PokemonEntity
&& ((PokemonEntity) livingEntity).getBeamMode() == 0
&& ((PokemonEntity) livingEntity).getPokemon().getAbility().getName().equals("lightningrod"));
if (!list.isEmpty()) {
ci.setReturnValue(Optional.of((list.get(serverLevel.random.nextInt(list.size()))).blockPosition()));
}
}
}
}
@@ -10,10 +10,7 @@ package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.server.level.ServerPlayer;
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;
@@ -46,10 +43,4 @@ public abstract class ServerPlayerMixin {
controllable.getOrientationController().setActive(false);
}
//TODO: Switch to sending out on load instead of preventing saving
@ModifyExpressionValue(method = "addAdditionalSaveData", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hasExactlyOnePlayerPassenger()Z"))
private boolean cobblemon$cancelSavingPokemonMounts(boolean original, @Local(ordinal=0) Entity entity) {
return original && !(entity instanceof PokemonEntity);
}
}
@@ -1,50 +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.block.HeartyGrainsBlock;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.DoublePlantBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.SimpleBlockFeature;
import net.minecraft.world.level.levelgen.feature.configurations.SimpleBlockConfiguration;
import org.spongepowered.asm.mixin.Debug;
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(SimpleBlockFeature.class)
@Debug(export = true, print = true)
public class SimpleBlockFeatureMixin {
//this mixin inserts the logic needed to ensure that when hearty grains are of a certain age, they place with the upper block too
//similarly to what the doubleblock logic is here
@Inject(method = "place", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;canSurvive(Lnet/minecraft/world/level/LevelReader;Lnet/minecraft/core/BlockPos;)Z", shift = At.Shift.AFTER), cancellable = true)
public void place(FeaturePlaceContext<SimpleBlockConfiguration> context, CallbackInfoReturnable<Boolean> cir,
@Local BlockState blockState, @Local BlockPos blockPos, @Local WorldGenLevel worldGenLevel) {
if (!(blockState.getBlock() instanceof HeartyGrainsBlock)) {
return;
}
if (blockState.getValue(HeartyGrainsBlock.Companion.getAGE()) <= HeartyGrainsBlock.AGE_AFTER_HARVEST) {
return; //block is too young to be double
}
if (!worldGenLevel.isEmptyBlock(blockPos.above())) {
cir.setReturnValue(false); //no space for the 2nd block above
return;
}
DoublePlantBlock.placeAt(worldGenLevel, blockState, blockPos, Block.UPDATE_CLIENTS);
cir.setReturnValue(true);
}
}
@@ -8,33 +8,23 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.api.riding.sound.RideAttenuationModel;
import com.cobblemon.mod.common.api.riding.sound.RideLoopSound;
import com.cobblemon.mod.common.client.sound.BattleMusicController;
import com.cobblemon.mod.common.duck.ChannelDuck;
import com.cobblemon.mod.common.duck.SoundEngineDuck;
import com.google.common.collect.Multimap;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.audio.Channel;
import net.minecraft.client.resources.sounds.Sound;
import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.client.resources.sounds.TickableSoundInstance;
import net.minecraft.client.sounds.ChannelAccess;
import net.minecraft.client.sounds.SoundEngine;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import java.util.Iterator;
import java.util.Map;
@Mixin(SoundEngine.class)
@@ -55,86 +45,6 @@ public abstract class SoundEngineMixin implements SoundEngineDuck {
@Shadow
public abstract boolean isActive(SoundInstance sound);
@Inject(method = "play",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/sounds/ChannelAccess$ChannelHandle;execute(Ljava/util/function/Consumer;)V"
),
cancellable = true
)
private void cobblemon$overrideChannelExecute(
SoundInstance sound,
CallbackInfo ci,
@Local(name = "h") float h,
@Local(name = "i") float i,
@Local(name = "g") float g,
@Local(name = "bl2") boolean bl2,
@Local(name = "bl3") boolean bl3,
@Local(name = "bl") boolean bl,
@Local(name = "vec3") Vec3 vec3,
@Local(name = "channelHandle") ChannelAccess.ChannelHandle handle
) {
if (!(sound instanceof RideLoopSound rideSound)) return;
if (handle == null) return;
handle.execute((channel) -> {
channel.setPitch(i);
channel.setVolume(h);
if (rideSound.getRideAttenuation() == RideAttenuationModel.LINEAR) {
channel.linearAttenuation(g);
} else if (rideSound.getRideAttenuation() == RideAttenuationModel.EXPONENTIAL){
((ChannelDuck) channel).cobblemon$inverseAttenuation(g);
} else {
channel.disableAttenuation();
}
// Apply low pass filter if the rideSound dictate that it should be muffled
if (rideSound.getShouldMuffle()) {
((ChannelDuck) channel).cobblemon$applyLowPassFilter(1.0f, rideSound.getMuffleAmount());
} else {
((ChannelDuck) channel).cobblemon$clearFilters();
}
channel.setLooping(bl2 && !bl3);
channel.setSelfPosition(vec3);
channel.setRelative(bl);
});
}
@Inject(
method = "tickNonPaused",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/sounds/ChannelAccess$ChannelHandle;execute(Ljava/util/function/Consumer;)V",
ordinal = 0
)
)
private void cobblemon$tickableSoundTick(
CallbackInfo ci,
@Local(name = "tickableSoundInstance") TickableSoundInstance instance,
@Local(name = "channelHandle", ordinal = 0) ChannelAccess.ChannelHandle handle,
@Local(name = "f", ordinal = 0) float volume,
@Local(name = "g", ordinal = 0) float pitch,
@Local(name = "vec3", ordinal = 0) Vec3 vec3
) {
if (!(instance instanceof RideLoopSound rideSound)) return;
handle.execute(channel -> {
channel.setVolume(volume);
channel.setPitch(pitch);
channel.setSelfPosition(vec3);
// Apply low pass filter if the rideSound dictate that it should be muffled
if (rideSound.getShouldMuffle()) {
((ChannelDuck) channel).cobblemon$applyLowPassFilter(1.0f, rideSound.getMuffleAmount());
} else {
((ChannelDuck) channel).cobblemon$clearFilters();
}
});
}
/** Resumes the Source belonging to the queried SoundInstance. */
private void resume(SoundInstance sound) {
ChannelAccess.ChannelHandle sourceManager = this.instanceToChannel.get(sound);
@@ -1,67 +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.world.CobblemonStructureIDs;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.RandomState;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructurePiece;
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
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.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
@Mixin(Structure.class)
public abstract class StructureMixin {
// Define a set of structures that should not spawn below Sea Level
@Unique
private static final Set<ResourceLocation> RESTRICTED_STRUCTURES = new HashSet<>(Arrays.asList(
CobblemonStructureIDs.STONJOURNER_HENGE,
CobblemonStructureIDs.LUNA_HENGE,
CobblemonStructureIDs.SOL_HENGE
));
@Inject(method = "generate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/structure/StructureStart;isValid()Z"),
cancellable = true)
public void cobblemon$isValid(RegistryAccess registryAccess, ChunkGenerator chunkGenerator, BiomeSource biomeSource,
RandomState randomState, StructureTemplateManager structureTemplateManager, long seed,
ChunkPos chunkPos, int references, LevelHeightAccessor heightAccessor,
Predicate<Holder<Biome>> validBiome,
CallbackInfoReturnable<StructureStart> cir,
@Local StructureStart structureStart) {
ResourceLocation structureKey = registryAccess.registryOrThrow(Registries.STRUCTURE).getKey((Structure) (Object) this);
if(!RESTRICTED_STRUCTURES.contains(structureKey)) {
return;
}
for (StructurePiece piece : structureStart.getPieces()) {
if (piece.getBoundingBox().minY() < chunkGenerator.getSeaLevel()) {
cir.setReturnValue(StructureStart.INVALID_START);
return;
}
}
}
}
@@ -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);
}
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.mixin.accessor;
import net.minecraft.server.level.ServerEntity;
import net.minecraft.server.network.ServerPlayerConnection;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@@ -19,7 +18,4 @@ import java.util.Set;
public interface TrackedEntityAccessor {
@Accessor("seenBy")
Set<ServerPlayerConnection> getSeenBy();
@Accessor("serverEntity")
ServerEntity getServerEntity();
}
@@ -1,131 +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.brewing;
import com.cobblemon.mod.common.CobblemonRecipeTypes;
import com.cobblemon.mod.common.item.crafting.brewingstand.BrewingStandInput;
import com.cobblemon.mod.common.item.crafting.brewingstand.BrewingStandRecipe;
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;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.LevelEvent;
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;
@Mixin(BrewingStandBlockEntity.class)
public class BrewingStandBlockEntityMixin {
@WrapOperation(method = "serverTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/BrewingStandBlockEntity;doBrew(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/core/NonNullList;)V"))
private static void cobblemon$doBrew(Level level, BlockPos pos, NonNullList<ItemStack> slots, Operation<Void> original) {
var recipe = cobblemon$fetchBrewingRecipe(slots, level);
if(recipe == null) {
original.call(level, pos, slots);
return;
}
//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());
}
}
itemStack.shrink(1);
if (itemStack.getItem().hasCraftingRemainingItem()) {
ItemStack itemStack2 = new ItemStack(itemStack.getItem().getCraftingRemainingItem());
if (itemStack.isEmpty()) {
itemStack = itemStack2;
} else {
Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), itemStack2);
}
}
slots.set(3, itemStack);
level.levelEvent(LevelEvent.SOUND_BREWING_STAND_BREW, pos, 0);
}
@WrapOperation(method = "serverTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/BrewingStandBlockEntity;isBrewable(Lnet/minecraft/world/item/alchemy/PotionBrewing;Lnet/minecraft/core/NonNullList;)Z"))
private static boolean cobblemon$isBrewable(PotionBrewing potionBrewing, NonNullList<ItemStack> items, Operation<Boolean> original,
@Local(argsOnly = true) Level level) {
return cobblemon$fetchBrewingRecipe(items, level) != null || original.call(potionBrewing, items);
}
@Unique
private static BrewingStandRecipe cobblemon$fetchBrewingRecipe(NonNullList<ItemStack> items, Level level) {
ItemStack ingredient = items.get(3);
List<ItemStack> bottles = items.subList(0, 3);
boolean allBottlesEmpty = true;
for (ItemStack bottle : bottles) {
if (!bottle.isEmpty()) {
allBottlesEmpty = false;
break;
}
}
if (ingredient.isEmpty() || allBottlesEmpty) {
return null;
}
BrewingStandInput input = new BrewingStandInput(ingredient, bottles);
RecipeManager recipeManager = level.getRecipeManager();
Optional<RecipeHolder<BrewingStandRecipe>> recipeHolder =
recipeManager.getRecipeFor(CobblemonRecipeTypes.INSTANCE.getBREWING_STAND(), input, level);
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);
}
}
}
}
}
}
@@ -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.mixin.brewing;
import com.cobblemon.mod.common.duck.RecipeAwareSlot;
import com.cobblemon.mod.common.item.crafting.brewingstand.BrewingStandRecipe;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.BrewingStandMenu;
import net.minecraft.world.inventory.ContainerData;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(BrewingStandMenu.class)
public abstract class BrewingStandMenuMixin {
@Inject(method = "<init>(ILnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/Container;Lnet/minecraft/world/inventory/ContainerData;)V", at = @At("TAIL"))
private void captureLevel(int containerId, Inventory playerInventory, Container brewingStandContainer, ContainerData brewingStandData, CallbackInfo ci) {
var recipeManager = playerInventory.player.level().getRecipeManager();
for (Slot slot : ((BrewingStandMenu) (Object) this).slots) {
if (slot instanceof RecipeAwareSlot awareSlot) {
awareSlot.setRecipeManager(recipeManager);
}
}
}
@WrapOperation(method = "quickMoveStack", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/BrewingStandMenu$PotionSlot;mayPlaceItem(Lnet/minecraft/world/item/ItemStack;)Z"))
private boolean cobblemon$isPotionBottle(ItemStack stack, Operation<Boolean> original, @Local(argsOnly = true) Player player) {
return BrewingStandRecipe.Companion.isBottle(stack, player.level().getRecipeManager()) || original.call(stack);
}
}
@@ -1,44 +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.brewing;
import com.cobblemon.mod.common.duck.RecipeAwareSlot;
import com.cobblemon.mod.common.item.crafting.brewingstand.BrewingStandRecipe;
import net.minecraft.world.inventory.BrewingStandMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.level.Level;
import 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;
@Mixin(BrewingStandMenu.IngredientsSlot.class)
public class IngredientSlotMixin implements RecipeAwareSlot {
@Unique
private RecipeManager cobblemon$recipeManager;
@Override
public void setRecipeManager(RecipeManager recipeManager) {
this.cobblemon$recipeManager = recipeManager;
}
@Inject(
method = "mayPlace",
at = @At("HEAD"),
cancellable = true
)
private void cobblemon$mayPlace(ItemStack stack, CallbackInfoReturnable<Boolean> cir) {
if (BrewingStandRecipe.Companion.isInput(stack, cobblemon$recipeManager)) {
cir.setReturnValue(true);
}
}
}
@@ -1,44 +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.brewing;
import com.cobblemon.mod.common.duck.RecipeAwareSlot;
import com.cobblemon.mod.common.item.crafting.brewingstand.BrewingStandRecipe;
import net.minecraft.world.inventory.BrewingStandMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.level.Level;
import 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;
@Mixin(BrewingStandMenu.PotionSlot.class)
public class PotionSlotMixin implements RecipeAwareSlot {
@Unique
private RecipeManager cobblemon$recipeManager;
@Override
public void setRecipeManager(RecipeManager recipeManager) {
this.cobblemon$recipeManager = recipeManager;
}
@Inject(
method = "mayPlace",
at = @At("HEAD"),
cancellable = true
)
private void cobblemon$mayPlace(ItemStack stack, CallbackInfoReturnable<Boolean> cir) {
if (BrewingStandRecipe.Companion.isBottle(stack, cobblemon$recipeManager)) {
cir.setReturnValue(true);
}
}
}
@@ -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) {
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.CobblemonRecipeCategories;
import com.cobblemon.mod.common.item.crafting.CookingPotBookCategory;
import com.cobblemon.mod.common.item.crafting.CookingPotRecipe;
import com.cobblemon.mod.common.item.crafting.CookingPotRecipeBase;
import com.cobblemon.mod.common.item.crafting.CookingPotShapelessRecipe;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -20,6 +19,7 @@ import net.minecraft.client.ClientRecipeBook;
import net.minecraft.client.RecipeBookCategories;
import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
import net.minecraft.core.RegistryAccess;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.item.crafting.RecipeHolder;
import org.spongepowered.asm.mixin.Mixin;
@@ -40,23 +40,23 @@ public abstract class ClientRecipeBookMixin {
return null;
}
@Shadow
private Map<RecipeBookCategories, List<RecipeCollection>> collectionsByTab;
@Shadow private Map<RecipeBookCategories, List<RecipeCollection>> collectionsByTab;
@Shadow
private List<RecipeCollection> allCollections;
@Shadow private List<RecipeCollection> allCollections;
@Inject(method = "getCategory", at = @At(value = "HEAD"), cancellable = true)
private static void addCustomCategory(RecipeHolder<?> recipe, CallbackInfoReturnable<RecipeBookCategories> cir) {
Recipe<?> recipe2 = recipe.value();
if (recipe2 instanceof CookingPotRecipeBase) {
CookingPotBookCategory category = ((CookingPotRecipeBase) recipe2).getCategory();
if (recipe2 instanceof CookingPotRecipe || recipe2 instanceof CookingPotShapelessRecipe) {
CookingPotBookCategory category = recipe2 instanceof CookingPotRecipe
? ((CookingPotRecipe) recipe2).getCategory()
: ((CookingPotShapelessRecipe) recipe2).category();
RecipeBookCategories var7;
switch (category) {
case MISC -> var7 = CobblemonRecipeCategories.COOKING_POT_MISC.toVanillaCategory();
case FOODS -> var7 = CobblemonRecipeCategories.COOKING_POT_FOODS.toVanillaCategory();
case MEDICINES -> var7 = CobblemonRecipeCategories.COOKING_POT_MEDICINES.toVanillaCategory();
case COMPLEX_DISHES -> var7 = CobblemonRecipeCategories.COOKING_POT_COMPLEX_DISHES.toVanillaCategory();
case BAITS -> var7 = CobblemonRecipeCategories.COOKING_POT_BAITS.toVanillaCategory();
default -> throw new MatchException(null, null);
}
cir.setReturnValue(var7);
@@ -76,8 +76,8 @@ public abstract class ClientRecipeBookMixin {
.collect(ImmutableList.toImmutableList());
map2.put(recipeBookCategories, collections);
});
RecipeBookCategories.AGGREGATE_CATEGORIES.forEach((recipeBookCategories, list) -> map2.put(recipeBookCategories, (List) list.stream().flatMap((recipeBookCategoriesx) -> ((List) map2.getOrDefault(recipeBookCategoriesx, ImmutableList.of())).stream()).collect(ImmutableList.toImmutableList())));
CobblemonRecipeCategories.Companion.getCustomAggregateCategories().forEach((recipeBookCategories, list) -> map2.put(recipeBookCategories, (List) list.stream().flatMap((recipeBookCategoriesx) -> ((List) map2.getOrDefault(recipeBookCategoriesx, ImmutableList.of())).stream()).collect(ImmutableList.toImmutableList())));
RecipeBookCategories.AGGREGATE_CATEGORIES.forEach((recipeBookCategories, list) -> map2.put(recipeBookCategories, (List)list.stream().flatMap((recipeBookCategoriesx) -> ((List)map2.getOrDefault(recipeBookCategoriesx, ImmutableList.of())).stream()).collect(ImmutableList.toImmutableList())));
CobblemonRecipeCategories.Companion.getCustomAggregateCategories().forEach((recipeBookCategories, list) -> map2.put(recipeBookCategories, (List)list.stream().flatMap((recipeBookCategoriesx) -> ((List)map2.getOrDefault(recipeBookCategoriesx, ImmutableList.of())).stream()).collect(ImmutableList.toImmutableList())));
this.collectionsByTab = ImmutableMap.copyOf(map2);
this.allCollections = builder.build();
@@ -9,21 +9,22 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.gui.ScrollingWidget;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.client.gui.components.AbstractSelectionList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
@Mixin(AbstractSelectionList.class)
public abstract class EntryListWidgetMixin {
@SuppressWarnings("ConstantValue")
@ModifyExpressionValue(method = "getEntryAtPosition", at = @At(value = "CONSTANT", args = {"intValue=4"}))
public int cobblemon$adjustOnlyIfNecessary(int original) {
@ModifyConstant(method = "getEntryAtPosition", constant = @Constant(intValue = 4))
public int cobblemon$adjustOnlyIfNecessary(int input) {
if(ScrollingWidget.class.isInstance(this)) {
return 0;
}
return original;
return input;
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.client.model.HumanoidModel;
@@ -19,17 +18,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 +39,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 +50,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)
);
if (vehicle instanceof PokemonEntity pokemonEntity && entity instanceof AbstractClientPlayer) {
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) -> {
return behaviour.shouldRotatePlayerHead(settings, state, pokemonEntity);
});
if (!shouldRotatePlayerHead) {
netHeadYaw = 0f;
@@ -77,10 +73,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, (AbstractClientPlayer) entity, relevantPartsByName, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
}
}
}
@@ -1,37 +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.entity.MountedLivingEntityRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.model.EntityModel;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.world.entity.LivingEntity;
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(LivingEntityRenderer.class)
public class LivingEntityRendererMixin<T extends LivingEntity, M extends EntityModel<T>> {
@Inject(
method = "setupRotations",
at = @At("HEAD")
)
private void cobblemon$modifyRoll(LivingEntity entity, PoseStack poseStack, float bob, float yBodyRot, float partialTick, float scale, CallbackInfo ci) {
if (entity.isPassenger()) {
var vehicle = entity.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity) {
MountedLivingEntityRenderer.INSTANCE.render(entity, pokemonEntity, poseStack, bob, yBodyRot, partialTick, scale);
}
}
}
}
@@ -9,67 +9,28 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.CobblemonNetwork;
import com.cobblemon.mod.common.DoubleJump;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.riding.Seat;
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate;
import com.cobblemon.mod.common.client.render.MatrixWrapper;
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState;
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel;
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository;
import com.cobblemon.mod.common.duck.PlayerDuck;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket;
import com.mojang.authlib.GameProfile;
import net.minecraft.client.CameraType;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.player.Input;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.joml.Matrix3f;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Map;
@Mixin(LocalPlayer.class)
public abstract class LocalPlayerMixin extends AbstractClientPlayer implements OrientationControllable, DoubleJump {
public abstract class LocalPlayerMixin extends LivingEntity {
@Unique Matrix3f cobblemon$lastOrientation;
@Unique boolean cobblemon$isDoubleJumping = false;
@Shadow
private float jumpRidingScale;
@Shadow public Input input;
@Shadow private int autoJumpTime;
@Unique private int cobblemon$survivalJumpTriggerTime;
@Unique private boolean cobblemon$isJumping;
public LocalPlayerMixin(ClientLevel clientLevel, GameProfile gameProfile) {
super(clientLevel, gameProfile);
protected LocalPlayerMixin(EntityType<? extends LivingEntity> entityType, Level level) {
super(entityType, level);
}
// @Inject(method = "sendPosition", at = @At("TAIL"))
@@ -98,23 +59,6 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
controllable.getOrientationController().setActive(shouldUseCustomOrientation);
}
@Inject(method = "rideTick", at = @At("HEAD"))
private void cobblemon$updateDriverInputRideTick(CallbackInfo ci) {
if (Minecraft.getInstance().player != (Object)this) return;
if (!(this.getVehicle() instanceof PokemonEntity pokemonEntity)) return;
// Gather driver Input
float vertInput = this.jumping ? 1.0f : (((Player)this).isShiftKeyDown() ? -1.0f : 0.0f);
Vector3f driverInput = new Vector3f(Math.signum(this.xxa), vertInput, Math.signum(this.zza));
// Check if player input has changed. If it has then update the last sent and send it
Vector3f lastSentDriverInput = ((PlayerDuck)this).getLastSentDriverInput();
if (driverInput.equals(lastSentDriverInput)) return;
((PlayerDuck)this).setLastSentDriverInput(driverInput);
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateDriverInputPacket(driverInput));
}
@Unique
private boolean cobblemon$shouldUseCustomOrientation(LocalPlayer player) {
var playerVehicle = player.getVehicle();
@@ -132,108 +76,4 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
if (!(this instanceof OrientationControllable controllable)) return;
controllable.getOrientationController().setActive(false);
}
@Inject(method = "getJumpRidingScale", at = @At("HEAD"), cancellable = true)
public void modifyJumpRidingScale(CallbackInfoReturnable<Float> cir) {
LocalPlayer player = (LocalPlayer) (Object) this;
if (player.isPassenger() && player.getVehicle() instanceof PokemonEntity pokemonEntity) {
var rideValue = pokemonEntity.<Float>ifRidingAvailableSupply(null, (behaviour, settings, state) -> {
if (behaviour.canJump(settings, state, pokemonEntity, player)) return null;
return behaviour.setRideBar(settings, state, pokemonEntity, player);
});
if (rideValue != null) {
cir.setReturnValue(rideValue);
}
}
}
@Inject(method = "aiStep", at = @At("HEAD"))
private void cobblemon$aiStep(CallbackInfo ci) {
this.cobblemon$isDoubleJumping = false;
this.cobblemon$isJumping = this.input.jumping;
}
@Inject(method = "aiStep", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;isSprinting()Z", shift = At.Shift.BEFORE))
private void cobblemon$updateDoubleJumping(CallbackInfo ci) {
if (!this.cobblemon$isJumping && this.input.jumping && this.autoJumpTime <= 0 && !this.isSwimming()) {
if (this.cobblemon$survivalJumpTriggerTime != 0) {
this.cobblemon$isDoubleJumping = true;
this.cobblemon$survivalJumpTriggerTime = 0;
}
else {
this.cobblemon$survivalJumpTriggerTime = 7;
}
}
else if (this.cobblemon$survivalJumpTriggerTime > 0) {
this.cobblemon$survivalJumpTriggerTime--;
}
}
@Override
public boolean isDoubleJumping() {
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();
if (vehicle instanceof PokemonEntity pokemonEntity) {
PokemonClientDelegate delegate = (PokemonClientDelegate) pokemonEntity.getDelegate();
String locatorName = delegate.getSeatLocator(this);
MatrixWrapper locator = delegate.getLocatorStates().get(locatorName);
if (locator == null) {
return super.pick(hitDistance, partialTicks, hitFluids);
}
Vec3 locatorOffset = locator != null ? new Vec3(locator.getMatrix().getTranslation(new Vector3f())) : Vec3.ZERO;
OrientationController controller = this.getOrientationController();
float currEyeHeight = this.getEyeHeight();
Vector3f offset = new Vector3f(new Vector3f(0f, currEyeHeight - (this.getBbHeight() / 2), 0f));
if (Minecraft.getInstance().options.getCameraType() == CameraType.FIRST_PERSON) {
PosableModel model = VaryingModelRepository.INSTANCE.getPoser(pokemonEntity.getPokemon().getSpecies().getResourceIdentifier(), new FloatingState());
offset.add(cobblemon$getFirstPersonOffset(model, locatorName));
}
Matrix3f orientation = controller.isActive() && controller.getOrientation() != null ? controller.getOrientation() : new Matrix3f();
Vec3 rotatedEyeHeight = new Vec3(orientation.transform(offset));
Vec3 eyePosition = locatorOffset.add(pokemonEntity.position()).add(rotatedEyeHeight);
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 static @NotNull Vector3f cobblemon$getFirstPersonOffset(PosableModel model, String locatorName) {
Map<String, Vec3> cameraOffsets = model.getFirstPersonCameraOffset();
if (cameraOffsets.containsKey(locatorName)) {
return cameraOffsets.get(locatorName).toVector3f();
} else {
return new Vector3f(0f, 0f, 0f);
}
}
}
@@ -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;
@@ -31,6 +30,7 @@ import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(MouseHandler.class)
public abstract class MouseHandlerMixin {
@@ -117,24 +117,16 @@ 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();
this.smoothTurnX.reset();
var defaultSensitivity = this.minecraft.options.sensitivity().get() * 0.6 + 0.2;
var defaultSensitivity = this.minecraft.options.sensitivity().get() * 0.6000000238418579 + 0.20000000298023224;
var spyglassSensitivity = Math.pow(defaultSensitivity, 3);
var lookSensitivity = spyglassSensitivity * 8.0;
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);
player.turn(this.accumulatedDX * sensitivity, (this.accumulatedDY * sensitivity));
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,
@@ -9,16 +9,20 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.render.ClientPlayerIcon;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.model.EntityModel;
import net.minecraft.client.model.PlayerModel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -31,9 +35,9 @@ public abstract class PlayerRendererMixin extends LivingEntityRenderer<AbstractC
super(context, model, shadowRadius);
}
@Inject(method = "render", at = @At(value = "TAIL"))
public void renderPlayer(AbstractClientPlayer player, float f, float g, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, CallbackInfo ci) {
ClientPlayerIcon.Companion.onRenderPlayer(player);
@Inject(method = "render(Lnet/minecraft/world/entity/LivingEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V", at = @At(value = "TAIL"))
public void cobblemon$clientPlayerIcon(LivingEntity entity, float entityYaw, float partialTicks, PoseStack poseStack, MultiBufferSource buffer, int packedLight, CallbackInfo ci) {
ClientPlayerIcon.Companion.onRenderPlayer((Player) entity);
}
@WrapOperation(method = "getRenderOffset(Lnet/minecraft/client/player/AbstractClientPlayer;F)Lnet/minecraft/world/phys/Vec3;", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/AbstractClientPlayer;isCrouching()Z"))
@@ -51,4 +55,17 @@ public abstract class PlayerRendererMixin extends LivingEntityRenderer<AbstractC
playerModel.crouching = false;
}
}
@Inject(
method = "setupRotations(Lnet/minecraft/client/player/AbstractClientPlayer;Lcom/mojang/blaze3d/vertex/PoseStack;FFFF)V",
at = @At("HEAD")
)
private void cobblemon$modifyRoll(AbstractClientPlayer player, PoseStack poseStack, float a, float b, float partialTicks, float i, CallbackInfo ci) {
if (player.isPassenger()) {
var vehicle = player.getVehicle();
if (vehicle instanceof PokemonEntity entity) {
MountedPlayerRenderer.INSTANCE.render(player, entity, poseStack, a, b, partialTicks, i);
}
}
}
}
@@ -50,10 +50,10 @@ public class RecipeBookCategoriesMixin {
private static void cobblemon$addRecipeBookType(CallbackInfo ci) {
ArrayList<RecipeBookCategories> types = new ArrayList<>(List.of($VALUES));
types.add(cobblemon$createCategory("COOKING_POT_SEARCH", $VALUES.length, new ItemStack(Items.COMPASS)));
types.add(cobblemon$createCategory("COOKING_POT_FOODS", $VALUES.length + 1, new ItemStack(CobblemonItems.LEEK_AND_POTATO_STEW)));
types.add(cobblemon$createCategory("COOKING_POT_MEDICINES", $VALUES.length + 2, new ItemStack(CobblemonItems.POTION)));
types.add(cobblemon$createCategory("COOKING_POT_COMPLEX_DISHES", $VALUES.length + 3, new ItemStack(CobblemonItems.POKE_PUFF)));
types.add(cobblemon$createCategory("COOKING_POT_MISC", $VALUES.length + 4, new ItemStack(CobblemonItems.PROTEIN)));
types.add(cobblemon$createCategory("COOKING_POT_FOODS", $VALUES.length + 1, new ItemStack(CobblemonItems.ROASTED_LEEK)));
types.add(cobblemon$createCategory("COOKING_POT_MEDICINES", $VALUES.length + 2, new ItemStack(CobblemonItems.HP_UP)));
types.add(cobblemon$createCategory("COOKING_POT_BAITS", $VALUES.length + 3, new ItemStack(CobblemonItems.PERSIM_BERRY)));
types.add(cobblemon$createCategory("COOKING_POT_MISC", $VALUES.length + 4, new ItemStack(CobblemonItems.MIRROR_HERB)));
$VALUES = types.toArray(RecipeBookCategories[]::new);
}
@@ -64,7 +64,7 @@ public class RecipeBookCategoriesMixin {
CobblemonRecipeCategories.COOKING_POT_SEARCH.toVanillaCategory(),
CobblemonRecipeCategories.COOKING_POT_FOODS.toVanillaCategory(),
CobblemonRecipeCategories.COOKING_POT_MEDICINES.toVanillaCategory(),
CobblemonRecipeCategories.COOKING_POT_COMPLEX_DISHES.toVanillaCategory(),
CobblemonRecipeCategories.COOKING_POT_BAITS.toVanillaCategory(),
CobblemonRecipeCategories.COOKING_POT_MISC.toVanillaCategory());
cir.setReturnValue(var10000);
return;
@@ -1,60 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.client;
import net.minecraft.client.searchtree.IdSearchTree;
import net.minecraft.client.searchtree.SearchTree;
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.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@Mixin(IdSearchTree.class)
public abstract class SearchTreeMixin {
@Unique
private static final ThreadLocal<Boolean> reentryFlag = ThreadLocal.withInitial(() -> false);
/**
* Runs an additional search using the original search term where every instance of 'e' is replaced with 'é'
* Appends additional search results to original search results.
*/
@Inject(method = "search(Ljava/lang/String;)Ljava/util/List;", at = @At("RETURN"), cancellable = true)
private void onSearch(String string, CallbackInfoReturnable<List> cir) {
// If flag is true, skip to avoid infinite loop
if (reentryFlag.get()) {
return;
}
try {
reentryFlag.set(true);
List originalResults = cir.getReturnValue();
if (!string.contains("poke")) {
return;
}
String modifiedString = string.replace("poke", "poké");
List additionalResults = ((SearchTree)this).search(modifiedString);
Set combinedSet = new LinkedHashSet<>();
combinedSet.addAll(originalResults);
combinedSet.addAll(additionalResults);
cir.setReturnValue(new ArrayList<>(combinedSet));
} finally {
reentryFlag.set(false);
}
}
}
@@ -1,71 +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.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 kotlin.Unit;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Mixin(Minecraft.class)
public final class SnapshotWarningMixin {
@Unique
private final Pattern VERSION = Pattern.compile("^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)$");
@Inject(method = "addInitialScreens", at = @At("TAIL"))
public void cobblemon$addSnapshotWarningScreen(List<Function<Runnable, Screen>> output, CallbackInfo callback) {
if (CobblemonBuildDetails.SNAPSHOT && !this.cobblemon$isAcknowledged() && !SharedConstants.IS_RUNNING_IN_IDE) {
output.add((runnable) -> new SnapshotWarningScreen((acknowledgement, dsa) -> {
if (acknowledgement == SnapshotWarningScreen.Acknowledgement.NO) {
Minecraft.getInstance().stop();
return Unit.INSTANCE;
}
ClientPersistedData.INSTANCE.writeSnapshotsData(new ClientPersistedData.SnapshotAcknowledgementData(CobblemonBuildDetails.VERSION, dsa));
runnable.run();
return Unit.INSTANCE;
}));
}
}
@Unique
@SuppressWarnings("ResultOfMethodCallIgnored")
private boolean cobblemon$isAcknowledged() {
ClientPersistedData.SnapshotAcknowledgementData data = ClientPersistedData.INSTANCE.readSnapshotsData();
if (data == null) {
return false;
}
Matcher current = VERSION.matcher(CobblemonBuildDetails.VERSION);
current.find();
Matcher when = VERSION.matcher(data.getVersion());
when.find();
boolean alert = current.group("minor").compareTo(when.group("minor")) > 0 || current.group("major").compareTo(when.group("major")) > 0;
return !alert && data.getDontShowAgain();
}
}
@@ -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);
}
}
@@ -40,9 +40,9 @@ import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.scheduling.ServerRealTimeTaskTracker
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.CobblemonSpawningZoneGenerator
import com.cobblemon.mod.common.api.spawning.position.AreaSpawnablePositionResolver
import com.cobblemon.mod.common.api.spawning.SpawningZoneGenerator
import com.cobblemon.mod.common.api.spawning.CobblemonSpawningProspector
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
import com.cobblemon.mod.common.api.starter.StarterHandler
import com.cobblemon.mod.common.api.storage.PokemonStoreManager
import com.cobblemon.mod.common.api.storage.adapter.conversions.ReforgedConversion
@@ -63,10 +63,13 @@ import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataMongoBacken
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.*
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.ShowdownThread
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.block.DispenserBehaviorRegistry
import com.cobblemon.mod.common.client.CobblemonPack
import com.cobblemon.mod.common.command.argument.*
import com.cobblemon.mod.common.config.CobblemonConfig
@@ -75,8 +78,7 @@ import com.cobblemon.mod.common.config.constraint.IntConstraint
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
@@ -89,11 +91,23 @@ import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.pokemon.properties.*
import com.cobblemon.mod.common.pokemon.properties.AspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
import com.cobblemon.mod.common.pokemon.properties.FreezeFrameProperty
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
import com.cobblemon.mod.common.pokemon.properties.NoAIProperty
import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifDedicatedServer
import com.cobblemon.mod.common.util.isLaterVersion
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.requestWallpapers
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
@@ -101,8 +115,20 @@ import com.mongodb.ConnectionString
import com.mongodb.MongoClientSettings
import com.mongodb.client.MongoClient
import com.mongodb.client.MongoClients
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.UUID
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
import net.minecraft.client.Minecraft
import net.minecraft.commands.synchronization.SingletonArgumentInfo
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
@@ -113,17 +139,6 @@ import net.minecraft.world.level.Level
import net.minecraft.world.level.storage.LevelResource
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
object Cobblemon {
const val MODID = CobblemonBuildDetails.MOD_ID
@@ -145,8 +160,8 @@ object Cobblemon {
var isDedicatedServer = false
val showdownThread = ShowdownThread()
lateinit var config: CobblemonConfig
var spawningZoneGenerator: SpawningZoneGenerator = CobblemonSpawningZoneGenerator
var areaSpawnablePositionResolver: AreaSpawnablePositionResolver = object : AreaSpawnablePositionResolver {}
var prospector: SpawningProspector = CobblemonSpawningProspector
var areaContextResolver: AreaContextResolver = object : AreaContextResolver {}
val bestSpawner = BestSpawner
val battleRegistry = BattleRegistry
var storage = PokemonStoreManager()
@@ -198,7 +213,6 @@ object Cobblemon {
implementation.registerEntityDataSerializers()
implementation.registerCriteria()
implementation.registerEntitySubPredicates()
DispenserBehaviorRegistry.registerDispenserBehaviors()
DropEntry.register("command", CommandDropEntry::class.java)
DropEntry.register("item", ItemDropEntry::class.java, isDefault = true)
@@ -298,6 +312,9 @@ object Cobblemon {
SpeciesFeatures.types["flag"] = FlagSpeciesFeatureProvider::class.java
SpeciesFeatures.types["integer"] = IntSpeciesFeatureProvider::class.java
SpeciesFeatures.register(
DataKeys.CAN_BE_MILKED,
FlagSpeciesFeatureProvider(keys = listOf(DataKeys.CAN_BE_MILKED), default = true))
SpeciesFeatures.register(
DataKeys.HAS_BEEN_SHEARED,
FlagSpeciesFeatureProvider(keys = listOf(DataKeys.HAS_BEEN_SHEARED), default = false))
@@ -313,8 +330,7 @@ object Cobblemon {
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit(Unit)
CallbackHandler.setup()
EntityCallbackHandler.setup()
FlowHandler.setup()
ifDedicatedServer {
isDedicatedServer = true
@@ -18,8 +18,6 @@ object CobblemonActivities {
val POKEMON_SLEEPING_ACTIVITY = register(Activity("pokemon_sleeping"))
val POKEMON_GROW_CROP = register(Activity("pokemon_grow_crop"))
val POKEMON_SLEEP_ON_TRAINER_BED = register(Activity("pokemon_sleep_on_trainer_bed"))
val POKEMON_HERD = register(Activity("pokemon_herd"))
val POKEMON_POLLINATION = register(Activity("pokemon_pollination"))
fun register(activity: Activity): Activity {
activities.add(activity)
@@ -25,8 +25,6 @@ import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionOrEntityVariableAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.MemoryModuleTypeAdapter
import com.cobblemon.mod.common.util.adapters.SensorTypeAdapter
import com.cobblemon.mod.common.util.adapters.TaskConfigAdapter
import com.cobblemon.mod.common.util.adapters.TranslatedTextAdapter
import com.cobblemon.mod.common.util.cobblemonResource
@@ -41,8 +39,6 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.SensorType
import net.minecraft.world.entity.schedule.Activity
object CobblemonBehaviours : JsonDataRegistry<CobblemonBehaviour> {
@@ -51,8 +47,6 @@ object CobblemonBehaviours : JsonDataRegistry<CobblemonBehaviour> {
.registerTypeAdapter(Activity::class.java, ActivityAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(MemoryModuleType::class.java, MemoryModuleTypeAdapter)
.registerTypeAdapter(SensorType::class.java, SensorTypeAdapter)
.registerTypeAdapter(BehaviourConfig::class.java, BehaviourConfigAdapter)
.registerTypeAdapter(TaskConfig::class.java, TaskConfigAdapter)
.registerTypeAdapter(
@@ -78,9 +72,6 @@ object CobblemonBehaviours : JsonDataRegistry<CobblemonBehaviour> {
*/
val autoPokemonBehaviours = mutableListOf<CobblemonBehaviour>()
/** See [autoPokemonBehaviours] but think npc instead of pokemon*/
val autoNPCBehaviours = mutableListOf<CobblemonBehaviour>()
val behaviours = mutableMapOf<ResourceLocation, CobblemonBehaviour>()
override fun sync(player: ServerPlayer) {
@@ -89,7 +80,6 @@ object CobblemonBehaviours : JsonDataRegistry<CobblemonBehaviour> {
override fun reload(manager: ResourceManager) {
autoPokemonBehaviours.clear()
autoNPCBehaviours.clear()
val data = mutableMapOf<ResourceLocation, CobblemonBehaviour>()
manager.listResources(resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
resource.open().use { stream ->
@@ -98,8 +88,6 @@ object CobblemonBehaviours : JsonDataRegistry<CobblemonBehaviour> {
val behaviour = parse(reader, resolvedIdentifier)
if ("pokemon/auto/" in identifier.path) {
autoPokemonBehaviours.add(behaviour)
} else if ("npc/auto/" in identifier.path) {
autoNPCBehaviours.add(behaviour)
}
data[resolvedIdentifier] = behaviour
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.block.entity.PokeCakeBlockEntity
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
import com.cobblemon.mod.common.block.entity.PCBlockEntity
@@ -87,23 +88,35 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
BlockEntityType.Builder.of(::DisplayCaseBlockEntity, CobblemonBlocks.DISPLAY_CASE).build(null)
)
// TODO after 1.7
// @JvmField
// val INCENSE_SWEET: BlockEntityType<SweetIncenseBlockEntity> = this.create("incense_sweet",
// BlockEntityType.Builder.of(::SweetIncenseBlockEntity, CobblemonBlocks.INCENSE_SWEET).build(null)
// )
@JvmField
val INCENSE_SWEET: BlockEntityType<SweetIncenseBlockEntity> = this.create("incense_sweet",
BlockEntityType.Builder.of(::SweetIncenseBlockEntity, CobblemonBlocks.INCENSE_SWEET).build(null)
)
@JvmField
val CAMPFIRE: BlockEntityType<CampfireBlockEntity> = this.create("campfire_pot",
BlockEntityType.Builder.of(::CampfireBlockEntity,
CobblemonBlocks.CAMPFIRE,
CobblemonBlocks.SOUL_CAMPFIRE
).build(null)
BlockEntityType.Builder.of(::CampfireBlockEntity, CobblemonBlocks.CAMPFIRE).build(null)
)
/*@JvmField
val LURE_CAKE: BlockEntityType<LureCakeBlockEntity> =
BlockEntityType.Builder.of({ pos, state -> LureCakeBlockEntity(pos, state) },
CobblemonBlocks.LURE_CAKE
).build(null)*/
@JvmField
val POKE_SNACK: BlockEntityType<PokeSnackBlockEntity> = this.create(
"poke_snack",
BlockEntityType.Builder.of(::PokeSnackBlockEntity, CobblemonBlocks.POKE_SNACK, CobblemonBlocks.POKE_CAKE).build(null)
val LURE_CAKE: BlockEntityType<LureCakeBlockEntity> = this.create(
"lure_cake",
BlockEntityType.Builder.of(::LureCakeBlockEntity, CobblemonBlocks.LURE_CAKE).build(null)
)
@JvmField
val POKE_CAKE: BlockEntityType<PokeCakeBlockEntity> = this.create(
"poke_cake",
BlockEntityType.Builder.of(::PokeCakeBlockEntity, CobblemonBlocks.POKE_CAKE).build(null)
)
@JvmField
val CANDLE_POKE_CAKE: BlockEntityType<CandlePokeCakeBlockEntity> = this.create(
"candle_poke_cake",
BlockEntityType.Builder.of(::CandlePokeCakeBlockEntity, CobblemonBlocks.CANDLE_POKE_CAKE).build(null)
)
}
@@ -15,14 +15,10 @@ import com.cobblemon.mod.common.block.LecternBlock
import com.cobblemon.mod.common.block.MintBlock.MintType
import com.cobblemon.mod.common.block.campfirepot.CampfirePotBlock
import com.cobblemon.mod.common.block.chest.GildedChestBlock
import com.cobblemon.mod.common.block.general.BaleBlock
import com.cobblemon.mod.common.block.grower.SaccharineTreeGrower
import com.cobblemon.mod.common.block.sign.CobblemonHangingSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonWallHangingSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonWallSignBlock
import com.cobblemon.mod.common.block.general.HorizontalRotationCarpetBlock
import com.cobblemon.mod.common.block.general.HorizontalRotationalBlock
import com.cobblemon.mod.common.mixin.invoker.*
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.util.cobblemonResource
@@ -40,7 +36,6 @@ import net.minecraft.world.level.block.*
import net.minecraft.world.level.block.state.BlockBehaviour
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.properties.BlockSetType
import net.minecraft.world.level.block.state.properties.BlockStateProperties
import net.minecraft.world.level.block.state.properties.BlockStateProperties.LIT
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument
import net.minecraft.world.level.block.state.properties.WoodType
@@ -128,7 +123,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
@@ -177,14 +172,14 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val SACCHARINE_LOG = this.create("saccharine_log", SaccharineLogBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0F).sound(SoundType.WOOD).ignitedByLava()))
@JvmField
val SACCHARINE_LOG_SLATHERED = this.create("saccharine_log_slathered", SaccharineLogSlatheredBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0F).sound(SoundType.WOOD).ignitedByLava()))
val SACCHARINE_HONEY_LOG = this.create("saccharine_honey_log", SaccharineHoneyLogBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0F).sound(SoundType.WOOD).ignitedByLava()))
@JvmField
val STRIPPED_SACCHARINE_LOG = this.create("stripped_saccharine_log", RotatedPillarBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0F).sound(SoundType.WOOD).ignitedByLava())) //log("stripped_saccharine_log")
val STRIPPED_SACCHARINE_LOG = this.create("stripped_saccharine_log", SaccharineStrippedLogBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0F).sound(SoundType.WOOD).ignitedByLava())) //log("stripped_saccharine_log")
@JvmField
val SACCHARINE_WOOD = log("saccharine_wood")
@JvmField
val STRIPPED_SACCHARINE_WOOD = this.create("stripped_saccharine_wood", RotatedPillarBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0F).sound(SoundType.WOOD).ignitedByLava())) //log("stripped_saccharine_log")
val STRIPPED_SACCHARINE_WOOD = this.create("stripped_saccharine_wood", SaccharineStrippedLogBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0F).sound(SoundType.WOOD).ignitedByLava())) //log("stripped_saccharine_log")
@JvmField
val SACCHARINE_PLANKS = this.create("saccharine_planks", Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
@JvmField
@@ -205,15 +200,15 @@ 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()))
@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)))
val SACCHARINE_BUTTON = this.create("saccharine_button", BlocksInvoker.createWoodenButtonBlock(BlockSetType.OAK))
@JvmField
val SACCHARINE_PRESSURE_PLATE = this.create("saccharine_pressure_plate", PressurePlateBlockInvoker.`cobblemon$create`(SACCHARINE_BLOCK_SET_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).noCollission().strength(0.5f).sound(SoundType.WOOD)))
@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
@@ -227,14 +222,12 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val SACCHARINE_TRAPDOOR = this.create("saccharine_trapdoor", TrapdoorBlockInvoker.`cobblemon$create`(SACCHARINE_BLOCK_SET_TYPE, BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(3.0F).sound(SoundType.WOOD).noOcclusion()))
@JvmField
val SACCHARINE_SAPLING = this.create("saccharine_sapling", SaplingBlock(SaccharineTreeGrower(), PLANT_PROPERTIES))
val SACCHARINE_SAPLING = this.create("saccharine_sapling", SaccharineSaplingBlock(PLANT_PROPERTIES))
@JvmField
val MEDICINAL_LEEK = this.create("medicinal_leek", MedicinalLeekBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.CRIMSON_NYLIUM).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MEDICINAL_LEEK_SOUNDS)))
// TODO after 1.7
// @JvmField
// val BUGWORT = this.create("bugwort", BugwortBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.COLOR_PURPLE).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MEDICINAL_LEEK_SOUNDS)))
@JvmField
val BUGWORT = this.create("bugwort", BugwortBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.COLOR_PURPLE).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MEDICINAL_LEEK_SOUNDS)))
@JvmField
val ENERGY_ROOT = this.create("energy_root", EnergyRootBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.DIRT).noCollission().randomTicks().instabreak().sound(CobblemonSounds.ENERGY_ROOT_SOUNDS)))
@JvmField
@@ -243,9 +236,11 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).mapColor(MapColor.PLANT).ignitedByLava().noCollission().instabreak().sound(CobblemonSounds.REVIVAL_HERB_SOUNDS)))
@JvmField
val POKE_CAKE = this.create("poke_cake", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(LIT)) 3 else 0 }, false))
val LURE_CAKE = this.create("lure_cake", LureCakeBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY)))
@JvmField
val POKE_SNACK = this.create("poke_snack", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(LIT)) 3 else 0 }, true))
val POKE_CAKE = this.create("poke_cake", PokeCakeBlock(BlockBehaviour.Properties.ofFullCopy(LURE_CAKE)))
@JvmField
val CANDLE_POKE_CAKE = this.create("candle_poke_cake", CandlePokeCakeBlock(BlockBehaviour.Properties.ofFullCopy(LURE_CAKE).lightLevel { blockState -> if (blockState.getValue(LIT) == true) 3 else 0 }))
@JvmField
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
@@ -450,11 +445,8 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
)
@JvmField
val CAMPFIRE = create("campfire", CampfireBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.PODZOL).strength(2.0F).lightLevel{14}, false))
val CAMPFIRE = create("campfire", CampfireBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.PODZOL).strength(2.0F).lightLevel { if ((it.getValue(CampfireBlock.SOUL) as Boolean)) 9 else 14 }))
@JvmField
val SOUL_CAMPFIRE = create("soul_campfire", CampfireBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.PODZOL).strength(2.0F).lightLevel{9}, true))
@JvmField
val RELIC_COIN_POUCH = create("relic_coin_pouch", CoinPouchBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.RELIC_COIN_POUCH_SOUNDS).pushReaction(PushReaction.DESTROY).noOcclusion(), true))
@JvmField
@@ -478,30 +470,29 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
val GIMMIGHOUL_CHEST = create("gimmighoul_chest", GildedChestBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.CHEST).noOcclusion().sound(CobblemonSounds.GILDED_CHEST_SOUNDS), GildedChestBlock.Type.FAKE))
@JvmField
val MONITOR = create("monitor", MonitorBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(5.0F, 6.0F).lightLevel { if (it.getValue(MonitorBlock.SCREEN) != MonitorBlock.MonitorScreen.OFF) 13 else 0 }))
val MONITOR = create("monitor", MonitorBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(5.0F, 6.0F).lightLevel { if (it.getValue(MonitorBlock.SCREEN) != MonitorBlock.MonitorScreen.OFF) 15 else 0 }))
@JvmField
val FOSSIL_ANALYZER = create("fossil_analyzer", FossilAnalyzerBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(5.0F, 6.0F).noOcclusion()))
@JvmField
val RESTORATION_TANK = create("restoration_tank", RestorationTankBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.GLASS).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(5.0F, 6.0F).noOcclusion()))
@JvmField
val HEALING_MACHINE = create("healing_machine", HealingMachineBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2f).noOcclusion().lightLevel { if (it.getValue(HealingMachineBlock.CHARGE_LEVEL) >= HealingMachineBlock.MAX_CHARGE_LEVEL) 12 else 5 }))
val HEALING_MACHINE = create("healing_machine", HealingMachineBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2f).noOcclusion().lightLevel { if (it.getValue(HealingMachineBlock.CHARGE_LEVEL) >= HealingMachineBlock.MAX_CHARGE_LEVEL) 7 else 2 }))
@JvmField
val PC = create("pc", PCBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if ((it.getValue(PCBlock.ON) as Boolean) && (it.getValue(PCBlock.PART) == PCBlock.PCPart.TOP)) 13 else 0 }))
val PC = create("pc", PCBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if ((it.getValue(PCBlock.ON) as Boolean) && (it.getValue(PCBlock.PART) == PCBlock.PCPart.TOP)) 10 else 0 }))
@JvmField
val LECTERN = create("lectern", LecternBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.WOOD).strength(2.5F).ignitedByLava().lightLevel { if (it.getValue(LecternBlock.EMIT_LIGHT) as Boolean) 13 else 0 }))
val LECTERN = create("lectern", LecternBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.WOOD).strength(2.5F).ignitedByLava()))
@JvmField
val DISPLAY_CASE = create("display_case", DisplayCaseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F).requiresCorrectToolForDrops()))
// TODO after 1.7
// @JvmField
// val INCENSE_SWEET = create("incense_sweet", SweetIncenseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3f)))
val DISPLAY_CASE = create("display_case", DisplayCaseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F)))
@JvmField
val PASTURE = create("pasture", PastureBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BROWN).sound(SoundType.WOOD).strength(2F).noOcclusion().pushReaction(PushReaction.BLOCK).lightLevel { if ((it.getValue(PastureBlock.ON) as Boolean) && (it.getValue(PastureBlock.PART) == PastureBlock.PasturePart.TOP)) 13 else 0 }))
val INCENSE_SWEET = create("incense_sweet", SweetIncenseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3f)))
@JvmField
val PASTURE = create("pasture", PastureBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BROWN).sound(SoundType.WOOD).strength(2F).noOcclusion().pushReaction(PushReaction.BLOCK).lightLevel { if ((it.getValue(PastureBlock.ON) as Boolean) && (it.getValue(PastureBlock.PART) == PastureBlock.PasturePart.TOP)) 10 else 0 }))
val RED_MINT = create("red_mint", MintBlock(MintType.RED, BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_RED).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MINT_SOUNDS).pushReaction(PushReaction.DESTROY)))
@JvmField
@@ -522,34 +513,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val POTTED_PEP_UP_FLOWER = this.create("potted_pep_up_flower", BlocksInvoker.createFlowerPotBlock(PEP_UP_FLOWER))
@JvmField
val HEARTY_GRAINS = this.create("hearty_grains", HeartyGrainsBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.PLANT).noCollission().randomTicks().instabreak()))
@JvmField
val GALARICA_NUT_BUSH = this.create("galarica_nut_bush", NutBushBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).randomTicks().noCollission().sound(SoundType.SWEET_BERRY_BUSH).pushReaction(PushReaction.DESTROY)))
@JvmField
val POTTED_RED_APRICORN_SAPLING = this.create("potted_red_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(RED_APRICORN_SAPLING))
@JvmField
val POTTED_YELLOW_APRICORN_SAPLING = this.create("potted_yellow_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(YELLOW_APRICORN_SAPLING))
@JvmField
val POTTED_GREEN_APRICORN_SAPLING = this.create("potted_green_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(GREEN_APRICORN_SAPLING))
@JvmField
val POTTED_BLUE_APRICORN_SAPLING = this.create("potted_blue_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(BLUE_APRICORN_SAPLING))
@JvmField
val POTTED_PINK_APRICORN_SAPLING = this.create("potted_pink_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(PINK_APRICORN_SAPLING))
@JvmField
val POTTED_BLACK_APRICORN_SAPLING = this.create("potted_black_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(BLACK_APRICORN_SAPLING))
@JvmField
val POTTED_WHITE_APRICORN_SAPLING = this.create("potted_white_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(WHITE_APRICORN_SAPLING))
@JvmField
val POTTED_SACCHARINE_SAPLING = this.create("potted_saccharine_sapling", BlocksInvoker.createFlowerPotBlock(SACCHARINE_SAPLING))
@JvmField
val HEARTY_GRAIN_BALE = this.create("hearty_grain_bale", BaleBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).instrument(NoteBlockInstrument.BANJO).strength(0.5F).sound(CobblemonSounds.HEARTY_GRAIN_BALE_SOUNDS)))
@JvmField
val TATAMI_BLOCK = this.create("tatami_block", HorizontalRotationalBlock(BlockBehaviour.Properties.of().ignitedByLava().mapColor(MapColor.PLANT).strength(0.1F).sound(CobblemonSounds.TATAMI_BLOCK_SOUNDS)))
@JvmField
val TATAMI_MAT = this.create("tatami_mat", HorizontalRotationCarpetBlock(BlockBehaviour.Properties.of().ignitedByLava().mapColor(MapColor.PLANT).strength(0.1F).sound(CobblemonSounds.TATAMI_MAT_SOUNDS)))
val HEARTY_GRAINS = this.create("hearty_grains", HeartyGrainsBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.PLANT).noCollission().randomTicks().instabreak().sound(CobblemonSounds.VIVICHOKE_SOUNDS)))
/**
* Returns a map of all the blocks that can be stripped with an axe in the format of input - output.
@@ -561,9 +525,11 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
APRICORN_LOG to STRIPPED_APRICORN_LOG,
SACCHARINE_WOOD to STRIPPED_SACCHARINE_WOOD,
SACCHARINE_LOG to STRIPPED_SACCHARINE_LOG,
SACCHARINE_LOG_SLATHERED to STRIPPED_SACCHARINE_LOG
SACCHARINE_HONEY_LOG to STRIPPED_SACCHARINE_LOG
)
private fun apricornBlock(name: String, apricorn: Apricorn): ApricornBlock = this.create(name, ApricornBlock(BlockBehaviour.Properties.of().mapColor(apricorn.mapColor()).randomTicks().strength(Blocks.OAK_LOG.defaultDestroyTime(), Blocks.OAK_LOG.explosionResistance).sound(SoundType.WOOD).noOcclusion(), apricorn))
private fun tumblestoneBlock(name: String, stage: Int, height: Int, xzOffset: Int, nextStage: Block?) : Block {
@@ -663,19 +629,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
Triple(APRICORN_FENCE, 5, 20),
Triple(APRICORN_FENCE_GATE, 5, 20),
Triple(APRICORN_SLAB, 5, 20),
Triple(APRICORN_STAIRS, 5, 20),
Triple(SACCHARINE_LOG, 5, 5),
Triple(SACCHARINE_LOG_SLATHERED, 5, 5),
Triple(STRIPPED_SACCHARINE_LOG, 5, 5),
Triple(SACCHARINE_WOOD, 5, 5),
Triple(STRIPPED_SACCHARINE_WOOD, 5, 5),
Triple(SACCHARINE_PLANKS, 5, 20),
Triple(SACCHARINE_LEAVES, 30, 60),
Triple(SACCHARINE_FENCE, 5, 20),
Triple(SACCHARINE_FENCE_GATE, 5, 20),
Triple(SACCHARINE_SLAB, 5, 20),
Triple(SACCHARINE_STAIRS, 5, 20)
Triple(APRICORN_STAIRS, 5, 20)
).onEach{ data -> setFlammable(data.first, data.second, data.third) }
}
@@ -1,122 +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.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.MoParams
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.events.Cancelable
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.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,
* 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>()
override val type = PackType.SERVER_DATA
override fun sync(player: ServerPlayer) {
player.sendPacket(CallbackRegistrySyncPacket(clientCallbacks.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>>()
override fun reload(manager: ResourceManager) {
clientCallbacks.clear()
callbacks.clear()
val unsortedCallbacks = mutableMapOf<ResourceLocation, MutableList<Pair<String, ExpressionLike>>>()
val folderBeforeNameRegex = ".*\\/([^\\/]+)\\/[^\\/]+\$".toRegex()
val predicate: Predicate<ResourceLocation> = Predicate { path -> path.endsWith(CobblemonScripts.MOLANG_EXTENSION) }
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 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)
}
}
}
}
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")
observable.emit(this)
}
fun run(
eventResourceLocation: ResourceLocation,
context: Map<String, MoValue>,
functions: Map<String, (MoParams) -> Any> = emptyMap(),
cancelable: Cancelable? = null
) {
if (eventResourceLocation !in callbacks) return
if (cancelable == null) {
runtime.environment.query.functions.remove("cancel")
} else {
runtime.environment.query.addFunction("cancel") { cancelable }
}
// Most places in the mod use query structs so it's probably nicer to present the things as query values.
context.forEach { (key, value) -> runtime.environment.query.addFunction(key) { return@addFunction value } }
functions.forEach { (name, function) -> runtime.environment.query.addFunction(name, function) }
callbacks[eventResourceLocation]?.forEach {
if (cancelable != null && cancelable.isCanceled) {
return
}
it.resolve(runtime, context)
}
}
}
@@ -59,7 +59,6 @@ object CobblemonCommands {
ApplyPlayerTextureCommand.register(dispatcher)
RenameBoxCommand.register(dispatcher)
ChangeBoxWallpaperCommand.register(dispatcher)
RunMolangCommand.register(dispatcher)
RunMolangScriptCommand.register(dispatcher)
SpectateBattleCommand.register(dispatcher)
CobblemonConfigCommand.register(dispatcher)
@@ -69,8 +68,6 @@ object CobblemonCommands {
MarkGiveCommand.register(dispatcher)
MarkTakeCommand.register(dispatcher)
MarkGiveAllCommand.register(dispatcher)
PcSearchCommand.register(dispatcher)
PcTakeCommand.register(dispatcher)
// Possibly lock down registration if and only if under dev environment or running in an environment
// with a certain system environment variable set
@@ -0,0 +1,114 @@
/*
* 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.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.MoParams
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.events.Cancelable
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.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 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 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 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(FlowRegistrySyncPacket(clientFlows.entries))
}
val runtime by lazy { MoLangRuntime().setup() } // Lazy for if someone adds to generalFunctions in MoLangFunctions
val clientFlows = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
val flows = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
override fun reload(manager: ResourceManager) {
clientFlows.clear()
flows.clear()
val unsortedFlows = mutableMapOf<ResourceLocation, MutableList<Pair<String, ExpressionLike>>>()
val folderBeforeNameRegex = ".*\\/([^\\/]+)\\/[^\\/]+\$".toRegex()
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()
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 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)
}
}
}
}
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)
}
fun run(
eventResourceLocation: ResourceLocation,
context: Map<String, MoValue>,
functions: Map<String, (MoParams) -> Any> = emptyMap(),
cancelable: Cancelable? = null
) {
if (cancelable == null) {
runtime.environment.query.functions.remove("cancel")
} else {
runtime.environment.query.addFunction("cancel") { cancelable }
}
// Most places in the mod use query structs so it's probably nicer to present the things as query values.
context.forEach { (key, value) -> runtime.environment.query.addFunction(key) { return@addFunction value } }
functions.forEach { (name, function) -> runtime.environment.query.addFunction(name, function) }
flows[eventResourceLocation]?.forEach {
if (cancelable != null && cancelable.isCanceled) {
return
}
it.resolve(runtime, context)
}
}
}
@@ -8,14 +8,18 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.item.components.*
import com.cobblemon.mod.common.block.PotComponent
import com.cobblemon.mod.common.item.components.BaitEffectsComponent
import com.cobblemon.mod.common.item.components.FlavourComponent
import com.cobblemon.mod.common.item.components.FoodColourComponent
import com.cobblemon.mod.common.item.components.HeldItemCapableComponent
import com.cobblemon.mod.common.item.components.PokemonItemComponent
import com.cobblemon.mod.common.item.components.RodBaitComponent
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.mojang.serialization.Codec
import net.minecraft.core.Registry
import net.minecraft.core.component.DataComponentType
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
import net.minecraft.network.codec.ByteBufCodecs
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
@@ -26,10 +30,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)
@@ -56,25 +60,6 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
.networkSynchronized(FoodColourComponent.PACKET_CODEC)
.build())
val INGREDIENT: DataComponentType<IngredientComponent> = create("ingredient", DataComponentType.builder<IngredientComponent>()
.persistent(IngredientComponent.CODEC)
.networkSynchronized(IngredientComponent.PACKET_CODEC)
.build())
val FOOD: DataComponentType<FoodComponent> = create("food", DataComponentType.builder<FoodComponent>()
.persistent(FoodComponent.CODEC)
.networkSynchronized(FoodComponent.PACKET_CODEC)
.build())
val MOB_EFFECTS: DataComponentType<MobEffectsComponent> = create("mob_effects", DataComponentType.builder<MobEffectsComponent>()
.persistent(MobEffectsComponent.CODEC)
.networkSynchronized(MobEffectsComponent.PACKET_CODEC)
.build())
val CRAFTED: DataComponentType<Boolean> = create("crafted", DataComponentType.builder<Boolean>()
.persistent(Codec.BOOL)
.networkSynchronized(ByteBufCodecs.BOOL)
.build())
fun register() {
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:pokemon_item"), POKEMON_ITEM)
@@ -83,10 +68,6 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:bait_effects"), BAIT_EFFECTS)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:flavour"), FLAVOUR)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food_colour"), FOOD_COLOUR)
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
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.api.item.ability.AbilityChanger
import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.text.blue
@@ -22,7 +21,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
@@ -36,13 +34,10 @@ import com.cobblemon.mod.common.item.berry.PPRestoringBerryItem
import com.cobblemon.mod.common.item.berry.PortionHealingBerryItem
import com.cobblemon.mod.common.item.berry.StatusCuringBerryItem
import com.cobblemon.mod.common.item.berry.VolatileCuringBerryItem
import com.cobblemon.mod.common.item.food.PonigiriItem
import com.cobblemon.mod.common.item.food.SinisterTeaItem
import com.cobblemon.mod.common.item.interactive.*
import com.cobblemon.mod.common.item.interactive.ability.AbilityChangeItem
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.IVs
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.core.Registry
@@ -58,8 +53,6 @@ import net.minecraft.world.food.FoodProperties
import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.HangingSignItem
import net.minecraft.world.item.Item
import net.minecraft.world.item.Item.Properties
import net.minecraft.world.item.ItemNameBlockItem
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.Rarity
@@ -193,35 +186,28 @@ 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_BLACK = campfirePotItem(CobblemonBlocks.BLACK_CAMPFIRE_POT, "black")
@JvmField
val CAMPFIRE_POT_YELLOW = campfirePotItem(CobblemonBlocks.YELLOW_CAMPFIRE_POT, CampfirePotColor.YELLOW)
val CAMPFIRE_POT_BLUE = campfirePotItem(CobblemonBlocks.BLUE_CAMPFIRE_POT, "blue")
@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_PINK = campfirePotItem(CobblemonBlocks.PINK_CAMPFIRE_POT, "pink")
@JvmField
val CAMPFIRE_POT_PINK = campfirePotItem(CobblemonBlocks.PINK_CAMPFIRE_POT, CampfirePotColor.PINK)
val CAMPFIRE_POT_RED = campfirePotItem(CobblemonBlocks.RED_CAMPFIRE_POT, "red")
@JvmField
val CAMPFIRE_POT_BLACK = campfirePotItem(CobblemonBlocks.BLACK_CAMPFIRE_POT, CampfirePotColor.BLACK)
val CAMPFIRE_POT_WHITE = campfirePotItem(CobblemonBlocks.WHITE_CAMPFIRE_POT, "white")
@JvmField
val CAMPFIRE_POT_WHITE = campfirePotItem(CobblemonBlocks.WHITE_CAMPFIRE_POT, CampfirePotColor.WHITE)
val CAMPFIRE_POT_YELLOW = campfirePotItem(CobblemonBlocks.YELLOW_CAMPFIRE_POT, "yellow")
@JvmField
val HEARTY_GRAINS = compostableItem("hearty_grains", ItemNameBlockItem(CobblemonBlocks.HEARTY_GRAINS, Properties().rarity(Rarity.COMMON)))
@JvmField
val HEARTY_GRAIN_BALE = compostableBlockItem("hearty_grain_bale", CobblemonBlocks.HEARTY_GRAIN_BALE, 0.85f)
val Hearty_Grains = compostableItem("hearty_grains", HeartyGrainsItem(CobblemonBlocks.HEARTY_GRAINS))
@JvmField
val TATAMI_BLOCK = blockItem("tatami_block", CobblemonBlocks.TATAMI_BLOCK)
@JvmField
val TATAMI_MAT = blockItem("tatami_mat", CobblemonBlocks.TATAMI_MAT)
val VIVICHOKE = compostableItem("vivichoke")
@JvmField
val VIVICHOKE = compostableItem("vivichoke", CobblemonItem(Item.Properties()), 0.80f)
@JvmField
val VIVICHOKE_SEEDS = compostableItem("vivichoke_seeds", VivichokeItem(CobblemonBlocks.VIVICHOKE_SEEDS), 0.30f)
val VIVICHOKE_SEEDS = compostableItem("vivichoke_seeds", VivichokeItem(CobblemonBlocks.VIVICHOKE_SEEDS))
@JvmField
val RED_APRICORN = apricornItem("red", ApricornItem(CobblemonBlocks.RED_APRICORN))
@@ -264,7 +250,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val APRICORN_PLANKS = blockItem("apricorn_planks", CobblemonBlocks.APRICORN_PLANKS)
@JvmField
val APRICORN_LEAVES = compostableBlockItem("apricorn_leaves", CobblemonBlocks.APRICORN_LEAVES, 0.30f)
val APRICORN_LEAVES = compostableBlockItem("apricorn_leaves", CobblemonBlocks.APRICORN_LEAVES)
@JvmField
val APRICORN_BOAT = create("apricorn_boat", CobblemonBoatItem(CobblemonBoatType.APRICORN, false, Item.Properties().stacksTo(1)))
@JvmField
@@ -311,7 +297,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val SACCHARINE_LOG = blockItem("saccharine_log", CobblemonBlocks.SACCHARINE_LOG)
@JvmField
val SACCHARINE_LOG_SLATHERED = blockItem("saccharine_log_slathered", CobblemonBlocks.SACCHARINE_LOG_SLATHERED)
val SACCHARINE_HONEY_LOG = blockItem("saccharine_honey_log", CobblemonBlocks.SACCHARINE_HONEY_LOG)
@JvmField
val STRIPPED_SACCHARINE_LOG = blockItem("stripped_saccharine_log", CobblemonBlocks.STRIPPED_SACCHARINE_LOG)
@JvmField
@@ -321,7 +307,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val SACCHARINE_PLANKS = blockItem("saccharine_planks", CobblemonBlocks.SACCHARINE_PLANKS)
@JvmField
val SACCHARINE_LEAVES = compostableBlockItem("saccharine_leaves", CobblemonBlocks.SACCHARINE_LEAVES, 0.35f)
val SACCHARINE_LEAVES = compostableBlockItem("saccharine_leaves", CobblemonBlocks.SACCHARINE_LEAVES)
@JvmField
val SACCHARINE_BOAT = create("saccharine_boat", CobblemonBoatItem(CobblemonBoatType.SACCHARINE, false, Item.Properties().stacksTo(1)))
@JvmField
@@ -348,99 +334,274 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val SACCHARINE_HANGING_SIGN = this.create("saccharine_hanging_sign", HangingSignItem(CobblemonBlocks.SACCHARINE_HANGING_SIGN, CobblemonBlocks.SACCHARINE_WALL_HANGING_SIGN, Item.Properties().stacksTo(16)))
@JvmField
val SACCHARINE_SAPLING = compostableBlockItem("saccharine_sapling", CobblemonBlocks.SACCHARINE_SAPLING, 0.30f)
val SACCHARINE_SAPLING = saccharineSaplingItem("saccharine_sapling", SaccharineSaplingItem(CobblemonBlocks.SACCHARINE_SAPLING))
// @JvmField
// val BUGWORT = compostableBlockItem("bugwort", CobblemonBlocks.BUGWORT) // TODO after 1.7
@JvmField
val BUGWORT = bugwortItem("bugwort", BugwortItem(CobblemonBlocks.BUGWORT))
@JvmField
val POKE_BAIT = noSettingsItem("poke_bait")
@JvmField
val POKE_CAKE = this.create("poke_cake", BlockItem(CobblemonBlocks.POKE_CAKE, Item.Properties().stacksTo(1)))
val LURE_CAKE = blockItem("lure_cake", CobblemonBlocks.LURE_CAKE)
@JvmField
val POKE_SNACK = this.create("poke_snack", BlockItem(CobblemonBlocks.POKE_SNACK, Item.Properties().stacksTo(16)))
val POKE_CAKE = blockItem("poke_cake", CobblemonBlocks.POKE_CAKE)
val aprijuices = mutableListOf<AprijuiceItem>()
@JvmField
val APRIJUICE_BLACK = aprijuiceItem(Apricorn.BLACK)
@JvmField
val APRIJUICE_RED = aprijuiceItem(Apricorn.RED)
@JvmField
val APRIJUICE_YELLOW = aprijuiceItem(Apricorn.YELLOW)
@JvmField
val APRIJUICE_GREEN = aprijuiceItem(Apricorn.GREEN)
@JvmField
val APRIJUICE_BLUE = aprijuiceItem(Apricorn.BLUE)
@JvmField
val APRIJUICE_PINK = aprijuiceItem(Apricorn.PINK)
val APRIJUICE_GREEN = aprijuiceItem(Apricorn.GREEN)
@JvmField
val APRIJUICE_BLACK = aprijuiceItem(Apricorn.BLACK)
val APRIJUICE_YELLOW = aprijuiceItem(Apricorn.YELLOW)
@JvmField
val APRIJUICE_WHITE = aprijuiceItem(Apricorn.WHITE)
@JvmField
val APRIJUICE_PINK = aprijuiceItem(Apricorn.PINK)
@JvmField
val PONIGIRI = create("ponigiri", PonigiriItem())
@JvmField
val SINISTER_TEA = create("sinister_tea", SinisterTeaItem())
@JvmField
val POKE_PUFF = pokepuffItem("poke_puff")
val POKE_PUFF = noSettingsItem("poke_puff") // todo make a PokePuffItem class for friendship boosting purposes
// FOODS
@JvmField
val SWEET_HEART = noSettingsItem("sweet_heart") // TODO make a SweetHeartItem class for breeding purposes
val SWEET_HEART = noSettingsItem("sweet_heart") // todo make a SweetHeartItem class for breeding purposes
@JvmField
val TASTY_TAIL = create("tasty_tail", foodItem(3, 0.3f))
val PEWTER_CRUNCHIES = create("pewter_crunchies", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})
@JvmField
val PEWTER_CRUNCHIES = regionalFoodItem("pewter_crunchies", 64, 7, 0.343f, false)
@JvmField
val RAGE_CANDY_BAR = regionalFoodItem("rage_candy_bar", 64, 8, 0.2f, false)
@JvmField
val LAVA_COOKIE = regionalFoodItem("lava_cookie", 64, 2, 0.3f, false)
@JvmField
val OLD_GATEAU = regionalFoodItem("old_gateau", 64, 9, 0.1335f, false)
@JvmField
val CASTELIACONE = regionalFoodItem("casteliacone", 64, 5, 0.28f, false)
@JvmField
val LUMIOSE_GALETTE = regionalFoodItem("lumiose_galette", 64, 4, 0.4f, false)
@JvmField
val BIG_MALASADA = regionalFoodItem("big_malasada", 64, 7, 0.343f, false)
@JvmField
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 64, 10, 0.6f, false, ItemStack(Items.BOWL, 1))
@JvmField
val JUBILIFE_MUFFIN = regionalFoodItem("jubilife_muffin", 64, 8, 0.2f, false)
@JvmField
val OPEN_FACED_SANDWICH = regionalFoodItem("open_faced_sandwich", 64, 13, 0.5f, false)
val RAGE_CANDY_BAR = create("rage_candy_bar", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})
// todo we might need to wait on these for later? These impact battles and may be harder to do
@JvmField
val LAVA_COOKIE = create("lava_cookie", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})
@JvmField
val OLD_GATEAU = create("old_gateau", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})
@JvmField
val CASTELIACONE = create("casteliacone", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})
@JvmField
val SHALOR_SABLE = create("shalor_sable", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})
@JvmField
val LUMIOSE_GALETTE = create("lumiose_galette", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})
@JvmField
val BIG_MALASADA = create("big_malasada", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})
/*@JvmField // todo see what we are doing for these (are there different kinds?)
val CURRY = create("curry", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.usingConvertsTo(Items.BOWL)
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})*/
@JvmField
val JUBILIFE_MUFFIN = create("jubilife_muffin", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})
/*@JvmField // todo see what we are doing for these (are there different kinds?)
val SANDWICH = create("sandwich", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.usingConvertsTo(Items.BOWL)
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})*/
// todo we might need to wait on these for later?
/*@JvmField
val CHOICE_DUMPLING = noSettingsItem("choice_dumpling") // todo make a ChoiceDumpingItem class for battle purposes
val CHOICE_DUMPLING = noSettingsItem("choice_dumpling") // todo make a ChoiceDumpingItem class for breeding purposes
@JvmField
val SWAP_SNACK = noSettingsItem("swap_snack") // todo make a SwapSnackItem class for battle purposes
val SWAP_SNACK = noSettingsItem("swap_snack") // todo make a SwapSnackItem class for breeding purposes
@JvmField
val TWICE_SPICED_BEETROOT = noSettingsItem("twice_spiced_beetroot") // todo make a TwiceSpiceBeetrootItem class for battle purposes
val TWICE_SPICED_BEETROOT = noSettingsItem("twice_spiced_beetroot") // todo make a TwiceSpiceBeetrootItem class for breeding purposes
*/
@JvmField
val POTATO_MOCHI = create("potato_mochi", CobblemonItem(Properties().stacksTo(64)
val POTATO_MOCHI = create("potato_mochi", object : Item(Properties().stacksTo(16)
.food(FoodProperties.Builder()
.nutrition(4)
.saturationModifier(0.4F)
.build())))
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})
@JvmField
val CANDIED_APPLE = create("candied_apple", CobblemonItem(Properties().stacksTo(64)
val FRIED_RICE = create("fried_rice", object : Item(Properties().stacksTo(16)
.food(FoodProperties.Builder()
.nutrition(6)
.saturationModifier(0.2335F)
.usingConvertsTo(Items.STICK)
.build())))
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})
@JvmField
val CANDIED_BERRY = create("candied_berry", CobblemonItem(Properties().stacksTo(64)
val CANDIED_APPLE = create("candied_apple", object : Item(Properties().stacksTo(16)
.food(FoodProperties.Builder()
.nutrition(5)
.saturationModifier(0.22F)
.usingConvertsTo(Items.STICK)
.build())))
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})
@JvmField
val CANDIED_BERRY = create("candied_berry", object : Item(Properties().stacksTo(16)
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})
@JvmField
val JELLY_DOUGHNUT = create("jelly_doughnut", object : Item(Properties().stacksTo(16)
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
user.removeAllEffects()
return super.finishUsingItem(stack, world, user)
}
})
// todo etc?
//@JvmField
//val SCATTER_BANG = this.create("scatter_bang", ScatterBangItem(Item.Settings()))
@@ -461,8 +622,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
@JvmField
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
// @JvmField
// val INCENSE_SWEET = blockItem("incense_sweet", CobblemonBlocks.INCENSE_SWEET) // TODO after 1.7
@JvmField
val INCENSE_SWEET = blockItem("incense_sweet", CobblemonBlocks.INCENSE_SWEET)
// Evolution items
@@ -492,9 +653,9 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField val CRACKED_POT = noSettingsItem("cracked_pot")
@JvmField val MASTERPIECE_TEACUP = noSettingsItem("masterpiece_teacup")
@JvmField val UNREMARKABLE_TEACUP = noSettingsItem("unremarkable_teacup")
@JvmField val SWEET_APPLE = compostableItem("sweet_apple", foodItem(4, 0.3f), 0.65f)
@JvmField val TART_APPLE = compostableItem("tart_apple", foodItem(4, 0.3f), 0.65f)
@JvmField val SYRUPY_APPLE = compostableItem("syrupy_apple", foodItem(4, 0.3f), 0.65f)
@JvmField val SWEET_APPLE = create("sweet_apple", foodItem(4, 0.3f))
@JvmField val TART_APPLE = create("tart_apple", foodItem(4, 0.3f))
@JvmField val SYRUPY_APPLE = create("syrupy_apple", foodItem(4, 0.3f))
@JvmField val GALARICA_CUFF = noSettingsItem("galarica_cuff")
@JvmField val GALARICA_WREATH = noSettingsItem("galarica_wreath")
@JvmField val BLACK_AUGURITE = noSettingsItem("black_augurite")
@@ -504,9 +665,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField val AUSPICIOUS_ARMOR = heldItem("auspicious_armor")
@JvmField val MALICIOUS_ARMOR = heldItem("malicious_armor")
@JvmField val SHELL_HELMET = heldItem("shell_helmet")
@JvmField val METAL_ALLOY = noSettingsItem("metal_alloy")
@JvmField val SCROLL_OF_DARKNESS = noSettingsItem("scroll_of_darkness")
@JvmField val SCROLL_OF_WATERS = noSettingsItem("scroll_of_waters")
private val berries = mutableMapOf<ResourceLocation, BerryItem>()
// Plants
@@ -583,34 +741,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField val BERRY_JUICE = this.create("berry_juice", BerryJuiceItem())
@JvmField
val GALARICA_NUTS = compostableItemNameBlockItem("galarica_nuts", CobblemonBlocks.GALARICA_NUT_BUSH)
// Hyper Training Items
val hyperTrainingItems = mutableListOf<HyperTrainingItem>()
@JvmField
val HEALTH_CANDY = hyperTrainingItem("health_candy", 1, setOf(Stats.HP), 0..IVs.MAX_VALUE)
@JvmField
val MIGHTY_CANDY = hyperTrainingItem("mighty_candy", 1, setOf(Stats.ATTACK), 0..IVs.MAX_VALUE)
@JvmField
val TOUGH_CANDY = hyperTrainingItem("tough_candy", 1, setOf(Stats.DEFENCE), 0..IVs.MAX_VALUE)
@JvmField
val SMART_CANDY = hyperTrainingItem("smart_candy", 1, setOf(Stats.SPECIAL_ATTACK), 0..IVs.MAX_VALUE)
@JvmField
val COURAGE_CANDY = hyperTrainingItem("courage_candy", 1, setOf(Stats.SPECIAL_DEFENCE), 0..IVs.MAX_VALUE)
@JvmField
val QUICK_CANDY = hyperTrainingItem("quick_candy", 1, setOf(Stats.SPEED), 0..IVs.MAX_VALUE)
@JvmField
val SICKLY_CANDY = hyperTrainingItem("sickly_candy", -1, setOf(Stats.HP), 0..IVs.MAX_VALUE)
@JvmField
val WEAK_CANDY = hyperTrainingItem("weak_candy", -1, setOf(Stats.ATTACK), 0..IVs.MAX_VALUE)
@JvmField
val BRITTLE_CANDY = hyperTrainingItem("brittle_candy", -1, setOf(Stats.DEFENCE), 0..IVs.MAX_VALUE)
@JvmField
val NUMB_CANDY = hyperTrainingItem("numb_candy", -1, setOf(Stats.SPECIAL_ATTACK), 0..IVs.MAX_VALUE)
@JvmField
val COWARD_CANDY = hyperTrainingItem("coward_candy", -1, setOf(Stats.SPECIAL_DEFENCE), 0..IVs.MAX_VALUE)
@JvmField
val SLOW_CANDY = hyperTrainingItem("slow_candy", -1, setOf(Stats.SPEED), 0..IVs.MAX_VALUE)
val GALARICA_NUTS = create("galarica_nuts", GalaricaNutItem())
// Medicine
@JvmField
@@ -666,12 +797,14 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val MEDICINAL_LEEK = heldItem("medicinal_leek", MedicinalLeekItem(CobblemonBlocks.MEDICINAL_LEEK, Item.Properties().food(FoodProperties.Builder().fast().nutrition(1).saturationModifier(0.2f).build())), "leek")
@JvmField
val ROASTED_LEEK = compostableItem("roasted_leek", CobblemonItem(Item.Properties().food(FoodProperties.Builder().fast().nutrition(3).saturationModifier(0.2f).build())), 0.85f)
val ROASTED_LEEK = create("roasted_leek", Item(Item.Properties().food(FoodProperties.Builder().fast().nutrition(3).saturationModifier(0.3f).build())))
@JvmField
val VIVICHOKE_DIP = create("vivichoke_dip", object : CobblemonItem(Properties().stacksTo(1)
val BRAISED_VIVICHOKE = create("braised_vivichoke", Item(Item.Properties().food(FoodProperties.Builder().nutrition(6).saturationModifier(0.6f).build())))
@JvmField
val VIVICHOKE_DIP = create("vivichoke_dip", object : Item(Properties().stacksTo(1)
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(0.6F)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.usingConvertsTo(Items.BOWL)
@@ -682,21 +815,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
}
})
@JvmField
val ENERGY_ROOT = compostableItem("energy_root", EnergyRootItem(CobblemonBlocks.ENERGY_ROOT, Properties()), 0.60f)
val ENERGY_ROOT = compostableItem("energy_root", EnergyRootItem(CobblemonBlocks.ENERGY_ROOT, Item.Properties().food(FoodProperties.Builder().nutrition(1).fast().saturationModifier(0.2f).build())))
@JvmField
val REVIVAL_HERB = compostableItem("revival_herb", RevivalHerbItem(CobblemonBlocks.REVIVAL_HERB))
@JvmField
val PEP_UP_FLOWER = compostableBlockItem("pep_up_flower", CobblemonBlocks.PEP_UP_FLOWER)
@JvmField
val MEDICINAL_BREW = noSettingsItem("medicinal_brew")
val MEDICINAL_BREW = create("medicinal_brew", Item(Item.Properties()))
@JvmField
val REMEDY = compostableItem("remedy", RemedyItem(RemedyItem.NORMAL), 0.65f)
val REMEDY = create("remedy", RemedyItem(RemedyItem.NORMAL))
@JvmField
val FINE_REMEDY = compostableItem("fine_remedy", RemedyItem(RemedyItem.FINE), 0.75f)
val FINE_REMEDY = create("fine_remedy", RemedyItem(RemedyItem.FINE))
@JvmField
val SUPERB_REMEDY = compostableItem("superb_remedy", RemedyItem(RemedyItem.SUPERB), 0.85f)
@JvmField
val MOOMOO_MILK = create("moomoo_milk", CobblemonItem(Item.Properties().craftRemainder(Items.GLASS_BOTTLE).stacksTo((16))))
val SUPERB_REMEDY = create("superb_remedy", RemedyItem(RemedyItem.SUPERB))
@JvmField
val POTION = create("potion", PotionItem(PotionType.POTION))
@@ -712,7 +843,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val HEAL_POWDER = create("heal_powder", HealPowderItem())
@JvmField
val LEEK_AND_POTATO_STEW = create("leek_and_potato_stew", CobblemonItem(Item.Properties().food(FoodProperties.Builder().nutrition(6).saturationModifier(0.6f).usingConvertsTo(Items.BOWL).build()).stacksTo(1)))
val LEEK_AND_POTATO_STEW = create("leek_and_potato_stew", Item(Item.Properties().food(FoodProperties.Builder().nutrition(8).saturationModifier(0.6f).usingConvertsTo(Items.BOWL).build()).stacksTo(1)))
@JvmField
val REVIVE = create("revive", ReviveItem(max = false))
@JvmField
@@ -914,7 +1045,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val ASSAULT_VEST = heldItem("assault_vest")
@JvmField
val BIG_ROOT = compostableBlockItem("big_root", CobblemonBlocks.BIG_ROOT, 0.40f)
val BIG_ROOT = blockItem("big_root", CobblemonBlocks.BIG_ROOT)
@JvmField
val BINDING_BAND = heldItem("binding_band")
@JvmField
@@ -931,8 +1062,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val CHOICE_SCARF = heldItem("choice_scarf")
@JvmField
val CLEANSE_TAG = heldItem("cleanse_tag")
@JvmField
val CLEAR_AMULET = heldItem("clear_amulet")
// @JvmField
// val CLEAR_AMULET = heldItem("clear_amulet")
@JvmField
val COVERT_CLOAK = heldItem("covert_cloak")
@JvmField
@@ -961,18 +1092,18 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val FOCUS_SASH = heldItem("focus_sash")
@JvmField
val GRASSY_SEED = heldItem("grassy_seed")
@JvmField
val GRIP_CLAW = heldItem("grip_claw")
// @JvmField
// val GRIP_CLAW = heldItem("grip_claw")
@JvmField
val HARD_STONE = heldItem("hard_stone")
@JvmField
val HEAVY_DUTY_BOOTS = heldItem("heavy_duty_boots")
@JvmField
val IRON_BALL = heldItem("iron_ball")
// @JvmField
// val LAGGING_TAIL = heldItem("lagging_tail")
@JvmField
val LAGGING_TAIL = heldItem("lagging_tail")
@JvmField
val LEFTOVERS = compostableHeldItem("leftovers", null, 0.50f)
val LEFTOVERS = heldItem("leftovers")
@JvmField
val LIFE_ORB = heldItem("life_orb")
@JvmField
@@ -983,8 +1114,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val LOADED_DICE = heldItem("loaded_dice")
@JvmField
val LUCKY_EGG = heldItem("lucky_egg")
@JvmField
val LUMINOUS_MOSS = heldItem("luminous_moss")
// @JvmField
// val LUMINOUS_MOSS = heldItem("luminous_moss")
@JvmField
val MAGNET = heldItem("magnet")
@JvmField
@@ -1459,8 +1590,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
private fun blockItem(name: String, block: Block, rarity: Rarity = Rarity.COMMON): BlockItem = this.create(name, BlockItem(block, Item.Properties().rarity(rarity)))
private fun itemNameBlockItem(name: String, block: Block, rarity: Rarity = Rarity.COMMON): ItemNameBlockItem = this.create(name, ItemNameBlockItem(block, Item.Properties().rarity(rarity)))
private fun noSettingsItem(name: String): CobblemonItem = this.create(name, CobblemonItem(Item.Properties()))
private fun itemWithRarity(name: String, rarity: Rarity): CobblemonItem = this.create(name, CobblemonItem(Item.Properties().rarity(rarity)))
@@ -1501,8 +1630,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
}
@@ -1513,38 +1642,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
return item
}
private fun pokepuffItem(name: String): PokePuffItem {
val item = create("poke_puff", PokePuffItem())
return item
}
private fun regionalFoodItem(
name: String,
stacksTo: Int,
nutrition: Int,
saturationModifier: Float,
alwaysEdible: Boolean = false,
convertsToOnUse: ItemStack? = null
): RegionalFoodItem {
val foodPropertiesBuilder = FoodProperties.Builder()
.nutrition(nutrition)
.saturationModifier(saturationModifier)
if (alwaysEdible == true) {
foodPropertiesBuilder.alwaysEdible()
}
if (convertsToOnUse != null && !convertsToOnUse.isEmpty) {
foodPropertiesBuilder.usingConvertsTo(convertsToOnUse.item)
}
val properties = Item.Properties()
.stacksTo(stacksTo)
.food(foodPropertiesBuilder.build())
return create(name, RegionalFoodItem(properties))
}
private fun heldItem(name: String, remappedName: String? = null): CobblemonItem = create(
name,
CobblemonItem(Item.Properties()).also {
@@ -1556,8 +1653,11 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
private fun heldItem(name: String, item: Item, remappedName: String? = null) = create(
name = name,
entry = item.also {
remappedName?.let { remappedName ->
if (remappedName != null) {
CobblemonHeldItemManager.registerRemap(it, remappedName)
CobblemonHeldItemManager.registerRemap(Items.BONE, "thickclub")
CobblemonHeldItemManager.registerRemap(Items.SNOWBALL, "snowball")
CobblemonHeldItemManager.registerRemap(Items.GOLD_BLOCK, "bignugget")
}
}
)
@@ -1583,13 +1683,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
private fun mintItem(name: String, mintItem: MintItem): MintItem {
val item = this.create(name, mintItem)
mints[item.nature.displayName] = item
compostable(item, .95f)
return item
}
private fun hyperTrainingItem(name: String, increaseAmount: Int, targetStats: Set<Stat>, validRange: IntRange): HyperTrainingItem {
val item = this.create(name, HyperTrainingItem(increaseAmount, targetStats, validRange))
hyperTrainingItems.add(item)
compostable(item, .65f)
return item
}
@@ -1603,7 +1697,21 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
private fun apricornSeedItem(name: String, apricornSeedItem: ApricornSeedItem): ApricornSeedItem {
val finalName = "${name}_apricorn_seed"
val item = this.create(finalName, apricornSeedItem)
compostable(item, .30f)
compostable(item, .65f)
return item
}
private fun saccharineSaplingItem(name: String, saccharineSaplingItem: SaccharineSaplingItem): SaccharineSaplingItem {
val finalName = "saccharine_sapling"
val item = this.create(finalName, saccharineSaplingItem)
compostable(item, .65f)
return item
}
private fun bugwortItem(name: String, bugwortItem: BugwortItem): BugwortItem {
val finalName = "bugwort"
val item = this.create(finalName, bugwortItem)
compostable(item, .65f)
return item
}
@@ -1611,19 +1719,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
private fun mintSeed(name: String, mintBlock: MintBlock): Item {
val finalName = "${name}_mint_seeds"
val item = this.blockItem(finalName, mintBlock)
compostable(item, .30f)
compostable(item, .65f)
return item
}
private fun mintLeaf(name: String, mintLeafItem: MintLeafItem): MintLeafItem {
val finalName = "${name}_mint_leaf"
val item = this.create(finalName, mintLeafItem)
compostable(item, .50f)
compostable(item, .65f)
return item
}
private fun compostableItem(name: String, item: Item? = null, increaseLevelChance: Float = .65f): Item {
val createdItem = this.create(name, item ?: CobblemonItem(Item.Properties()))
val createdItem = this.create(name, item ?: Item(Item.Properties()))
compostable(createdItem, increaseLevelChance)
return createdItem
}
@@ -1634,19 +1742,13 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
return createdItem
}
private fun compostableBlockItem(name: String, block: Block, increaseLevelChance: Float = .85f): Item {
private fun compostableBlockItem(name: String, block: Block, increaseLevelChance: Float = .65f): Item {
val createdItem = this.blockItem(name, block)
compostable(createdItem, increaseLevelChance)
return createdItem
}
private fun compostableItemNameBlockItem(name: String, block: Block, increaseLevelChance: Float = .30f): Item {
val createdItem = this.itemNameBlockItem(name, block)
compostable(createdItem, increaseLevelChance)
return createdItem
}
private fun foodItem(nutrition: Int, saturationModifier: Float): Item {
return CobblemonItem(Item.Properties().food(FoodProperties.Builder().nutrition(nutrition).saturationModifier(saturationModifier).build()))
return Item(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)
@@ -8,8 +8,6 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.ai.CobblemonAttackTargetData
import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
import com.cobblemon.mod.common.api.dialogue.ActiveDialogue
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.mojang.serialization.Codec
@@ -43,23 +41,7 @@ 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")
val HIVE_COOLDOWN = register<Boolean>("hive_cooldown")
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))
@@ -10,7 +10,9 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotScreen
import com.cobblemon.mod.common.platform.PlatformRegistry
import net.minecraft.client.gui.screens.MenuScreens
import net.minecraft.core.Registry
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
@@ -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,12 +20,10 @@ 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
import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
import com.cobblemon.mod.common.client.net.effect.SaccharineLogBlockParticlesHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
@@ -38,11 +36,9 @@ import com.cobblemon.mod.common.client.net.pasture.ClosePastureHandler
import com.cobblemon.mod.common.client.net.pasture.OpenPastureHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonUnpasturedHandler
import com.cobblemon.mod.common.client.net.pasture.UpdatePastureConflictFlagHandler
import com.cobblemon.mod.common.client.net.pokedex.ServerConfirmedRegisterHandler
import com.cobblemon.mod.common.client.net.pokemon.update.ClientboundUpdateRidingStateHandler
import com.cobblemon.mod.common.client.net.pokemon.update.PokemonUpdatePacketHandler
import com.cobblemon.mod.common.client.net.riding.ClientboundUpdateDriverInputHandler
import com.cobblemon.mod.common.client.net.settings.OpenCobblemonConfigEditorHandler
import com.cobblemon.mod.common.client.net.settings.ServerSettingsPacketHandler
import com.cobblemon.mod.common.client.net.sound.UnvalidatedPlaySoundS2CPacketHandler
@@ -65,8 +61,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
@@ -79,20 +75,16 @@ import com.cobblemon.mod.common.net.messages.client.cooking.ToggleCookingPotLidP
import com.cobblemon.mod.common.net.messages.client.data.*
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
import com.cobblemon.mod.common.net.messages.client.effect.PokeSnackBlockParticlesPacket
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.net.messages.client.effect.SaccharineLogBlockParticlesPacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import com.cobblemon.mod.common.net.messages.client.npc.CloseNPCEditorPacket
import com.cobblemon.mod.common.net.messages.client.npc.OpenNPCEditorPacket
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket
import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonPasturedPacket
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonUnpasturedPacket
import com.cobblemon.mod.common.net.messages.client.pasture.UpdatePastureConflictFlagPacket
import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedRegisterPacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.*
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.AddEvolutionPacket
@@ -148,7 +140,6 @@ import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamRequestPack
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamResponsePacket
import com.cobblemon.mod.common.net.messages.server.battle.RemoveSpectatorPacket
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
import com.cobblemon.mod.common.net.messages.server.behaviour.DamageOnCollisionPacket
import com.cobblemon.mod.common.net.messages.server.behaviour.SetEntityBehaviourPacket
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
@@ -157,7 +148,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
@@ -165,20 +155,17 @@ import com.cobblemon.mod.common.net.messages.server.dialogue.InputToDialoguePack
import com.cobblemon.mod.common.net.messages.server.npc.SaveNPCPacket
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket
import com.cobblemon.mod.common.net.messages.server.pasture.PasturePokemonPacket
import com.cobblemon.mod.common.net.messages.server.pasture.SetPastureConflictPacket
import com.cobblemon.mod.common.net.messages.server.pasture.UnpastureAllPokemonPacket
import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPacket
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
import com.cobblemon.mod.common.net.messages.server.starter.RequestStarterScreenPacket
import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
@@ -200,7 +187,6 @@ import com.cobblemon.mod.common.net.serverhandling.battle.SpectateBattleHandler
import com.cobblemon.mod.common.net.serverhandling.battle.TeamLeaveHandler
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestHandler
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestResponseHandler
import com.cobblemon.mod.common.net.serverhandling.behaviour.DamageOnCollisionPacketHandler
import com.cobblemon.mod.common.net.serverhandling.behaviour.SetEntityBehaviourHandler
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
@@ -209,7 +195,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
@@ -218,19 +203,16 @@ import com.cobblemon.mod.common.net.serverhandling.evolution.AcceptEvolutionHand
import com.cobblemon.mod.common.net.serverhandling.npc.SaveNPCHandler
import com.cobblemon.mod.common.net.serverhandling.orientation.OrientationPacketHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.PasturePokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.SetPastureConflictHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpastureAllPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandler
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.riding.DismountPokemonPacketHandler
import com.cobblemon.mod.common.net.serverhandling.riding.DriverInputPacketHandler
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.starter.RequestStarterScreenHandler
import com.cobblemon.mod.common.net.serverhandling.starter.SelectStarterPacketHandler
import com.cobblemon.mod.common.net.serverhandling.storage.BenchMoveHandler
@@ -277,7 +259,6 @@ object CobblemonNetwork {
// Pokemon Update Packets
list.add(PacketRegisterInfo(FriendshipUpdatePacket.ID, FriendshipUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(FullnessUpdatePacket.ID, FullnessUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(MoveSetUpdatePacket.ID, MoveSetUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(NatureUpdatePacket.ID, NatureUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(ShinyUpdatePacket.ID, ShinyUpdatePacket::decode, PokemonUpdatePacketHandler()))
@@ -312,7 +293,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 +371,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,18 +384,15 @@ 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))
list.add(PacketRegisterInfo(SpawnSnowstormEntityParticlePacket.ID, SpawnSnowstormEntityParticlePacket::decode, SpawnSnowstormEntityParticleHandler))
list.add(PacketRegisterInfo(RunPosableMoLangPacket.ID, RunPosableMoLangPacket::decode, RunPosableMoLangHandler))
list.add(PacketRegisterInfo(SaccharineLogBlockParticlesPacket.ID, SaccharineLogBlockParticlesPacket::decode, SaccharineLogBlockParticlesHandler))
list.add(PacketRegisterInfo(PokeSnackBlockParticlesPacket.ID, PokeSnackBlockParticlesPacket::decode, PokeSnackBlockParticlesHandler))
// Hax
list.add(PacketRegisterInfo(UnvalidatedPlaySoundS2CPacket.ID, UnvalidatedPlaySoundS2CPacket::decode, UnvalidatedPlaySoundS2CPacketHandler))
@@ -439,7 +417,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(ClosePasturePacket.ID, ClosePasturePacket::decode, ClosePastureHandler))
list.add(PacketRegisterInfo(PokemonPasturedPacket.ID, PokemonPasturedPacket::decode, PokemonPasturedHandler))
list.add(PacketRegisterInfo(PokemonUnpasturedPacket.ID, PokemonUnpasturedPacket::decode, PokemonUnpasturedHandler))
list.add(PacketRegisterInfo(UpdatePastureConflictFlagPacket.ID, UpdatePastureConflictFlagPacket::decode, UpdatePastureConflictFlagHandler))
// Orientation
list.add(PacketRegisterInfo(ClientboundUpdateOrientationPacket.ID, ClientboundUpdateOrientationPacket::decode, S2CUpdateOrientationHandler))
@@ -475,7 +452,6 @@ object CobblemonNetwork {
// Riding
list.add(PacketRegisterInfo(ClientboundUpdateRidingStatePacket.ID, ClientboundUpdateRidingStatePacket::decode, ClientboundUpdateRidingStateHandler))
list.add(PacketRegisterInfo(ClientboundUpdateDriverInputPacket.ID, ClientboundUpdateDriverInputPacket::decode, ClientboundUpdateDriverInputHandler))
return list
}
@@ -551,8 +527,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(PasturePokemonPacket.ID, PasturePokemonPacket::decode, PasturePokemonHandler))
list.add(PacketRegisterInfo(UnpasturePokemonPacket.ID, UnpasturePokemonPacket::decode, UnpasturePokemonHandler))
list.add(PacketRegisterInfo(UnpastureAllPokemonPacket.ID, UnpastureAllPokemonPacket::decode, UnpastureAllPokemonHandler))
list.add(PacketRegisterInfo(SetPastureConflictPacket.ID, SetPastureConflictPacket::decode, SetPastureConflictHandler))
// Block entity
list.add(PacketRegisterInfo(AdjustBlockEntityViewerCountPacket.ID, AdjustBlockEntityViewerCountPacket::decode, AdjustBlockEntityViewerCountHandler))
@@ -581,16 +555,12 @@ 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))
// Cooking
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
// Behaviour Packets
list.add(PacketRegisterInfo(SetEntityBehaviourPacket.ID, SetEntityBehaviourPacket::decode, SetEntityBehaviourHandler))
list.add(PacketRegisterInfo(DamageOnCollisionPacket.ID, DamageOnCollisionPacket::decode, DamageOnCollisionPacketHandler))
return list
}
@@ -24,19 +24,23 @@ object CobblemonPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Regist
override val resourceKey: ResourceKey<Registry<PoiType>> = Registries.POINT_OF_INTEREST_TYPE
@JvmField
val NURSE_KEY = createKey("nurse", CobblemonBlocks.HEALING_MACHINE, 1, 1)
val NURSE_KEY: ResourceKey<PoiType> = createKey("nurse")
@JvmField
val POKE_SNACK_KEY = createKey("poke_snack", CobblemonBlocks.POKE_SNACK, 0, 1)
val NURSE = create(NURSE_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.HEALING_MACHINE), 1, 1))
@JvmField
val SACCHARINE_LOG_SLATHERED_KEY = createKey("saccharine_log_slathered", CobblemonBlocks.SACCHARINE_LOG_SLATHERED, 0, 1)
// TODO after 1.7
// @JvmField
// val INCENSE_SWEET_KEY = createKey("incense_sweet", CobblemonBlocks.INCENSE_SWEET, 0, 1)
val LURE_CAKE_KEY: ResourceKey<PoiType> = createKey("lure_cake")
@JvmField
val LURE_CAKE = create(LURE_CAKE_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.LURE_CAKE), 0, 1))
@JvmField
val SACCHARINE_HONEY_LOG_KEY: ResourceKey<PoiType> = createKey("saccharine_honey_log")
@JvmField
val SACCHARINE_HONEY_LOG = create(SACCHARINE_HONEY_LOG_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.SACCHARINE_HONEY_LOG), 0, 1))
@JvmField
val INCENSE_SWEET_KEY: ResourceKey<PoiType> = createKey("saccharine_honey_log")
@JvmField
val INCENSE_SWEET = create(INCENSE_SWEET_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.INCENSE_SWEET), 0, 1))
private fun createKey(string: String, block: Block, maxTickets: Int, validRange: Int ): ResourceKey<PoiType> =
ResourceKey.create(Registries.POINT_OF_INTEREST_TYPE, cobblemonResource(string)).also { resourceKey ->
create(resourceKey.location().path, PoiType(getBlockStates(block), maxTickets, validRange))
}
private fun createKey(string: String): ResourceKey<PoiType> = ResourceKey.create(Registries.POINT_OF_INTEREST_TYPE, cobblemonResource(string))
private fun getBlockStates(block: Block): Set<BlockState> = ImmutableSet.copyOf(block.stateDefinition.possibleStates)
}
}
@@ -15,13 +15,13 @@ enum class CobblemonRecipeCategories {
COOKING_POT_SEARCH("COOKING_POT_SEARCH"),
COOKING_POT_FOODS("COOKING_POT_FOODS"),
COOKING_POT_MEDICINES("COOKING_POT_MEDICINES"),
COOKING_POT_COMPLEX_DISHES("COOKING_POT_COMPLEX_DISHES"),
COOKING_POT_BAITS("COOKING_POT_BAITS"),
COOKING_POT_MISC("COOKING_POT_MISC");
companion object {
val customAggregateCategories: Map<RecipeBookCategories, List<RecipeBookCategories>> = mapOf(
COOKING_POT_SEARCH.toVanillaCategory() to listOf(
COOKING_POT_FOODS.toVanillaCategory(), COOKING_POT_MISC.toVanillaCategory(), COOKING_POT_MEDICINES.toVanillaCategory(), COOKING_POT_COMPLEX_DISHES.toVanillaCategory()
COOKING_POT_FOODS.toVanillaCategory(), COOKING_POT_MISC.toVanillaCategory(), COOKING_POT_MEDICINES.toVanillaCategory(), COOKING_POT_BAITS.toVanillaCategory()
)
)
}
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.item.crafting.CookingPotRecipe
import com.cobblemon.mod.common.item.crafting.CookingPotShapelessRecipe
import com.cobblemon.mod.common.item.crafting.brewingstand.BrewingStandRecipe
import com.cobblemon.mod.common.platform.PlatformRegistry
import net.minecraft.core.Registry
import net.minecraft.core.registries.BuiltInRegistries
@@ -25,8 +24,6 @@ object CobblemonRecipeSerializers : PlatformRegistry<Registry<RecipeSerializer<*
val COOKING_POT_COOKING: RecipeSerializer<*> = register("cobblemon:cooking_pot", CookingPotRecipe.Serializer())
val COOKING_POT_SHAPELESS: RecipeSerializer<*> = register("cobblemon:cooking_pot_shapeless", CookingPotShapelessRecipe.Serializer())
val BREWING_STAND: RecipeSerializer<*> = register("cobblemon:brewing_stand", BrewingStandRecipe.Serializer())
override val registry: Registry<RecipeSerializer<*>>
get() = BuiltInRegistries.RECIPE_SERIALIZER
override val resourceKey: ResourceKey<Registry<RecipeSerializer<*>>>
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.item.crafting.CookingPotRecipe
import com.cobblemon.mod.common.item.crafting.CookingPotShapelessRecipe
import com.cobblemon.mod.common.item.crafting.brewingstand.BrewingStandRecipe
import com.cobblemon.mod.common.platform.PlatformRegistry
import net.minecraft.core.Registry
import net.minecraft.core.registries.BuiltInRegistries
@@ -25,8 +24,6 @@ object CobblemonRecipeTypes : PlatformRegistry<Registry<RecipeType<*>>, Resource
val COOKING_POT_COOKING: RecipeType<CookingPotRecipe> = register("cobblemon:cooking_pot")
val COOKING_POT_SHAPELESS: RecipeType<CookingPotShapelessRecipe> = register("cobblemon:cooking_pot_shapeless")
val BREWING_STAND: RecipeType<BrewingStandRecipe> = register("cobblemon:brewing_stand")
override val registry: Registry<RecipeType<*>>
get() = BuiltInRegistries.RECIPE_TYPE
override val resourceKey: ResourceKey<Registry<RecipeType<*>>>
@@ -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)
}
}
}
@@ -8,7 +8,10 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.*
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.DefendOwnerSensor
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.DrowsySensor
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonAdultSensor
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonGrowableCropSensor
import com.cobblemon.mod.common.pokemon.ai.PokemonDisturbancesSensor
import com.cobblemon.mod.common.entity.sensor.BattlingPokemonSensor
import com.cobblemon.mod.common.entity.sensor.NPCBattlingSensor
@@ -33,14 +36,6 @@ object CobblemonSensors {
val NEARBY_GROWABLE_CROPS = register("nearby_growable_crops", ::PokemonGrowableCropSensor)
val NEARBY_BEE_HIVE = register("nearby_bee_hive", ::BeeHiveSensor)
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)
fun <E : Entity, U : Sensor<E>> register(id: String, supplier: Supplier<U>): SensorType<U> {
val sensor = SensorType(supplier)
sensors[id] = sensor
@@ -125,7 +125,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val MOCHI_USE = this.create("item.medicine.feather.use")
@JvmField
val MULCH_PLACE = this.create("block.mulch.place")
@JvmField
@@ -204,19 +203,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val EVOLUTION_STONE_BLOCK_STEP = this.create("block.evolution_stone_block.step")
@JvmField
val TATAMI_BLOCK_BREAK = this.create("block.tatami.break")
@JvmField
val TATAMI_BLOCK_HIT = this.create("block.tatami.hit")
@JvmField
val TATAMI_BLOCK_PLACE = this.create("block.tatami.place")
@JvmField
val TATAMI_BLOCK_STEP = this.create("block.tatami.step")
@JvmField
val TATAMI_MAT_BREAK = this.create("block.tatami_mat.break")
@JvmField
val TATAMI_MAT_PLACE = this.create("block.tatami_mat.place")
@JvmField
val GIMMIGHOUL_GIVE_ITEM = this.create("pokemon.gimmighoul.give_item")
@JvmField
@@ -230,8 +216,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
val BERRY_HARVEST = this.create("block.berry_bush.harvest")
@JvmField
val BERRY_EAT = this.create("item.berry.eat")
@JvmField
val BERRY_EAT_FULL = this.create("item.berry.eat.full")
@JvmField
val BIG_ROOT_BREAK = this.create("block.big_root.break")
@@ -243,24 +227,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val VIVICHOKE_PLACE = this.create("block.vivichoke.place")
@JvmField
val HEARTY_GRAIN_BALE_BREAK = this.create("block.hearty_grain_bale.break")
@JvmField
val HEARTY_GRAIN_BALE_PLACE = this.create("block.hearty_grain_bale.place")
@JvmField
val HEARTY_GRAIN_BALE_HIT = this.create("block.hearty_grain_bale.hit")
@JvmField
val HEARTY_GRAIN_BALE_STEP = this.create("block.hearty_grain_bale.step")
@JvmField
val HEARTY_GRAINS_BREAK = this.create("block.hearty_grains.break")
@JvmField
val HEARTY_GRAINS_PLACE = this.create("block.hearty_grains.place")
@JvmField
val HEARTY_GRAINS_BREAK_WATER = this.create("block.hearty_grains.break_water")
@JvmField
val HEARTY_GRAINS_PLACE_WATER = this.create("block.hearty_grains.place_water")
@JvmField
val MINT_BREAK = this.create("block.mint.break")
@JvmField
@@ -306,11 +272,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
RELIC_COIN_SACK_STEP
)
@JvmField
val RIDE_LOOP_LEATHER = create("ride.loop.leather")
@JvmField
val RIDE_LOOP_PLUMAGE = create("ride.loop.plumage")
@JvmField
val MOVE_QUICKATTACK_TARGET = this.create("move.quickattack.target")
@JvmField
@@ -355,8 +316,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val IMPACT_WATER = this.create("impact.water")
// Note to self or whoever's peeking, block sounds have to be at 1.1f pitch
// For some reason, the game pitches them down and compensating for it here returns them to normal
@JvmField
val TUMBLESTONE_SOUNDS = SoundType(1f, 1.1f,
TUMBLESTONE_BREAK,
@@ -374,7 +333,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
TUMBLESTONE_HIT,
TUMBLESTONE_STEP
)
@JvmField
val EVOLUTION_STONE_BLOCK_SOUNDS = SoundType(1f, 1.1f,
EVOLUTION_STONE_BLOCK_BREAK,
@@ -384,23 +342,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
EVOLUTION_STONE_BLOCK_STEP
)
@JvmField
val TATAMI_BLOCK_SOUNDS = SoundType(1f, 1.1f,
TATAMI_BLOCK_BREAK,
TATAMI_BLOCK_STEP,
TATAMI_BLOCK_PLACE,
TATAMI_BLOCK_HIT,
TATAMI_BLOCK_STEP
)
@JvmField
val TATAMI_MAT_SOUNDS = SoundType(1f, 1.1f,
TATAMI_MAT_BREAK,
TATAMI_BLOCK_STEP,
TATAMI_MAT_PLACE,
TATAMI_BLOCK_HIT,
TATAMI_BLOCK_STEP
)
@JvmField
val BERRY_BUSH_SOUNDS = SoundType(0.8f, 1.1f,
@@ -447,33 +388,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
SoundEvents.GRASS_FALL
)
@JvmField
val HEARTY_GRAIN_BALE_SOUNDS = SoundType(0.6f, 1.1f,
HEARTY_GRAIN_BALE_BREAK,
HEARTY_GRAIN_BALE_STEP,
HEARTY_GRAIN_BALE_PLACE,
HEARTY_GRAIN_BALE_HIT,
HEARTY_GRAIN_BALE_STEP
)
@JvmField
val HEARTY_GRAINS_SOUNDS = SoundType(0.6f, 1.1f,
HEARTY_GRAINS_BREAK,
HEARTY_GRAIN_BALE_STEP,
HEARTY_GRAINS_PLACE,
HEARTY_GRAIN_BALE_HIT,
HEARTY_GRAIN_BALE_STEP
)
@JvmField
val HEARTY_GRAINS_WATER_SOUNDS = SoundType(0.6f, 1.1f,
HEARTY_GRAINS_BREAK_WATER,
SoundEvents.GRASS_STEP,
HEARTY_GRAINS_PLACE_WATER,
SoundEvents.GRASS_HIT,
SoundEvents.GRASS_FALL
)
@JvmField
val MINT_SOUNDS = SoundType(0.6f, 1.1f,
MINT_BREAK,
@@ -513,6 +427,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
val DISPLAY_CASE_PLACE = this.create("block.display_case.place")
@JvmField
val DISPLAY_CASE_STEP = this.create("block.display_case.step")
@JvmField
val DISPLAY_CASE_SOUNDS = SoundType(1f, 1f,
DISPLAY_CASE_BREAK,
@@ -523,7 +438,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
)
@JvmField
val CAMPFIRE_POT_SET = this.create("block.campfire_pot.set")
val CAMPFIRE_POT_PLACE_CAMPFIRE = this.create("block.campfire_pot.place_campfire")
@JvmField
val CAMPFIRE_POT_RETRIEVE = this.create("block.campfire_pot.retrieve")
@JvmField
@@ -531,14 +446,12 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val CAMPFIRE_POT_CLOSE = this.create("block.campfire_pot.close")
@JvmField
val CAMPFIRE_POT_TAKE_ITEM = this.create("block.campfire_pot.take_item")
@JvmField
val CAMPFIRE_POT_ACTIVE = this.create("block.campfire_pot.active")
@JvmField
val CAMPFIRE_POT_AMBIENT = this.create("block.campfire_pot.ambient")
val CAMPFIRE_POT_USE = this.create("block.campfire_pot.use")
@JvmField
val CAMPFIRE_POT_COOK = this.create("block.campfire_pot.cook")
@JvmField
val CAMPFIRE_POT_CRAFT = this.create("block.campfire_pot.craft")
@JvmField
val CAMPFIRE_POT_PLACE = this.create("block.campfire_pot.place")
@JvmField
val CAMPFIRE_POT_BREAK = this.create("block.campfire_pot.break")
@@ -32,7 +32,6 @@ class PokemonCriterion(
}
override fun matches(player: ServerPlayer, context: Pokemon): Boolean {
var speciesCheck = (species.equals(context.species.resourceIdentifier.path) || species.equals(context.species.resourceIdentifier.toString())) || species.equals("any")
return speciesCheck && properties.matches(context)
return (species.equals(context.species.resourceIdentifier.path) || species.equals(context.species.resourceIdentifier)) && properties.matches(context)
}
}
@@ -15,12 +15,9 @@ import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbilityType
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
import java.io.File
import kotlin.collections.set
/**
* Registry for all known Abilities
@@ -34,31 +31,15 @@ object Abilities : DataRegistry {
val DUMMY = AbilityTemplate(name = "dummy")
private val abilityMap = mutableMapOf<String, AbilityTemplate>()
internal val abilityScripts = mutableMapOf<String, String>() // abilityId to JavaScript
override fun reload(manager: ResourceManager) {
PotentialAbility.types.clear()
PotentialAbility.types.add(CommonAbilityType)
PotentialAbility.types.add(HiddenAbilityType)
this.abilityMap.clear()
this.abilityScripts.clear()
ShowdownService.service.resetRegistryData("ability")
manager.listResources("abilities") { it.path.endsWith(".js") }.forEach { (identifier, resource) ->
resource.open().use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
val js = reader.readText()
abilityScripts[resolvedIdentifier.path] = js
}
}
}
ShowdownService.service.sendRegistryData(abilityScripts, "ability")
val abilitiesJson = ShowdownService.service.getRegistryData("ability")
val abilitiesJson = ShowdownService.service.getAbilityIds()
for (i in 0 until abilitiesJson.size()) {
val jsonAbility = abilitiesJson[i].asJsonObject
val id = jsonAbility.get("id").asString
val id = abilitiesJson[i].asString
val ability = AbilityTemplate(id)
this.register(ability)
}
@@ -78,7 +59,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" }
}
}
@@ -8,132 +8,41 @@
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
var coreActivities = setOf(Activity.CORE)
val activities = mutableListOf<ActivityConfigurationContext>()
val schedule = Schedule.EMPTY
val memories = mutableSetOf<MemoryModuleType<*>>()
val sensors = mutableSetOf<SensorType<*>>()
val onAdd = mutableListOf<ExpressionLike>()
fun getOrCreateActivity(activity: Activity): ActivityConfigurationContext {
return activities.firstOrNull { it.activity == activity } ?: ActivityConfigurationContext(activity).also(activities::add)
}
fun addMemories(vararg memory: MemoryModuleType<*>) {
memories.addAll(memory)
}
fun addMemories(memories: Collection<MemoryModuleType<*>>) {
this.memories.addAll(memories)
}
fun addSensors(vararg sensor: SensorType<*>) {
sensors.addAll(sensor)
}
fun addSensors(sensors: Collection<SensorType<*>>) {
this.sensors.addAll(sensors)
}
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))
fun apply(entity: LivingEntity, behaviourConfigs: List<BehaviourConfig>) {
val brain = entity.brain
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)
onAdd.forEach { runtime.resolve(it) }
}
activities.forEach { it.apply(entity) }
brain.setCoreActivities(coreActivities)
brain.setDefaultActivity(defaultActivity)
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
}
}
@@ -1,71 +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 com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
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.util.asExpression
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.resolve
import net.minecraft.world.entity.Entity
/**
* This is used in the AI system to store optional additional information about the attack target,
* mainly things like when to give up pursuit, what sort of attacks are permitted, whatever. It's
* a temporary memory, same as ATTACK_TARGET, so relogs will clear it and that's why it has no codec.
*
* To help you get a sense of a use case, imagine a Pidgeotto has an AI task that says it should hunt
* Magikarp that are close to the surface. Once they've found one and chosen them as the attack target,
* they should give up the target if the Magikarp goes too deep, but if it's attacking an entity because
* they attacked its owner, it shouldn't give up on the target for that reason. To address this, when
* hunting Magikarp, the Pidgeotto will set this memory to a CobblemonAttackTargetData with some
* [Expression] that encourages the entity to give up the target if the Magikarp goes too deep.
*
* To make this work, any transition from the FIGHT activity will erase this memory.
*
* @author Hiroku
* @since June 23rd, 2025
*/
class CobblemonAttackTargetData(
/** An expression that returns false when the target is no longer worth attacking. */
var shouldContinue: Expression = "true".asExpression(),
var hitsUntilDisengage: Int = -1, // -1 means no limit
var disengageAction: (entity: Entity) -> Unit = {}
) {
val struct = ObjectValue<CobblemonAttackTargetData>(this).also {
it.addFunction("hits_until_disengage") { DoubleValue(hitsUntilDisengage.toDouble()) }
it.addFunction("set_hits_until_disengage") { params ->
val value = params.getDouble(0)
hitsUntilDisengage = value.toInt()
DoubleValue(hitsUntilDisengage.toDouble())
}
it.addFunction("set_disengage_action") { params ->
val script = params.getString(0).asIdentifierDefaultingNamespace()
val function = CobblemonScripts.scripts[script] ?: run {
Cobblemon.LOGGER.warn("set_disengage_action was given script '$script' which does not exist.")
return@addFunction DoubleValue.ZERO
}
disengageAction = { entity ->
val runtime = MoLangRuntime().setup()
runtime.environment.query.addFunction("entity") { entity.asMostSpecificMoLangValue() }
runtime.resolve(function)
}
DoubleValue.ONE
}
it.addFunction("unset_disengage_action") { params ->
disengageAction = {}
}
}
}
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.resolve
import com.cobblemon.mod.common.util.withQueryValue
import com.google.gson.annotations.SerializedName
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.LivingEntity
@@ -29,26 +28,23 @@ class CobblemonBehaviour(
val visible: Boolean = true,
val entityType: ResourceLocation? = null,
val configurations: List<BehaviourConfig> = mutableListOf(),
// I feel like the onAdd+onAddScript etc could be merged using some interface and a clever deserializer,
// 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,
val onAdd: ExpressionLike? = null,
val undo: ExpressionLike? = null,
val undoScript: 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)
}
configurations.forEach { it.configure(entity, behaviourConfigurationContext) }
}
/** Undoes anything that needs undoing once this configuration is being removed from an entity that had it before. */
fun onRemove(entity: LivingEntity) {
fun undo(entity: LivingEntity) {
val runtime = MoLangRuntime().setup()
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (onRemove != null) {
runtime.resolve(onRemove)
if (undoScript != null) {
CobblemonScripts.run(undoScript, runtime)
}
if (undo != null) {
runtime.resolve(undo)
}
}
}
@@ -1,29 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.phys.Vec3
/**
* Wraps around [BlockPosTracker] to add additional flags for Cobblemon-specific behavior.
*
* @author Hiroku
* @since August 4th, 2025
*/
class CobblemonBlockPosTracker(
pos: Vec3,
val flags: Set<String> = emptySet(),
) : BlockPosTracker(pos) {
companion object {
const val BATTLE_LOOK_BYPASS_FLAG = "battle_look_bypass"
}
fun isBattleLookBypass() = flags.contains(BATTLE_LOOK_BYPASS_FLAG)
}
@@ -1,91 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.util.getBooleanOrNull
import com.cobblemon.mod.common.util.getDoubleOrNull
import kotlin.math.sqrt
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.PathfinderMob
/**
* Used to maintain some settings around how the entity should wander this tick. The idea is you
* reset this with some script task at priority 0 then some number of steps before it gets to the wander
* tasks you can have other conditional tasks jump in to configure parts of this.
*
* @author Hiroku
* @since July 18, 2025
*/
class CobblemonWanderControl(
var center: WanderCenter? = null,
var maxAttempts: Int = 4, // How many times to try to find a suitable point before giving up on that tick.
var allowLand: Boolean = true,
var allowWater: Boolean = false,
var allowAir: Boolean = false,
var pathCooldownTicks: Int = 20 * 5, // Used to mitigate the cost of pathfinding.
var wanderSpeed: Float = 0.35F,
) {
class WanderCenter(val x: Number, val y: Number, val z: Number, val minRange: Float, val maxRange: Float)
@Transient
val struct: QueryStruct = QueryStruct(hashMapOf())
.addFunction("center_x") { center?.x ?: 0.0 }
.addFunction("center_y") { center?.y ?: 0.0 }
.addFunction("center_z") { center?.z ?: 0.0 }
.addFunction("max_attempts") { maxAttempts.toDouble() }
.addFunction("min_range") { center?.minRange ?: 0.0 }
.addFunction("max_range") { center?.maxRange ?: 0.0 }
.addFunction("allow_land") { if (allowLand) 1.0 else 0.0 }
.addFunction("allow_water") { if (allowWater) 1.0 else 0.0 }
.addFunction("allow_air") { if (allowAir) 1.0 else 0.0 }
.addFunction("path_cooldown_ticks") { pathCooldownTicks.toDouble() }
.addFunction("wander_speed") { wanderSpeed }
.addFunction("set_center") { params ->
if (params.params.size < 3) {
throw IllegalArgumentException("set_center requires at least 3 parameters: x, y, z")
}
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
val minRange = params.getDoubleOrNull(3) ?: 0.0
val maxRange = params.getDoubleOrNull(4) ?: Float.MAX_VALUE.toDouble()
center = WanderCenter(
x = x,
y = y,
z = z,
minRange = minRange.toFloat(),
maxRange = maxRange.toFloat(),
)
}
.addFunction("set_max_attempts") { params -> maxAttempts = params.getDoubleOrNull(0)?.toInt() ?: 4 }
.addFunction("set_allow_land") { params -> allowLand = params.getBooleanOrNull(0) ?: true }
.addFunction("set_allow_water") { params -> allowWater = params.getBooleanOrNull(0) ?: false }
.addFunction("set_allow_air") { params -> allowAir = params.getBooleanOrNull(0) ?: false }
.addFunction("set_path_cooldown_ticks") { params -> pathCooldownTicks = params.getDoubleOrNull(0)?.toInt() ?: (20 * 5) }
.addFunction("set_wander_speed") { params -> wanderSpeed = params.getDoubleOrNull(0)?.toFloat() ?: 0.35F }
.addFunction("reset") {
center = null
allowLand = false
allowWater = false
allowAir = false
pathCooldownTicks = 20 * 5
wanderSpeed = 0.35F
maxAttempts = 4
}
fun isSuitable(position: BlockPos): Boolean {
val center = center ?: return true
val centerPos = BlockPos(center.x.toInt(), center.y.toInt(), center.z.toInt())
val distance = sqrt(centerPos.distSqr(position))
val suitable = distance >= center.minRange && distance <= center.maxRange
return suitable
}
}
@@ -13,29 +13,8 @@ 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>
/**
* In the case of [ExpressionOrEntityVariable] the right side is the only one that requires visibility for a user,
* this function is a shorthand for finding all of the values in the list that have something to show the user to
* potentially edit.
*/
fun List<ExpressionOrEntityVariable>.asVariables() = mapNotNull { it.right().getOrNull() }
@@ -12,20 +12,7 @@ import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
/**
* A wrapper for [BehaviorControl] that allows it to be used with any [LivingEntity]. If the class the task was
* actually intended for is not the same as the entity being passed in, the task will just not run. This is just
* a trick to allow type safety.
*
* @author Hiroku
* @since October 19th, 2024
*/
class WrapperLivingEntityTask<T : LivingEntity>(val task: BehaviorControl<T>, val clazz: Class<T>) : BehaviorControl<LivingEntity> {
companion object {
inline fun <reified E : LivingEntity> BehaviorControl<E>.wrapped(): BehaviorControl<LivingEntity> {
return WrapperLivingEntityTask(this, E::class.java)
}
}
override fun tryStart(level: ServerLevel, entity: LivingEntity, gameTime: Long): Boolean {
if (clazz.isInstance(entity)) {
return task.tryStart(level, entity as T, gameTime)
@@ -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
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
import net.minecraft.world.entity.ai.sensing.SensorType
/**
* Does nothing except conditionally add some memories and sensors to the brain of the entity.
*
* @author Hiroku
* @since June 27th, 2025
*/
class AddMemoriesAndSensorsConfig : BehaviourConfig {
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
val memories: Set<MemoryModuleType<*>> = emptySet()
val sensors: Set<SensorType<*>> = mutableSetOf()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(condition).asVariables()
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (!checkCondition(behaviourConfigurationContext, condition)) return
behaviourConfigurationContext.addMemories(memories)
behaviourConfigurationContext.addSensors(sensors)
}
}
@@ -23,14 +23,10 @@ class AddTasksToActivity : BehaviourConfig {
val activities = mutableListOf<Activity>()
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
val tasksByPriority = mutableMapOf<Int, List<TaskConfig>>()
override fun getVariables(entity: LivingEntity) = tasksByPriority.values.flatten().flatMap { it.getVariables(entity) } + listOf(condition).asVariables()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = tasksByPriority.values
.flatten()
.flatMap {
it.getVariables(entity, behaviourConfigurationContext)
} + 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) {}
@@ -24,28 +24,18 @@ class ApplyBehaviours : BehaviourConfig {
var condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
@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,26 +37,22 @@ 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,
cobblemonResource("set_core_activities") to SetCoreActivities::class.java,
cobblemonResource("add_variables") to AddVariablesConfig::class.java,
cobblemonResource("set_variables") to SetVariablesConfig::class.java,
cobblemonResource("add_memories_and_sensors") to AddMemoriesAndSensorsConfig::class.java,
)
}
/** 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,40 @@
/*
* 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
class CustomScriptBehaviourConfig : BehaviourConfig {
var condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
val variables: List<MoLangConfigVariable> = emptyList()
val script: ExpressionLike = "0".asExpressionLike()
companion object {
val runtime = MoLangRuntime().setup()
}
override fun getVariables(entity: LivingEntity) = variables
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (!checkCondition(entity, condition)) return
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",
@@ -168,7 +168,7 @@ Example:
```json
{
"type": "set_variables",
"variableValues": {
"variables": {
"aggressive": true,
"attack_range": "10"
}
@@ -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,85 @@
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.util.asExpression
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.util.activityRegistry
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.resolveBoolean
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
/**
* 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 condition = "true".asExpressionLike()
val script = cobblemonResource("dummy")
val variables = mutableListOf<MoLangConfigVariable>()
private fun runScript(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (!checkCondition(behaviourConfigurationContext, condition)) return
script.resolve(behaviourConfigurationContext.runtime)
override fun getVariables(entity: LivingEntity) = variables
override fun configure(entity: LivingEntity, context: BehaviourConfigurationContext) {
val runtime = MoLangRuntime().setup()
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!runtime.resolveBoolean(condition)) return
val struct = createBrainStruct(entity, context)
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
}
runtime.resolve(script, mapOf("brain" to struct))
}
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = variables
override fun preconfigure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (executeEarly) {
runScript(entity = entity, behaviourConfigurationContext = behaviourConfigurationContext)
}
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
}
}
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (!executeEarly) {
runScript(entity = entity, behaviourConfigurationContext = behaviourConfigurationContext)
}
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()
}
@@ -15,8 +15,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,28 @@
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))
}
}
}

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