Compare commits

..
6333 changed files with 32408 additions and 369239 deletions
+78 -306
View File
@@ -1,91 +1,39 @@
# Changelog
## [1.4.1](#1-4-1)
### Changes
- Slightly lowered the volume of all cries
- Pasture blocks will now also connect their bottom left and right sides to walls, iron bars, glass panes and any other modded block that follows the same connection rules.
## [1.4.0](#1-4-0)
### Additions
- Cobblemon now has compatibility with [Adorn](https://modrinth.com/mod/adorn) allowing you to craft Apricorn wood furniture.
- Berries can now be used in recipes from [Farmer's Delight](https://modrinth.com/mod/farmers-delight) and [Farmer's Delight (Fabric)](https://modrinth.com/mod/farmers-delight-fabric), as well as any other mods using the same berry tags.
- The nature of cobblemon will now be displayed italicized when a mint has been applied. Hovering over the nature will display the mint that was applied. This is the intended behaviour, because the original nature and taste of the Cobblemon does not change when a mint is applied.
- Giving Pokémon items now plays a sound
### Fixes
- Fixed various stone related blocks not being valid for Big Roots to spread on the Fabric version.
- Updated the registration of compostable items to improve compatibility with Fabric forks such as Quilt. Please note this does not mean we officially support Quilt, this change was only done since it was possible by correcting the registration to use the new intended way in the Fabric API.
- Fixed Dispensers being unable to shear grown Apricorns.
- Fixed Bowl not being given back to player after using Berry Juice
- Fixed Moonphases for Clefairy line
- Fixed issue where Potions, Super Potions, and Hyper Potions did not work during battle
- Fixed the compatibility patch with the Forge version of [Carry On](https://modrinth.com/mod/carry-on) due to a bug on the mod, the Fabric version was unchanged and is still compatible.
- Added the ability to place Berries on modded Farmland blocks.
- Shouldered Pokémon now hop off when selected in team and r is pressed. This also is in effect in battles leading to shouldered Pokémon jumping of the shoulder of the trainer when it is their turn.
- Made more items compostable and changed the process for making items compostable.
- Added the ability for Hoppers to fill Brewing Stands with Medicinal Brews and Potions.
- Apricorn blocks are now flammable.
- The default pose for Pokémon being passengers is now "standing".
- Fixed issue where some IVs were changing every player relog.
- Fixed cursed battle message
- Fixed mistakes in pokemon spawning JSONs
- Fixed advancement crash from bad datapack evolution data.
### Developer
- Fixed the `SpawnEvent` not respecting usage of `Cancelable#cancel`.
- Added the `EvolutionTestedEvent`, this allows listening and overriding the final result of evolution requirement tests.
## [1.4.0 - The Friends and Farms Update (October 13th, 2023)](#1-4-0)
### Additions
- Added pasture blocks, used to let your PC Pokémon roam around an area.
- Added nicknaming from the summary menu of a Pokémon (click their name).
- Added trading between players. Press R while looking at another player and you'll figure the rest out.
- Added mints for changing Pokémon stats. These are most commonly found at high altitudes.
- Added [Revival Herbs](https://wiki.cobblemon.com/index.php/Revival_Herb), with pep-up flowers when fully grown, growing in lush caves.
- Added [Medicinal Leeks](https://wiki.cobblemon.com/index.php/Medicinal_Leek), growing on the surface of rivers and ponds. It is a potion ingredient and can be cooked as food!.
- Added [Big Roots](https://wiki.cobblemon.com/index.php/Big_Root), generating from cave ceiling dirt which sometimes spread as energy roots.
- Added [69 Berry Trees](https://wiki.cobblemon.com/index.php/Berry_Tree) and [Berries](https://wiki.cobblemon.com/index.php/Berry). Some are found in village farms, some from planting different berries close to each other.
- Added mulches: Mulch Base, Growth Mulch, Surprise Mulch, Coarse Mulch, Humid Mulch, Rich Mulch, Loamy Mulch, Peat Mulch, and Sandy Mulch
- Added [Vivichokes](https://wiki.cobblemon.com/index.php/Vivichoke), obtainable from wandering villager trades and some loot chests.
- Added medicine brewing using medicinal leeks and berries in brewing stands.
- Added Pokémon cries when in battles and being sent out.
- Added medicinal items: Berry Juice, Heal Powder, Remedy, Fine Remedy, Superb Remedy, Revive, Max Revive, Potion, Super Potion, Hyper Potion, Max Potion, Full Restore, Full Heal, Antidote, Awakening, Burn Heal, Ice Heal, and Paralyze Heal.
- Added battle items: X Attack, X Defence, X Sp.Atk, X Sp.Def, X Speed, Dire Hit, and Guard Spec.
- Added EV items: Power Anklet, Power Band, Power Belt, Power Bracelet, Power Lens, Power Weight.
- Added food items: Roasted Leek, Leek and Potato Stew, Braised Vivichoke, and Vivichoke Dip
- Added evolution items: Auspicious Armor and Malicious Armor, which can be used to evolve Charcadet into Armarouge or Ceruledge respectively.
- Added (mostly brewing) recipes for HP Up, Protein, Iron, Calcium, Zinc, Carbos, PP Up, PP Max, and Medicinal Leek to Magenta Dye.
- Added held items: Bright Powder, Destiny Knot
- Added AI for Nosepass to point towards world spawn when idle. We just think it's neat.
- Added shoulder mounting for Mimikyu.
- Added nicknaming.
- Added mints, found on mountains.
- Added revival herbs, with pep-up flowers if fully grown, growing in lush caves.
- Added medicinal leeks, growing on the surface of rivers and ponds. It is a potion ingredient!
- Added big roots, generating from cave ceiling dirt and sometimes spreading into energy roots.
- Added trading between players. Press R while looking at another player, you'll figure it out.
- Added pasture blocks, used to let your PC Pokémon roam around your base.
- Added support for scale in animations.
- Added support for jump keyframes (i.e. pre and post keyframes)
- Made Nosepass point towards world spawn while idle.
- Added structure spawn conditions
- Added Pokémon cry system for battles and sending out.
- Added cries for Gen 1-9 starters and their evolutions.
- Added cries for Chatot, Darmanitan, Darumaka, Lucario, Mimikyu, Quagsire, Riolu, Wooper, Caterpie, Metapod, Butterfree, Weedle, Kakuna, Beedrill, Pidgey, Pidgeotto, Pidgeot, Rattata, Raticate, Cleffa, Clefairy, Clefable, Tauros, Miltank, Buneary, Lopunny, Wooloo, Dubwool.
- Added recipes for Berry Juice, Heal Powder, Remedy, Fine Remedy, Superb Remedy, Revive, Max Revive, HP Up, Protein, Iron, Calcium, Zinc, Carbos, PP Up, PP Max, Pasture, Medicinal Leek to Magenta Dye, Roasted Leek, Leek and Potato Stew, Braised Vivichoke, Vivichoke Dip, Mulch Base, Growth Mulch, Surprise Mulch, Coarse Mulch, Humid Mulch, Rich Mulch, Loamy Mulch, Peat Mulch, Sandy Mulch, Health Feather, Muscle Feather, Resist Feather, Genius Feather, Clever Feather, Genius Feather
- Added a shoulder mount for Mimikyu
- Added Advancement trigger for defeating Pokémon and collecting varieties of Pokémon.
- Added sleep animations to Arcanine, Jigglypuff, Wigglytuff, Vulpix and Ninetales.
- Added flying placeholder animations to Pidgey, Pidgeotto, Pidgeot, Golbat, Crobat, Scyther, Scizor, Zapdos, Moltres, Articuno, Dragonite, Rowlet, Dartrix, and Decidueye.
- Added loot to various vanilla chest loot tables (Link Cable in Ancient Cities, Woodland Mansions, End Cities, and Igloos, Vivichoke Seeds in Jungle Temples, Dungeons, and Plains, Savanna, Snowy, and Taiga Villages, and all 7 Apricorn Sprouts in Desert, Plains, Savanna, Snowy, and Taiga Villages, as well as the Bonus Chest, which can also have 5 of the basic Poké Ball types)
- Added a `doShinyStarters` gamerule to make it quick and easy to be offered shiny starters.
- Added a `doPokemonLoot` gamerule to toggle Pokémon dropping items/exp on death.
- Added ability activation announcement when in battle.
- Added animations for Wailord and made it BIGGER.
- Added Cherry Torterra variant.
- Added 2 new face spots for Spinda. The number of unique Spindas increases...
- Added Forretress Shulker variant.
- Added loot to various vanilla chest loot tables (Link Cable in Ancient Cities, Woodland Mansions, End Cities, and Igloos, Vivichoke Seeds in Jungle Temples, Dungeons, and Plains, Savanna, Snowy, and Taiga Villages, and all 7 Apricorn Sprouts in Desert, Plains, Savanna, Snowy, and Taiga Villages, as well as the Bonus Chest)
- Added the `/teststore <player> <store> <properties>` command allowing command block/mcfunction users to query a party, PC or both for Pokémon matching specific properties and returning the match count, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.teststore` if a permission mod is present.
- Added the `/querylearnset <player> <slot> <move>` command allowing command block/mcfunction users to query a party slot and check if the Pokémon can learn a specific move returning a 1 if yes otherwise 0, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.querylearnset` if a permission mod is present.
- Added the `/testpcslot <player> <slot> <properties>` command allowing command block/mcfunction users to query a pc slot and check if the Pokémon matches specific properties returning a 1 if yes otherwise 0, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.testpcslot` if a permission mod is present.
- Added the `/testpartyslot <player> <slot> <properties>` command allowing command block/mcfunction users to query a party slot and check if the Pokémon matches a specific properties returning a 1 if yes otherwise 0, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.testpartyslot` if a permission mod is present.
- Added the `/clearparty <player>` command for emptying a player's party.
- Added the `/clearpc <player>` command for emptying a player's PC.
- Added the `/pokemonrestart <reset_starters>` and the `/pokemonrestartother <player> <reset_starters>` command allowing command block/mcfunction users to reset a players Pokémon data.
- Added support for "isBattle" and "isTouchingWater" properties on resource pack Pokémon poses. This allows your custom Pokémon to be posed differently when in battle.
- Added support for "isVisible" on a transformed part on resource pack Pokémon poses. This allows your custom Pokémon to have bones disappear in specific poses, such as hiding Greninja's throwing star when not in a battle pose.
- Added the `doPokemonLoot` gamerule to toggle Pokémon dropping items/exp on death.
### Pokémon Added
#### Gen 2
- Chikorita
- Bayleef
- Meganium
- Totodile
- Croconaw
- Feraligatr
- Cyndaquil
- Quilava
- Typhlosion
- Spinarak
- Ariados
- Shuckle
@@ -99,12 +47,6 @@
- Flaaffy
- Ampharos
- Sudowoodo
- Snubbull
- Granbull
- Phanpy
- Donphan
- Teddiursa
- Ursaring
#### Gen 3
@@ -118,7 +60,7 @@
- Cacnea
- Cacturne
- Poochyena
- Mightyena
- Mightyenna
- Wingull
- Pelipper
- Numel
@@ -131,22 +73,11 @@
- Chimecho
- Barboach
- Whiscash
- Volbeat
- Illumise
- Zigzagoon
- Linoone
- Ralts
- Kirlia
- Gardevoir
- Nincada
- Ninjask
- Shedinja
- Beldum
- Metang
- Metagross
#### Gen 4
- Cherubi (Planned)
- Cherrim (Planned)
- Carnivine
- Shinx
- Luxio
@@ -159,77 +90,48 @@
- Chatot
- Combee
- Vespiquen
- Buizel
- Floatzel
- Starly
- Staravia
- Staraptor
- Gallade
#### Gen 5
- Bouffalant
- Bouffolant
- Roggenrola
- Boldore
- Gigalith
- Venipede
- Whirlipede
- Venepede
- Whirlpede
- Scolipede
- Yamask
- Cofagrigus
- Patrat
- Watchog
- Yamask (Planned)
- Cofagrigus (Planned)
- Patrat (Planned)
- Watchog (Planned)
- Lillipup
- Herdier
- Stoutland
- Cottonee
- Whimsicott
- Pidove
- Tranquill
- Unfezant
- Timburr
- Gurdurr
- Conkeldurr
#### Gen 6
- Scatterbug
- Spewpa
- Vivillon
- Skrelp
- Dragalge
- Bunnelby
- Diggersby
- Phantump
- Trevenant
- Fletchling
- Fletchinder
- Talonflame
- Vivillion
- Skrelp (Planned)
- Dragalge (Planned)
- Bunnelby (Planned)
- Diggersby (Planned)
#### Gen 7
- Wishiwashi
- Wishiwashi (Planned)
- Cutiefly
- Ribombee
- Stufful
- Bewear
- Comfey
- Alolan Exeggutor
- Alolan Raichu
- Alolan Meowth
- Alolan Persian
- Komala
- Wimpod
- Golisopod
- Crabrawler
- Crabominable
- Mudbray
- Mudsdale
#### Gen 8
- Arrokuda
- Barraskewda
- Arrokuda (Planned)
- Barraskewda (Planned)
- Nickit
- Thievul
- Falinks
@@ -238,28 +140,12 @@
- Rookidee
- Corvisquire
- Corviknight
- Galarian Ponyta
- Galarian Rapidash
- Yamper
- Boltund
- Galarian Zigzagoon
- Galarian Linoone
- Obstagoon
- Galarian Meowth
- Perrserker
- Ursaluna
#### Gen 9
- Sprigatito
- Sprigatitto
- Floragato
- Meowscarada
- Fuecoco
- Crocalor
- Skeledirge
- Quaxly
- Quaxwell
- Quaquaval
- Flittle
- Espathra
- Garganacl
@@ -269,186 +155,88 @@
- Ceruledge
- Cetoddle
- Cetitan
- Shroodle
- Grafaiai
- Shroodle (Planned)
- Grafaiai (Planned)
- Tandemaus
- Maushold
- Varoom
- Revavroom
- Squawkabilly
- Squawkabilly (Planned)
- Glimmet
- Glimmora
- Annihilape
- Tinkatink
- Tinkatuff
- Tinkaton
- Maschiff
- Mabosstiff
- Lechonk
- Oinkologne
- Paldean Wooper
- Clodsire
### Changes
- Removed the existing shoulder effects from Pokémon until we have more balanced versions of them (they're too powerful!)
- Updated models and textures of Weedle, Dwebble and Crustle, Spiritomb, Koffing and Weezing, Kadabra and Alakazam, Emolga, Oshawott, Doduo and Dodrio, Dratini and Dragonair and Dragonite, Sneasel and Weavile, Gyarados, Hitmonlee and Hitmonchan, Chesnaught, Spinda, Mamoswine, Steelix, Misdreavus and Mismagius, Buneary and Lopunny, Golduck, Meowth and Persian, Fennekin and Braixen and Delphox, Snivy and Servine and Serperior, Ratatta and Raticate, Nidorina and Nidoqueen, Nidoran Male and Nidoking, Riolu and Lucario, Haunter and Gengar, Mankey and Primeape, Mew and Mewtwo, Arcanine, Magnemite and Magneton and Magnezone, Exeggcute and Exeggutor, Elekid and Electabuzz and Electivire, Pichu and Pikachu and Raichu, Wooper, Drowzee and Hypno, Aerodactyl, Spearow and Fearow, Lickitung and Lickilicky, Pidgey and Pidgeotto and Pidgeot, Scyther and Scizor and Kleavor, Popplio and Brionne and Primarina, Torchic and Combusken and Blaziken, Happiny and Chansey and Blissey.
- Updated animations for Steelix, Turtwig and Grotle and Torterra, Ponyta and Rapidash, Piplup and Prinplup and Empoleon, Drowzee and Hypno, Farfetch'd, Exeggcute and Exeggutor, Bidoof, Chimecho, Lickitung and Lickilicky, Popplio and Brionne, Luvdisc, Chimchar and Monferno and Infernape, Sobble and Drizzile and Inteleon, Greninja, Heatmor, Aerodactyl, Ditto, Lotad and Lombre and Ludicolo, Pumpkaboo and Gourgeist.
- Updated model and textures of Arcanine, Magnemite, Magneton, Magnezone, Exeggcute, Exeggutor, Farfetchd, Elekid, Electabuzz, Electivire, Pichu, Pikachu, Raichu, Gengar, Wooper Drowzee, Hypno, Aerodactyl, Spearow, Fearow, Lickitung, Pidgeotto, Pidgeot, Scyther, Scizor, Kleavor, Popplio, Brionne, Primarina, Torchic, Combusken, Blaziken, Aerodacyl, Scyther, Scizor, Kleavor, Lickitung, Lickilicky, Happiny, Chansey, Pidgey, Pidgeotto, Pidgeot, Spearow, Fearow, Drowzee, Hypno, Arcanine, Magnemite, Magneton, Magnezone, Exeggcute, Exeggutor, Farfetchd, Elekid, Electabuzz, Electivire, Pichu, Pikachu, Raichu, and Gengar.
- Updated models of Sceptile and Shuckle.
- Updated sprites for EV medicines, the rare candy, and the apricorn door item.
- Updated textures for apricorn doors and all the evolution stone ores.
- Ponyta and Rapidash now have animated textures; they look insane.
- Updated Apricorn Leaves color.
- Wild Pokémon now heal if you are defeated by them or flee from them.
- Doubled the default time between ambient Pokémon cries (they have cries if you're using a resource pack to add them)
- Moved spawn attempts per tick to a config option (ticksBetweenSpawnAttempts)
- PCs can now be waterlogged
- Starter selection prompt now appears as a tutorial-esque toast instead of plain text
- Cobblemon items can now all have their own tooltips via resourcepacks, to add a tooltip simply add a lang entry like "item.cobblemon.{item_id}.tooltip", if you want to add multiple tooltip lines you can do so with "item.cobblemon.{item_id}.tooltip_1" and upwards.
- Updated texture of Weedle.
- Updated shiny texture of Dwebble and Crustle.
- Updated Animations for Piplup, Prinplup, Empoleon, Drowzee, Hypno, Farfetch'd, Exeggcute, Exeggutor, Bidoof, Chimecho, Lickytung, Lickilicky, Popplio, Brionne, Luvdisc, Chimchar, Monferno, Infernape, Sobble, Drizzile, Inteleon, Greninja, Heatmor, Aerodactyl, Ditto, Lotad, Lombre, Ludicolo, Pumpkaboo, Gourgeist.
- Reorganised the advancements recipes folder
- Pokéedit command now supports IVs and EVs.
- Pokeedit command now supports IVs and EVs.
- Reorganised creative categories
- Pokémon can now wander into non-solid blocks such as foliage
- Thrown Poké Balls now despawn after 30 seconds so that they don't fly forever.
- Pokemon can now wander into non-solid blocks such as foliage
- Pokeballs now despawn after 30 seconds
- Dive Balls will now have the same motion speed underwater as if they were thrown in the air.
- Hardcoded potion shoulder effects have been removed. You can now use any potion vanilla or otherwise with the parameters you'd like, for more information see the [Datapack & Resourcepack Creators](#datapack-&-resourcepack-creators) section.
- Clicking categories of the Stat subsection or the party reorder button in the Summary screen will now produce a click sound.
- Hardcoded potion shoulder effects have been removed, you can now use any potion vanilla or otherwise with the parameters you'd like, for more information see the [Datapack & Resourcepack Creators](#datapack-&-resourcepack-creators) section.
- Clicking categories of the Stat sub-section or the party reorder button in the Summary screen will now produce a click sound.
- Updated PC Recipe.
- Improved Pokémon AI and movement.
- Friendship will slowly increase when Pokémon are shoulder-mounted.
- Master Balls are now unable to be burned when dropped into fire/lava. They're made from stronger stuff.
- Pokémon will appear red when hurt, like regular entities, except when they're fainting.
- Pokémon's air meter no longer depletes while battling underwater.
- Sleeping partially restores PP of Pokémon
- Shoulder mounts now match the shoulder position a bit more accurately when sneaking.
- Poison Heal will now cause poisoned Pokémon to heal outside of battle.
- Updated Poké Ball, PC, UI, evolution and Healing Machine sounds.
### Added cries to the following Pokémon:
- All starters and their evolutions
- Caterpie, Metapod, Butterfree
- Weedle, Kakuna, Beedrill
- Pidgey, Pidgeotto, Pidgeot
- Rattata, Raticate
- Spearow, Fearow
- Ekans, Arbok
- Pichu, Pikachu, Raichu, Alolan Raichu
- Cleffa, Clefairy, Clefable
- Mankey, Primeape, Annihilape
- Ponyta, Rapidash, Galarian Ponyta, Galarian Rapidash
- Farfetch'd, Galarian Farfetch'd, Sirfetch'd
- Onix, Steelix
- Tauros
- Ditto
- Eevee, Vaporeon, Jolteon, Flareon, Espeon, Umbreon, Leafeon, Glaceon, Sylveon
- Hoothoot, Noctowl
- Mareep, Flaaffy, Ampharos
- Aipom, Ambipom
- Wooper, Quagsire, Clodsire
- Snubbull, Granbull
- Miltank
- Poochyena, Mightyena
- Taillow, Swellow
- Ralts, Kirlia, Gardevoir, Gallade
- Shroomish, Breloom
- Nincada, Ninjask, Shedinja
- Buneary, Lopunny
- Chingling
- Chatot
- Riolu, Lucario
- Pidove, Tranquill, Unfezant
- Roggenrola, Boldore, Gigalith
- Venipede, Whirlipede, Scolipede
- Maractus
- Dwebble, Crustle
- Yamask, Cofagrigus
- Bunnelby, Diggersby
- Fletchling, Fletchinder, Talonflame
- Scatterbug, Spewpa, Vivillon
- Honedge, Doublade, Aegislash
- Skrelp, Dragalge
- Phantump, Trevenant
- Pumpkaboo, Gourgeist
- Bergmite, Avalugg
- Mudbray, Mudsdale
- Stufful, Bewear
- Mimikyu
- Rookidee, Corvisquire, Corviknight
- Nickit, Thievul
- Wooloo, Dubwool
- Yamper, Boltund
- Tandemaus
- Fidough, Dachsbun
- Squawkabilly
- Nacli, Naclstack, Garganacl
- Charcadet, Armarouge, Ceruledge
- Maschiff, Mabosstiff
- Shroodle, Grafaiai
- Flittle, Espathra
- Tinkatink, Tinkatuff, Tinkaton
- Varoom, Revavroom
- Glimmet, Glimmora
- Cetoddle, Cetitan
- Tatsugiri
### Fixes
- Fixed spawning moon phase dependent Pokémon only when the moon phase is wrong (that's a funny woopsy)
- Fixed spawning moon phase dependent Pokémon only when the moon phase is wrong
- Fixed large Pokémon spawning partially inside walls where they suffocate.
- Fixed custom Pokémon in your party or PC not being removed when the addon is removed, causing major issues.
- Fixed messages for entry hazards, screens, weather, damage, healing, Tailwind, Perish Song, Destiny Bond, Shed Skin, Uproar, Forewarn, Disguise, Arena Trap, Yawn, Curse, Clamp, Whirlpool, Liquid Ooze, Miracle Eye, Safeguard, Magic Bounce, Lock On, Focus Energy, Confusion, and more.
- Fixed messages for entry hazards, screens, Tailwind, Perish Song, Destiny Bond, Shed Skin, Uproar, Forewarn, Disguise, Arena Trap, Yawn, Curse, Clamp, Whirlpool, Liquid Ooze, Miracle Eye and Safeguard.
- Fixed Porygon not evolving with an Upgrade.
- Fixed super sized Pumpkaboo not having any moves.
- Fixed Infernape look animation.
- Fixed Garchomp T-posing while swimming which was very funny.
- Fixed a bug that caused sleeping Pokémon to stay asleep. Forever. The years passing them by as they dream of a world without hate...
- Fixed a bug that would freeze a battle when a Pokémon gets trapped by an ability, making the trap abilities even scarier and trap-like than they were before.
- Fixed Garchomp t-posing while swimming.
- Fixed a bug that caused sleeping Pokémon to stay asleep.
- Fixed a bug that would freeze a battle when a Pokémon gets trapped due to an ability.
- Fixed the Poké Ball close animation canceling whenever colliding with a block.
- Fixed faint animations not working properly in add-ons.
- Fixed lighting and Pokémon label issues when a Pokémon item frame is nearby.
- Fixed Pokémon being able to spawn outside the world border as a tease.
- Fixed deepslate water stone ore items looking like deepslate fire stone ores. Huh?
- Fixed a bunch of client-side logging errors when Pokémon are shoulder mounted. You didn't notice? Good.
- Fixed deepslate water stone ore items looking like deepslate fire stone ores.
- Fixed a bunch of client-side logging errors when Pokémon are shoulder mounted.
- Fixed a crash when wild Pokémon have to struggle under specific circumstances.
- Fixed uncolored pixels on Yanma's shiny texture.
- Fixed apricorn tree leaves looking gross on the Fast graphics mode.
- Fixed apricorn tree leaves looking gross on Fast graphics mode.
- Fixed hoes not breaking apricorn tree leaves any faster.
- Fixed Shiftry's PC model position.
- Fixed the /pc command not playing the opening sound.
- Fixed Shedinja not being able to recover naturally.
- Fixed different forms of Pokémon not being able to appear as different sizes.
- Fixed the Healing Machine soft locking you from using others when removed by non-players.
- Fixed animations being sped up when using the Replay Mod.
- Fixed particle animations not running when a Pokémon is off-screen.
- Fixed Pokémon variants and layers not rendering correctly when shouldered and playing on a dedicated server, existing shoulders affected will need to be retrieved and shouldered again.
- Fixed shoulder effects not staying applied through situations that remove potion effects such as drinking milk.
- Fixed Shedinja not being able to recover naturally.
- Fixed Shedinja evolving to use the consumed Poké Ball and removed the held item to prevent dupes.
- Fixed Shedinja healing above 1 HP.
- Fixed Shedinja, basically.
- Shoulder effects will now stay applied through situations that remove potion effects such as drinking milk.
- Fixed Shedninja evolving to use the consumed Pokeball and removed the held item.
- Fixed non-existent species in spawn pool file causing random species to spawn.
- Fixed shearing Pokémon dropping 0-2 wool instead of 1-3.
- Fixed some alignment issues in the stat hexagon of the summary menu. OCD people rejoice.
- Fixed capture calculations not applying ball bonuses entirely correctly.
- Fixed battles soft-locking when consecutive Pokémon faint on switch-in.
- Fixed timing and color of battle window messages.
- Fixed players being able to trade, battle and let out their Pokémon while in spectator mode.
- Fixed Galarian Yamask not being able to evolve and by proxy the ``damage_taken`` evolution requirement.
- Fixed Bisharp not being able to evolve and by proxy the ``defeat`` evolution requirement.
- Fixed White-Striped Basculin not being able to evolve because of a broken ``recoil`` evolution requirement.
- Fixed Primeape, Qwilfish and Stantler not being able to evolve because of a broken ``use_move`` evolution requirement.
- Fixed Bramblin, Pawmo, and Rellor not being able to evolve because of a broken ``blocks_traveled`` evolution requirement.
- Fixed displayName property in spawn files not doing what it's meant to do.
- Fixed Pokémon not sleeping in the wild like we wanted them to.
- Fixed some alignment issues in the stat hexagon of the summary menu.
### Developer
- Added SpawnEvent, ThrownPokeballHitEvent, PokemonSentEvent, PokemonRecalledEvent.
- Added BattleFledEvent, BattleStartedEvent, BattleFaintedEvent.
- Added persistent NBT property inside Pokémon to store quick and simple data.
- Added SpawnEvent
- Added persistent NBT property inside Pokemon to store quick and simple data.
- Species and FormData have had their evolutions, pre-evolution and labels properties exposed. It is still recommended to work using a Pokémon instance when possible.
- Added capture check to BattleVictoryEvent.
- Added capture check to BattleVictoryEvent
- Added ThownPokeballHitEvent
- The various hardcoded potion shoulder effects have been removed, make use of PotionBaseEffect.
- Added ContextManager for tracking causes and contexts of conditions created during a battle. See BattleContext for types of conditions that are tracked.
- Added MongoDB support for storing Pokémon and Player data. Must be enabled in config, requires MongoDB core and sync drivers (4.10.0+).
- CobblemonShowdown updated to version 10.
- Generation of a battle can be set in BattleFormat.
- Pokémon now have ``teraType``, ``dmaxLevel``, and ``gmaxFactor`` properties. Gimmicks can be used during battle by adding the respective identifiers to ``keyItems`` in PlayerData: ``key_stone``, ``z_ring``, ``dynamax_band``, and ``tera_orb``. Dynamax is only supported in Gen 8 battles. Mega Evolution and Z-Power require custom held items to be added (e.g. an item with the path ``gengarite`` will allow Gengar to Mega Evolve). Currently custom Z-Crystals and Mega Stones are not supported.
### Datapack & Resourcepack Creators
- All potion related shoulder effects have had their IDs changed. They now all share the same type being `potion_effect` and use the vanilla Potion data [parameters](https://minecraft.fandom.com/wiki/Potion#Item_data). For example, here is the converted Pidgey asset:
- All potion related shoulder effects have had their IDs changed, they now all share the same type being `potion_effect` and use the vanilla Potion data [parameters](https://minecraft.fandom.com/wiki/Potion#Item_data), take for example the default Pidgey asset:
- ```json
{
"type": "potion_effect",
@@ -459,23 +247,6 @@
"showIcon": false
}
```
- Renamed the ``walked_steps`` evolution requirement to ``blocks_traveled``.
- Added support for scale in animations.
- Added support for jump keyframes (i.e. pre and post keyframes)
- Added structure spawning conditions
- Added Advancement trigger for defeating Pokémon and collecting varieties of Pokémon.
- Added support for "isBattle" and "isTouchingWater" properties on resource pack Pokémon poses. This allows your custom Pokémon to be posed differently when in battle.
- Added support for "isVisible" on a transformed part on resource pack Pokémon poses. This allows your custom Pokémon to have bones disappear in specific poses, such as hiding Greninja's throwing star when not in a battle pose.
- Added support for battle music. Sounds can be added to the ``battle.pvp.default`` and ``battle.pvw.default`` sound events.
- Added 'enabled' optional property on model layers, allowing later variations to disable previously-defined layers. See [this issue](https://gitlab.com/cable-mc/cobblemon/-/issues/335) for how this looks.
- Cobblemon items can now all have their own tooltips via resourcepacks. To add a tooltip, add a lang entry like "item.cobblemon.{item_id}.tooltip". If you want to add multiple tooltip lines you can do so with "item.cobblemon.{item_id}.tooltip_1" and upwards.
- Item interaction evolutions and held item requirements now support NBT by creating an object JSON containing the key ``item`` for what used to be the existing condition support and a ``nbt`` key for the NBT format, this is the string [format](https://minecraft.fandom.com/wiki/NBT_format) expected in commands. Existing data does not need to be updated.
- Fixed faint animations not working properly in add-ons.
- Fixed non-existent species in spawn pool files causing random species to spawn.
### Localization
- Added partial translations for Dutch, Polish, Swedish, Hungarian, Czech, Cyprus Greek, and even Esperanto.
- Updated translation for French and Canadian French, Simplified Chinese, Japanese, Korean, Spanish and Mexican Spanish, Pirate English, German, Thai, Turkish, Portuguese and Brazilian Portuguese, Ukrainian, and Russian.
## [1.3.1 (March 31st, 2023)](#1-3-1)
@@ -489,7 +260,7 @@
- Add lava surface spawn preset.
- Added an ``any`` evolution requirement allowing you to define ``possibilities`` of other evolution requirements, for example, this allows you to create an evolution that requires the Pokémon to be shiny or a female.
- Added the `/spawnpokemonfrompool [amount]` or `/forcespawn [amount]` command to spawn Pokémon(s) in the surrounding area using the natural spawn rates/pool of that area, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.spawnpokemon` if a permission mod is present. On a successful execution of the command, the amount of Pokémon spawned will be the output.
- Added the `/pokebox` and `/pokeboxall` commands to move Pokémon(s) to the PC from a Player's party, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.pokebox` if a permission mod is present. On a successful execution of the command the output will be the number of pokemon moved to the Player's PC.
- Added the `/pokebox` and `/pokeboxall` commands to move Pokemon(s) to the PC from a Player's party, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.pokebox` if a permission mod is present. On a successful execution of the command the output will be the number of pokemon moved to the Player's PC.
- Added the `/pc` command which opens up the PC UI the same way interacting with the block would, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.pc` if a permission mod is present.
### Changes
@@ -512,6 +283,7 @@
- Changed the recipes for Mystic Water, Miracle Seed, and Charcoal Stick to utilise the evolution stones, as well as Never-Melt Ice having an alternate recipe using the Ice Stone.
- Replaced the `Failed to handle` battle messages to `Missing interpretation` to make it more clear that mechanics do work just still pending dedicated messages.
- Healing Machine and PC are now mine-able with pickaxes and Apricorn leaves are mine-able using hoes.
- Updated Pokéball, UI and evolution sounds.
### Fixes
- Fixed killing a Dodrio killing your game. Dodrio will never look the same to us.
@@ -1,13 +1,3 @@
/*
*
* * 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/.
*
*/
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
val accessWidenerFile = "src/main/resources/cobblemon-common.accesswidener"
@@ -23,9 +13,6 @@ plugins {
repositories {
mavenCentral()
//JEI
maven("https://maven.blamejared.com/")
maven("https://maven.tterrag.com/")
}
license {
@@ -68,4 +55,4 @@ tasks {
withType<Jar> {
from(rootProject.file("LICENSE"))
}
}
}
-18
View File
@@ -1,13 +1,3 @@
/*
*
* * Copyright (C) 2023 Cobblemon Contributors
* *
* * This Source Code Form is subject to the terms of the Mozilla Public
* * License, v. 2.0. If a copy of the MPL was not distributed with this
* * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
*/
plugins {
id("cobblemon.base-conventions")
id("cobblemon.publish-conventions")
@@ -19,7 +9,6 @@ architectury {
repositories {
maven(url = "${rootProject.projectDir}/deps")
maven(url = "https://api.modrinth.com/maven")
mavenLocal()
}
@@ -28,20 +17,13 @@ dependencies {
implementation(libs.reflect)
modImplementation(libs.fabricLoader)
//Flywheel has no common dep so just pick one and don't use any platform specific code in common
// modCompileOnly(libs.flywheelFabric)
modApi(libs.molang)
compileOnlyApi(libs.jeiApi)
modCompileOnly(libs.adornFabric)
// For Showdown
modCompileOnly(libs.graal)
//shadowCommon group: 'commons-io', name: 'commons-io', version: '2.6'
// For datastore
modCompileOnly(libs.mongoDriverCore)
modCompileOnly(libs.mongoDriverSync)
testRuntimeOnly(libs.junitEngine)
testImplementation(libs.junitApi)
@@ -1,16 +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.bridges;
import net.minecraft.structure.processor.StructureProcessor;
public interface StructureProcessorListBridge {
void append(StructureProcessor processor);
void clear();
}
@@ -8,8 +8,6 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pose;
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityModel;
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
@@ -40,7 +38,7 @@ public interface Bone {
/**
* Renders a particular bone in relation to a particular entity.
*
* @param context The render context this bone is being drawn for.
* @param entity The entity this bone is being drawn for. This parameter need not be available and can be null
* @param stack The current [MatrixStack] for the renderer
* @param buffer The vertex consumer deployed by the renderer
* @param packedLight The light level to be applied to the bone
@@ -52,8 +50,8 @@ public interface Bone {
*
* @since 1.4.0
*/
void render(
RenderContext context,
<T extends Entity> void render(
@Nullable T entity,
MatrixStack stack,
VertexConsumer buffer,
int packedLight,
@@ -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.mixin;
import com.cobblemon.mod.common.block.PreEmptsExplosion;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.explosion.Explosion;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(value = Explosion.class)
public class ExplosionMixin {
@Redirect(
method = "affectWorld",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/World;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;",
ordinal = 0
)
)
public BlockState cobblemon$whenExploded(World world, BlockPos pos) {
BlockState blockState = world.getBlockState(pos);
if(blockState.getBlock() instanceof PreEmptsExplosion preExplosionBlock) {
preExplosionBlock.whenExploded(world, blockState, pos);
}
return blockState;
}
}
@@ -0,0 +1,47 @@
/*
* 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.CobblemonBlocks;
import com.cobblemon.mod.common.block.PastureBlock;
import net.minecraft.block.BlockState;
import net.minecraft.block.FenceBlock;
import net.minecraft.block.HorizontalFacingBlock;
import net.minecraft.util.math.Direction;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(value = FenceBlock.class)
public abstract class FenceBlockMixin {
// This could be done with a modify variable but it just doesn't seem to want to work, very crude unfinished explanation I had on Fabric discord was
// "boolean is a primitive, it isnt passed by reference but by value and you have your own object references anyway so assigning a new object to non-primitive references (variables) wont modify it after your mixin returns
//(you cant modify a random argument that mixin passes you that way, target the boolean not the FenceBlock)"
// However the issue is the only way to get all the params which we need is by passing in the entire method reference which enforces the return type.
@Redirect(method = "getStateForNeighborUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/FenceBlock;canConnect(Lnet/minecraft/block/BlockState;ZLnet/minecraft/util/math/Direction;)Z"))
private boolean cobblemon$getStateForNeighborUpdate(FenceBlock instance, BlockState state, boolean neighborIsFullSquare, Direction dir) {
return instance.canConnect(state, neighborIsFullSquare, dir) || this.cobblemon$canConnect(state, dir);
}
@Redirect(method = "getPlacementState", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/FenceBlock;canConnect(Lnet/minecraft/block/BlockState;ZLnet/minecraft/util/math/Direction;)Z"))
private boolean cobblemon$getPlacementState(FenceBlock instance, BlockState state, boolean neighborIsFullSquare, Direction dir) {
return instance.canConnect(state, neighborIsFullSquare, dir) || this.cobblemon$canConnect(state, dir);
}
private boolean cobblemon$canConnect(BlockState state, Direction dir) {
if (state.isOf(CobblemonBlocks.PASTURE) && state.get(PastureBlock.Companion.getPART()) == PastureBlock.PasturePart.BOTTOM) {
final Direction pastureFacing = state.get(HorizontalFacingBlock.FACING);
return dir == pastureFacing.rotateYClockwise() || dir == pastureFacing.rotateYCounterclockwise();
}
return false;
}
}
@@ -8,9 +8,7 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityModel;
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone;
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
@@ -38,7 +36,7 @@ public abstract class ModelPartMixin implements Bone {
}
@Override
public void render(RenderContext context, MatrixStack stack, VertexConsumer buffer, int packedLight, int packedOverlay, float r, float g, float b, float a) {
public <T extends Entity> void render(T entity, MatrixStack stack, VertexConsumer buffer, int packedLight, int packedOverlay, float r, float g, float b, float a) {
this.render(stack, buffer, packedLight, packedOverlay, r, g, b, a);
}
@@ -0,0 +1,44 @@
/*
* 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.api.spawning.mixins.CachedOnlyChunkAccessor;
import com.mojang.datafixers.util.Either;
import net.minecraft.server.world.ChunkHolder;
import net.minecraft.server.world.ServerChunkManager;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkManager;
import net.minecraft.world.chunk.ChunkStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import java.util.concurrent.CompletableFuture;
@Mixin(ServerChunkManager.class)
public abstract class ServerChunkManagerMixin extends ChunkManager implements CachedOnlyChunkAccessor {
@Shadow @Nullable protected abstract ChunkHolder getChunkHolder(long pos);
@Override
public Chunk cobblemon$request(int x, int z, @NotNull ChunkStatus status) {
long l = ChunkPos.toLong(x, z);
ChunkHolder holder = getChunkHolder(l);
if (holder != null) {
CompletableFuture<Either<Chunk, ChunkHolder.Unloaded>> future = holder.getFutureFor(status);
if (future.isDone()) {
return future.join().left().orElse(null);
}
}
return null;
}
}
@@ -1,32 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.mojang.datafixers.util.Pair;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.structure.pool.StructurePool;
import net.minecraft.structure.pool.StructurePoolElement;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.List;
@Mixin(StructurePool.class)
public interface StructurePoolAccessor {
@Accessor("elementCounts")
List<Pair<StructurePoolElement, Integer>> getElementCounts();
@Mutable
@Accessor("elementCounts")
void setElementCounts(List<Pair<StructurePoolElement, Integer>> elementCounts);
@Accessor("elements")
ObjectArrayList<StructurePoolElement> getElements();
}
@@ -1,217 +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 net.minecraft.registry.Registry;
import net.minecraft.structure.PoolStructurePiece;
import net.minecraft.structure.StructureTemplateManager;
import net.minecraft.structure.pool.*;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.random.Random;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.HeightLimitView;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.gen.noise.NoiseConfig;
import org.apache.commons.lang3.mutable.MutableObject;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
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.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.*;
@Mixin(StructurePoolBasedGenerator.StructurePoolGenerator.class)
public abstract class StructurePoolGeneratorMixin {
@Final
@Mutable
@Shadow Deque<StructurePoolBasedGenerator.ShapedPoolStructurePiece> structurePieces;
Map<String, Integer> generatedStructureGroupCounts;
private static final Map<String, Integer> structureMaxes;
private static final Map<Identifier, Set<String>> structureGroups = new HashMap<>();
static {
String pokecenter = "pokecenter";
String berry = "berry_farm";
structureGroups.put(CobblemonStructureIDs.PLAINS_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.DESERT_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.SAVANNA_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.SNOWY_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.TAIGA_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.SAVANNA_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.SAVANNA_BERRY_LARGE, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.PLAINS_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.PLAINS_BERRY_LARGE, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.SNOWY_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.SNOWY_BERRY_LARGE, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.DESERT_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.DESERT_BERRY_LARGE, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.TAIGA_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.TAIGA_BERRY_LARGE, Set.of(berry));
//Mapped using location string as key
Map<String, Integer> aMap = new HashMap<>();
aMap.put(pokecenter, 1);
aMap.put(berry, 2);
// aMap.put(CobblemonStructureIDs.PLAINS_LONG_PATH, 3);
// aMap.put(CobblemonStructureIDs.DESERT_LONG_PATH, 3);
// aMap.put(CobblemonStructureIDs.SAVANNA_LONG_PATH, 3);
// aMap.put(CobblemonStructureIDs.SNOWY_LONG_PATH, 3);
// aMap.put(CobblemonStructureIDs.TAIGA_LONG_PATH, 3);
structureMaxes = Collections.unmodifiableMap(aMap);
}
public Set<String> getGroups(Identifier structureIdentifier) {
return structureGroups.getOrDefault(structureIdentifier, Set.of());
}
public boolean hasReachedMaximum(Identifier structureIdentifier) {
Set<String> groups = getGroups(structureIdentifier);
for (String group : groups) {
int count = generatedStructureGroupCounts.getOrDefault(group, 0);
if (count >= structureMaxes.getOrDefault(group, Integer.MAX_VALUE)) {
return true;
}
}
return false;
}
public void incrementStructureCount(Identifier structureIdentifier) {
Set<String> groups = getGroups(structureIdentifier);
for (String group : groups) {
generatedStructureGroupCounts.put(group, generatedStructureGroupCounts.getOrDefault(group, 0) + 1);
}
}
@Inject(method = "<init>", at = @At("RETURN"))
private void onStructurePoolGeneratorCreation(Registry<StructurePool> registry, int maxSize, ChunkGenerator chunkGenerator, StructureTemplateManager structureTemplateManager, List<? super PoolStructurePiece> children, Random random, CallbackInfo ci) {
generatedStructureGroupCounts = new HashMap<>();
}
@ModifyVariable(method = "generatePiece", at = @At("STORE"), ordinal = 1)
private Iterator<StructurePoolElement> reduceStructurePoolElementIterator(Iterator<StructurePoolElement> iterator) {
List<StructurePoolElement> reducedList = new ArrayList<>();
while (iterator.hasNext()) {
StructurePoolElement structure = iterator.next();
Identifier structurePieceLocationKey = getCobblemonOnlyLocation(structure);
if (structurePieceLocationKey == null) {
reducedList.add(structure);
continue;
}
if (!hasReachedMaximum(structurePieceLocationKey)) {
reducedList.add(structure);
}
}
return reducedList.iterator();
}
@ModifyVariable(method = "generatePiece", at = @At("STORE"), ordinal = 1)
private PoolStructurePiece injected(PoolStructurePiece poolStructurePiece) {
Identifier structureLocationKey = getCobblemonOnlyLocation(poolStructurePiece.getPoolElement());
if (structureLocationKey != null) {
incrementStructureCount(structureLocationKey);
}
return poolStructurePiece;
}
// This doesn't SEEM necessary? It appears to work without.
@Inject(method = "generatePiece", at = @At("HEAD"))
private void beforeGeneratePiece(PoolStructurePiece piece, MutableObject<VoxelShape> pieceShape, int minY, boolean modifyBoundingBox, HeightLimitView world, NoiseConfig noiseConfig, CallbackInfo ci) {
// Identifier structureLocationKey = getCobblemonOnlyLocation(piece.getPoolElement());
//
// if (structureLocationKey != null) {
// Integer currentlyGenerated = generatedStructureCounts.get(structureLocationKey);
// if (currentlyGenerated == null) currentlyGenerated = 0;
// generatedStructureCounts.put(structureLocationKey, currentlyGenerated + 1);
// }
//
// List<StructurePoolBasedGenerator.ShapedPoolStructurePiece> reducedStructurePiecesList = structurePieces.stream().toList();
//
// for (Identifier maxStructureLocationKey : structureMaxes.keySet()) {
// Integer maxAllowed = structureMaxes.get(maxStructureLocationKey);
//
// Integer currentlyGenerated = generatedStructureCounts.get(maxStructureLocationKey);
// if (currentlyGenerated == null) currentlyGenerated = 0;
// if (currentlyGenerated < maxAllowed) {
// continue;
// }
//
// //Already have max so need to remove
// reducedStructurePiecesList = reducedStructurePiecesList.stream()
// .filter(shapedStructurePiece -> {
// Identifier locationKey = getCobblemonOnlyLocation(shapedStructurePiece.piece.getPoolElement());
// if (locationKey == null) {
// return true;
// }
//
// return !locationKey.equals(maxStructureLocationKey);
// })
// .collect(Collectors.toList());
// }
//
// structurePieces = new ArrayDeque<>(reducedStructurePiecesList);
}
private static Identifier getCobblemonOnlyLocation(StructurePoolElement structurePoolElement) {
Identifier location = getLocationIfAvailable(structurePoolElement);
if (location == null) return null;
if (!location.getNamespace().equals("cobblemon")) return null;
return location;
}
private static Identifier getLocationIfAvailable(StructurePoolElement structurePoolElement) {
if (structurePoolElement instanceof LegacySinglePoolElement legacySinglePoolElement) {
if (legacySinglePoolElement.location.left().isEmpty()) return null;
return legacySinglePoolElement.location.left().get();
} else if (structurePoolElement instanceof SinglePoolElement singlePoolElement) {
if (singlePoolElement.location.left().isEmpty()) return null;
return singlePoolElement.location.left().get();
} else {
return null;
}
}
// private List<StructurePoolElement> removeInstanceOfLocationKeyFrom(List<StructurePoolElement> structureList, Integer allowedNumberOfInstances, Identifier locationKey) {
// List<StructurePoolElement> reducedList = new ArrayList<>();
// int instancesFound = 0;
//
// for (StructurePoolElement structurePoolElement: structureList) {
// Identifier structureKey = getCobblemonOnlyLocation(structurePoolElement);
// if (structureKey == null || !structureKey.equals(locationKey)) {
// reducedList.add(structurePoolElement);
// continue;
// }
//
// if (instancesFound >= allowedNumberOfInstances) {
// continue;
// }
//
// reducedList.add(structurePoolElement);
// instancesFound++;
// }
//
// return reducedList;
// }
}
@@ -1,43 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.bridges.StructureProcessorListBridge;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.structure.processor.StructureProcessor;
import net.minecraft.structure.processor.StructureProcessorList;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(StructureProcessorList.class)
public class StructureProcessorListMixin implements StructureProcessorListBridge {
@Final
@Mutable
@Shadow
private List<StructureProcessor> list;
@Override
public void append(StructureProcessor processor) {
List<StructureProcessor> mutable = new ArrayList<>(this.list);
mutable.add(processor);
this.list = ImmutableList.copyOf(mutable);
}
@Override
public void clear() {
this.list = ImmutableList.of();
}
}
@@ -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.entity;
import com.cobblemon.mod.common.brewing.BrewingRecipes;
import net.minecraft.block.entity.BrewingStandBlockEntity;
import net.minecraft.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(BrewingStandBlockEntity.class)
public abstract class BrewingStandBlockEntityMixin
{
@Inject(
method = "isValid",
at = @At(value = "HEAD"),
cancellable = true
)
private void cobblemon$isValid(int slot, ItemStack stack, CallbackInfoReturnable<Boolean> cir) {
final BrewingStandBlockEntity entity = (BrewingStandBlockEntity) (Object) this;
if (slot < 3)
{
if (entity.getStack(slot).isEmpty() && BrewingRecipes.brewableItems.contains(stack.getItem()))
cir.setReturnValue(true);
}
}
}
@@ -1,20 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.invoker;
import net.minecraft.block.Block;
import org.spongepowered.asm.mixin.Mixin;
import net.minecraft.block.FireBlock;
import net.minecraft.block.Blocks;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(FireBlock.class)
public interface FireBlockInvoker {
@Invoker("registerFlammableBlock") void registerNewFlammableBlock(Block block, int burnChance, int spreadChance);
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.EvolvePokemonContext
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.SeasonResolver
import com.cobblemon.mod.common.api.data.DataProvider
@@ -19,9 +18,7 @@ import com.cobblemon.mod.common.api.drop.ItemDropEntry
import com.cobblemon.mod.common.api.events.CobblemonEvents.BATTLE_VICTORY
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
import com.cobblemon.mod.common.api.events.CobblemonEvents.EVOLUTION_COMPLETE
import com.cobblemon.mod.common.api.events.CobblemonEvents.LEVEL_UP_EVENT
import com.cobblemon.mod.common.api.events.CobblemonEvents.POKEMON_CAPTURED
import com.cobblemon.mod.common.api.events.CobblemonEvents.TRADE_COMPLETED
import com.cobblemon.mod.common.api.net.serializers.PoseTypeDataSerializer
import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
import com.cobblemon.mod.common.api.net.serializers.Vec3DataSerializer
@@ -49,16 +46,13 @@ 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
import com.cobblemon.mod.common.api.storage.adapter.database.MongoDBStoreAdapter
import com.cobblemon.mod.common.api.storage.adapter.flatfile.FileStoreAdapter
import com.cobblemon.mod.common.api.storage.adapter.flatfile.JSONStoreAdapter
import com.cobblemon.mod.common.api.storage.adapter.flatfile.NBTStoreAdapter
import com.cobblemon.mod.common.api.storage.adapter.flatifle.FileStoreAdapter
import com.cobblemon.mod.common.api.storage.adapter.flatifle.JSONStoreAdapter
import com.cobblemon.mod.common.api.storage.adapter.flatifle.NBTStoreAdapter
import com.cobblemon.mod.common.api.storage.factory.FileBackedPokemonStoreFactory
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
import com.cobblemon.mod.common.api.storage.player.PlayerDataStoreManager
import com.cobblemon.mod.common.api.storage.player.factory.JsonPlayerDataStoreFactory
import com.cobblemon.mod.common.api.storage.player.factory.MongoPlayerDataStoreFactory
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.battles.BattleFormat
@@ -92,6 +86,7 @@ import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.properties.UntradeableProperty
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
@@ -106,10 +101,6 @@ 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
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
@@ -254,6 +245,7 @@ object Cobblemon {
DataKeys.HAS_BEEN_SHEARED,
FlagSpeciesFeatureProvider(keys = listOf(DataKeys.HAS_BEEN_SHEARED), default = false))
CustomPokemonProperty.register(UntradeableProperty)
CustomPokemonProperty.register(UncatchableProperty)
CustomPokemonProperty.register(PokemonFlagProperty)
CustomPokemonProperty.register(HiddenAbilityPropertyType)
@@ -266,52 +258,15 @@ object Cobblemon {
PlatformEvents.SERVER_STARTING.subscribe { event ->
val server = event.server
playerData = PlayerDataStoreManager().also { it.setup(server) }
val mongoClient: MongoClient?
val pokemonStoreRoot = server.getSavePath(WorldSavePath.ROOT).resolve("pokemon").toFile()
val storeAdapter = when (config.storageFormat) {
"nbt", "json" -> {
val jsonFactory = JsonPlayerDataStoreFactory()
jsonFactory.setup(server)
playerData.setFactory(jsonFactory)
if (config.storageFormat == "nbt") {
NBTStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
} else {
JSONStoreAdapter(
pokemonStoreRoot.absolutePath,
useNestedFolders = true,
folderPerClass = true
)
}
}
"mongodb" -> {
try {
Class.forName("com.mongodb.client.MongoClient")
val mongoClientSettings = MongoClientSettings.builder()
.applyConnectionString(ConnectionString(config.mongoDBConnectionString))
.build()
mongoClient = MongoClients.create(mongoClientSettings)
val mongoFactory = MongoPlayerDataStoreFactory(mongoClient, config.mongoDBDatabaseName)
playerData.setFactory(mongoFactory)
MongoDBStoreAdapter(mongoClient, config.mongoDBDatabaseName)
} catch (e: ClassNotFoundException) {
LOGGER.error("MongoDB driver not found.")
throw e
}
}
else -> throw IllegalArgumentException("Unsupported storageFormat: ${config.storageFormat}")
}
.with(ReforgedConversion(server.getSavePath(WorldSavePath.ROOT))) as FileStoreAdapter<*>
storage.registerFactory(
priority = Priority.LOWEST,
factory = FileBackedPokemonStoreFactory(
adapter = storeAdapter,
adapter = if (config.storageFormat == "nbt") {
NBTStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
} else {
JSONStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
}.with(ReforgedConversion(server.getSavePath(WorldSavePath.ROOT))) as FileStoreAdapter<*>,
createIfMissing = true,
pcConstructor = { uuid -> PCStore(uuid).also { it.resize(config.defaultBoxCount) } }
)
@@ -337,7 +292,7 @@ object Cobblemon {
// Ensure the config option is enabled and that the result was a ninjask and that shedinja exists
if (this.config.ninjaskCreatesShedinja && pokemon.species.resourceIdentifier == ninjaskIdentifier && PokemonSpecies.getByIdentifier(Pokemon.SHEDINJA) != null) {
val player = pokemon.getOwnerPlayer() ?: return@subscribe
if (player.isCreative || player.inventory.containsAny { it.item is PokeBallItem }) {
if (player.inventory.containsAny { it.item is PokeBallItem }) {
var pokeball = Items.AIR
player.inventory.combinedInventory.forEach {
it.forEach {
@@ -346,12 +301,7 @@ object Cobblemon {
}
}
}
if (!player.isCreative) {
player.inventory.removeAmountIf(1) { it.item is PokeBallItem }
}
if (pokeball == Items.AIR) {
pokeball = CobblemonItems.POKE_BALL
}
player.inventory.removeAmountIf(1) { it.item is PokeBallItem }
val properties = event.evolution.result.copy()
properties.species = Pokemon.SHEDINJA.toString()
val product = pokemon.clone()
@@ -359,12 +309,9 @@ object Cobblemon {
properties.apply(product)
product.caughtBall = (pokeball as PokeBallItem).pokeBall
pokemon.storeCoordinates.get()?.store?.add(product)
CobblemonCriteria.EVOLVE_POKEMON.trigger(player, EvolvePokemonContext(event.pokemon.preEvolution!!.species.resourceIdentifier, product.species.resourceIdentifier, playerData.get(player).advancementData.totalEvolvedCount))
}
}
}
LEVEL_UP_EVENT.subscribe { AdvancementHandler.onLevelUp(it) }
TRADE_COMPLETED.subscribe { AdvancementHandler.onTradeCompleted(it) }
BagItems.observable.subscribe {
LOGGER.info("Starting dummy Showdown battle to force it to pre-load data.")
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
import com.cobblemon.mod.common.block.entity.PCBlockEntity
import com.cobblemon.mod.common.block.entity.PokemonPastureBlockEntity
@@ -28,7 +27,6 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
val HEALING_MACHINE: BlockEntityType<HealingMachineBlockEntity> = this.create("healing_machine", BlockEntityType.Builder.create(::HealingMachineBlockEntity, CobblemonBlocks.HEALING_MACHINE).build(null))
@JvmField
val PC: BlockEntityType<PCBlockEntity> = this.create("pc", BlockEntityType.Builder.create(::PCBlockEntity, CobblemonBlocks.PC).build(null))
val BERRY = this.create("berry", BlockEntityType.Builder.create(::BerryBlockEntity, *CobblemonBlocks.berries().values.toTypedArray()).build(null))
@JvmField
val PASTURE: BlockEntityType<PokemonPastureBlockEntity> = this.create("pasture", BlockEntityType.Builder.create(::PokemonPastureBlockEntity, CobblemonBlocks.PASTURE).build(null))
@@ -17,8 +17,6 @@ import com.cobblemon.mod.common.mixin.invoker.StairsBlockInvoker
import com.cobblemon.mod.common.mixin.invoker.TrapdoorBlockInvoker
import com.cobblemon.mod.common.mixin.invoker.*
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.block.BerryBlock
import net.minecraft.block.*
import net.minecraft.block.piston.PistonBehavior
import net.minecraft.entity.effect.StatusEffects
@@ -27,14 +25,11 @@ import net.minecraft.registry.Registry
import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.util.Identifier
import net.minecraft.util.math.intprovider.UniformIntProvider
@Suppress("SameParameterValue", "HasPlatformType", "MemberVisibilityCanBePrivate", "unused")
object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<Block>>, Block>() {
override val registry: Registry<Block> = Registries.BLOCK
override val registryKey: RegistryKey<Registry<Block>> = RegistryKeys.BLOCK
@@ -230,133 +225,17 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
APRICORN_LOG to STRIPPED_APRICORN_LOG
)
private fun apricornBlock(name: String, apricorn: Apricorn): ApricornBlock = this.create(name, ApricornBlock(AbstractBlock.Settings.create().mapColor(apricorn.mapColor()).ticksRandomly().strength(Blocks.OAK_LOG.hardness, Blocks.OAK_LOG.blastResistance).sounds(BlockSoundGroup.WOOD).nonOpaque(), apricorn))
private val berries = mutableMapOf<Identifier, BerryBlock>()
val AGUAV_BERRY = this.berryBlock("aguav")
val APICOT_BERRY = this.berryBlock("apicot")
val ASPEAR_BERRY = this.berryBlock("aspear")
val BABIRI_BERRY = this.berryBlock("babiri")
val BELUE_BERRY = this.berryBlock("belue")
val BLUK_BERRY = this.berryBlock("bluk")
val CHARTI_BERRY = this.berryBlock("charti")
val CHERI_BERRY = this.berryBlock("cheri")
val CHESTO_BERRY = this.berryBlock("chesto")
val CHILAN_BERRY = this.berryBlock("chilan")
val CHOPLE_BERRY = this.berryBlock("chople")
val COBA_BERRY = this.berryBlock("coba")
val COLBUR_BERRY = this.berryBlock("colbur")
val CORNN_BERRY = this.berryBlock("cornn")
val CUSTAP_BERRY = this.berryBlock("custap")
val DURIN_BERRY = this.berryBlock("durin")
val ENIGMA_BERRY = this.berryBlock("enigma")
val FIGY_BERRY = this.berryBlock("figy")
val GANLON_BERRY = this.berryBlock("ganlon")
val GREPA_BERRY = this.berryBlock("grepa")
val HABAN_BERRY = this.berryBlock("haban")
val HONDEW_BERRY = this.berryBlock("hondew")
val HOPO_BERRY = this.berryBlock("hopo")
val IAPAPA_BERRY = this.berryBlock("iapapa")
val JABOCA_BERRY = this.berryBlock("jaboca")
val KASIB_BERRY = this.berryBlock("kasib")
val KEBIA_BERRY = this.berryBlock("kebia")
val KEE_BERRY = this.berryBlock("kee")
val KELPSY_BERRY = this.berryBlock("kelpsy")
val LANSAT_BERRY = this.berryBlock("lansat")
val LEPPA_BERRY = this.berryBlock("leppa")
val LIECHI_BERRY = this.berryBlock("liechi")
val LUM_BERRY = this.berryBlock("lum")
val MAGO_BERRY = this.berryBlock("mago")
val MAGOST_BERRY = this.berryBlock("magost")
val MARANGA_BERRY = this.berryBlock("maranga")
val MICLE_BERRY = this.berryBlock("micle")
val NANAB_BERRY = this.berryBlock("nanab")
val NOMEL_BERRY = this.berryBlock("nomel")
val OCCA_BERRY = this.berryBlock("occa")
val ORAN_BERRY = this.berryBlock("oran")
val PAMTRE_BERRY = this.berryBlock("pamtre")
val PASSHO_BERRY = this.berryBlock("passho")
val PAYAPA_BERRY = this.berryBlock("payapa")
val PECHA_BERRY = this.berryBlock("pecha")
val PERSIM_BERRY = this.berryBlock("persim")
val PETAYA_BERRY = this.berryBlock("petaya")
val PINAP_BERRY = this.berryBlock("pinap")
val POMEG_BERRY = this.berryBlock("pomeg")
val QUALOT_BERRY = this.berryBlock("qualot")
val RABUTA_BERRY = this.berryBlock("rabuta")
val RAWST_BERRY = this.berryBlock("rawst")
val RAZZ_BERRY = this.berryBlock("razz")
val RINDO_BERRY = this.berryBlock("rindo")
val ROSELI_BERRY = this.berryBlock("roseli")
val ROWAP_BERRY = this.berryBlock("rowap")
val SALAC_BERRY = this.berryBlock("salac")
val SHUCA_BERRY = this.berryBlock("shuca")
val SITRUS_BERRY = this.berryBlock("sitrus")
val SPELON_BERRY = this.berryBlock("spelon")
val STARF_BERRY = this.berryBlock("starf")
val TAMATO_BERRY = this.berryBlock("tamato")
val TANGA_BERRY = this.berryBlock("tanga")
val TOUGA_BERRY = this.berryBlock("touga")
val WACAN_BERRY = this.berryBlock("wacan")
val WATMEL_BERRY = this.berryBlock("watmel")
val WEPEAR_BERRY = this.berryBlock("wepear")
val WIKI_BERRY = this.berryBlock("wiki")
val YACHE_BERRY = this.berryBlock("yache")
init {
/**
* Makes all blocks in array flammable by adding them to FireBlock's flammableRegistry.
* second value is burn chance and third value is spread chance
*/
arrayOf(
Triple(APRICORN_LOG, 5, 5),
Triple(STRIPPED_APRICORN_LOG, 5, 5),
Triple(APRICORN_WOOD, 5, 5),
Triple(STRIPPED_APRICORN_WOOD, 5, 5),
Triple(APRICORN_PLANKS, 5, 20),
Triple(APRICORN_LEAVES, 30, 60),
Triple(APRICORN_FENCE, 5, 20),
Triple(APRICORN_FENCE_GATE, 5, 20),
Triple(APRICORN_SLAB, 5, 20),
Triple(APRICORN_STAIRS, 5, 20)
).onEach{ data -> setFlammable(data.first, data.second, data.third) }
}
fun berries() = this.berries.toMap()
private fun berryBlock(name: String): BerryBlock {
val identifier = cobblemonResource("${name}_berry")
val block = this.create(identifier.path, BerryBlock(identifier, AbstractBlock.Settings.copy(Blocks.WHEAT).dynamicBounds().sounds(BlockSoundGroup.CROP).strength(0.2F)))
this.berries[identifier] = block
return block
}
/**
* Calls helper method from Vanilla
* Helper method for creating logs
* copied over from Vanilla
*/
private fun log(name: String, arg: MapColor = MapColor.DIRT_BROWN, arg2: MapColor = MapColor.DIRT_BROWN): PillarBlock {
val block = BlocksInvoker.createLogBlock(arg, arg2)
return this.create(name, block)
}
/**
* Method uses generic E in order to keep the block as the same return type.
* If E is not a block then it will not be set as flammable.
* Calls Vanilla implementation of registering a flammable block.
* Mixins looks cursed but it is java's fault.
*/
private fun <E> setFlammable(block: E, burnChance: Int, spreadChance: Int): E {
if(block !is Block) return block
var fireBlock: FireBlock = Blocks.FIRE as FireBlock
//Cursed Mixin stuff
(fireBlock as FireBlockInvoker).registerNewFlammableBlock(block as Block, burnChance, spreadChance)
return block
}
private fun evolutionStoneOre(name: String) = this.create(name, ExperienceDroppingBlock(AbstractBlock.Settings.copy(Blocks.IRON_ORE), UniformIntProvider.create(1, 2)))
private fun deepslateEvolutionStoneOre(name: String) = this.create(name, ExperienceDroppingBlock(AbstractBlock.Settings.copy(Blocks.DEEPSLATE_IRON_ORE), UniformIntProvider.create(1, 2)))
@@ -369,4 +248,4 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
val block = BlocksInvoker.createLeavesBlock(BlockSoundGroup.GRASS)
return this.create(name, block)
}
}
}
@@ -44,7 +44,6 @@ object CobblemonCommands {
TestPcSlotCommand.register(dispatcher)
TestPartySlotCommand.register(dispatcher)
ClearPartyCommand.register(dispatcher)
ClearPCCommand.register(dispatcher)
PokemonRestartCommand.register(dispatcher)
}
}
@@ -8,27 +8,22 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.mojang.brigadier.arguments.ArgumentType
import kotlin.reflect.KClass
import net.minecraft.advancement.criterion.Criterion
import net.minecraft.block.ComposterBlock
import net.minecraft.command.argument.serialize.ArgumentSerializer
import net.minecraft.item.ItemConvertible
import net.minecraft.network.PacketByteBuf
import net.minecraft.network.listener.ClientPlayPacketListener
import net.minecraft.network.packet.Packet
import net.minecraft.registry.RegistryKey
import net.minecraft.registry.tag.TagKey
import net.minecraft.resource.ResourceManager
import net.minecraft.resource.ResourceReloader
import net.minecraft.resource.ResourceType
import net.minecraft.server.MinecraftServer
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.world.GameRules
import net.minecraft.world.biome.Biome
@@ -165,51 +160,6 @@ interface CobblemonImplementation {
*/
fun server(): MinecraftServer?
/**
* Handles platform independent reloading of a [JsonDataRegistry].
*
* @param registry The [JsonDataRegistry] to reload.
* @param manager The [ResourceManager] to reload from.
*/
fun <T> reloadJsonRegistry(registry: JsonDataRegistry<T>, manager: ResourceManager): HashMap<Identifier, T>
/**
* Registers an item to the [ComposterBlock].
*
* @param item The [ItemConvertible] being registered.
* @param chance The chance % of increasing the composter level, 0 to 1 expected.
*/
fun registerCompostable(item: ItemConvertible, chance: Float)
/**
* Registers a builtin resource pack.
*
* @param id The unique [Identifier] of this pack.
* @param title The title displayed in the resource pack GUI, the description is still a part of the pack metadata.
* @param activationBehaviour The [ResourcePackActivationBehaviour] for this pack.
*/
fun registerBuiltinResourcePack(id: Identifier, title: Text, activationBehaviour: ResourcePackActivationBehaviour)
}
enum class ResourcePackActivationBehaviour {
/**
* The resource pack will start disabled.
*/
NORMAL,
/**
* The resource pack will start enabled.
*/
DEFAULT_ENABLED,
/**
* The resource pack will always be enabled.
* The user can reorder it but cannot remove it.
*/
ALWAYS_ENABLED;
}
enum class ModAPI {
@@ -13,27 +13,18 @@ import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
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.item.*
import com.cobblemon.mod.common.item.battle.DireHitItem
import com.cobblemon.mod.common.item.battle.GuardSpecItem
import com.cobblemon.mod.common.item.battle.XStatItem
import com.cobblemon.mod.common.item.berry.FriendshipRaisingBerryItem
import com.cobblemon.mod.common.item.berry.HealingBerryItem
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.interactive.*
import com.cobblemon.mod.common.item.interactive.PotionItem
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.block.Block
import net.minecraft.entity.LivingEntity
import net.minecraft.block.ComposterBlock
import net.minecraft.entity.effect.StatusEffectInstance
import net.minecraft.entity.effect.StatusEffects
import net.minecraft.item.*
@@ -41,8 +32,6 @@ import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.util.Identifier
import net.minecraft.world.World
@Suppress("unused")
object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<Item>>, Item>() {
@@ -117,40 +106,40 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val CHERISH_BALL = pokeBallItem(PokeBalls.CHERISH_BALL)
@JvmField
val VIVICHOKE = compostableItem("vivichoke")
val VIVICHOKE = create("vivichoke", Item(Item.Settings()))
@JvmField
val VIVICHOKE_SEEDS = compostableItem("vivichoke_seeds", VivichokeItem(CobblemonBlocks.VIVICHOKE_SEEDS))
val VIVICHOKE_SEEDS = create("vivichoke_seeds", VivichokeItem(CobblemonBlocks.VIVICHOKE_SEEDS))
@JvmField
val RED_APRICORN = apricronItem("red", ApricornItem(CobblemonBlocks.RED_APRICORN))
val RED_APRICORN = create("red_apricorn", ApricornItem(CobblemonBlocks.RED_APRICORN))
@JvmField
val YELLOW_APRICORN = apricronItem("yellow", ApricornItem(CobblemonBlocks.YELLOW_APRICORN))
val YELLOW_APRICORN = create("yellow_apricorn", ApricornItem(CobblemonBlocks.YELLOW_APRICORN))
@JvmField
val GREEN_APRICORN = apricronItem("green", ApricornItem(CobblemonBlocks.GREEN_APRICORN))
val GREEN_APRICORN = create("green_apricorn", ApricornItem(CobblemonBlocks.GREEN_APRICORN))
@JvmField
val BLUE_APRICORN = apricronItem("blue", ApricornItem(CobblemonBlocks.BLUE_APRICORN))
val BLUE_APRICORN = create("blue_apricorn", ApricornItem(CobblemonBlocks.BLUE_APRICORN))
@JvmField
val PINK_APRICORN = apricronItem("pink", ApricornItem(CobblemonBlocks.PINK_APRICORN))
val PINK_APRICORN = create("pink_apricorn", ApricornItem(CobblemonBlocks.PINK_APRICORN))
@JvmField
val BLACK_APRICORN = apricronItem("black", ApricornItem(CobblemonBlocks.BLACK_APRICORN))
val BLACK_APRICORN = create("black_apricorn", ApricornItem(CobblemonBlocks.BLACK_APRICORN))
@JvmField
val WHITE_APRICORN = apricronItem("white", ApricornItem(CobblemonBlocks.WHITE_APRICORN))
val WHITE_APRICORN = create("white_apricorn", ApricornItem(CobblemonBlocks.WHITE_APRICORN))
@JvmField
val RED_APRICORN_SEED = apricronSeedItem("red", ApricornSeedItem(CobblemonBlocks.RED_APRICORN_SAPLING))
val RED_APRICORN_SEED = create("red_apricorn_seed", ApricornSeedItem(CobblemonBlocks.RED_APRICORN_SAPLING))
@JvmField
val YELLOW_APRICORN_SEED = apricronSeedItem("yellow", ApricornSeedItem(CobblemonBlocks.YELLOW_APRICORN_SAPLING))
val YELLOW_APRICORN_SEED = create("yellow_apricorn_seed", ApricornSeedItem(CobblemonBlocks.YELLOW_APRICORN_SAPLING))
@JvmField
val GREEN_APRICORN_SEED = apricronSeedItem("green", ApricornSeedItem(CobblemonBlocks.GREEN_APRICORN_SAPLING))
val GREEN_APRICORN_SEED = create("green_apricorn_seed", ApricornSeedItem(CobblemonBlocks.GREEN_APRICORN_SAPLING))
@JvmField
val BLUE_APRICORN_SEED = apricronSeedItem("blue", ApricornSeedItem(CobblemonBlocks.BLUE_APRICORN_SAPLING))
val BLUE_APRICORN_SEED = create("blue_apricorn_seed", ApricornSeedItem(CobblemonBlocks.BLUE_APRICORN_SAPLING))
@JvmField
val PINK_APRICORN_SEED = apricronSeedItem("pink", ApricornSeedItem(CobblemonBlocks.PINK_APRICORN_SAPLING))
val PINK_APRICORN_SEED = create("pink_apricorn_seed", ApricornSeedItem(CobblemonBlocks.PINK_APRICORN_SAPLING))
@JvmField
val BLACK_APRICORN_SEED = apricronSeedItem("black", ApricornSeedItem(CobblemonBlocks.BLACK_APRICORN_SAPLING))
val BLACK_APRICORN_SEED = create("black_apricorn_seed", ApricornSeedItem(CobblemonBlocks.BLACK_APRICORN_SAPLING))
@JvmField
val WHITE_APRICORN_SEED = apricronSeedItem("white", ApricornSeedItem(CobblemonBlocks.WHITE_APRICORN_SAPLING))
val WHITE_APRICORN_SEED = create("white_apricorn_seed", ApricornSeedItem(CobblemonBlocks.WHITE_APRICORN_SAPLING))
@JvmField
val APRICORN_LOG = blockItem("apricorn_log", CobblemonBlocks.APRICORN_LOG)
@@ -163,7 +152,13 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val APRICORN_PLANKS = blockItem("apricorn_planks", CobblemonBlocks.APRICORN_PLANKS)
@JvmField
val APRICORN_LEAVES = compostableBlockItem("apricorn_leaves", CobblemonBlocks.APRICORN_LEAVES)
val APRICORN_LEAVES = blockItem("apricorn_leaves", CobblemonBlocks.APRICORN_LEAVES).also {
val compostChance = ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE.getFloat(Items.OAK_LEAVES)
// Should always pass unless Mojang reworks leaves to no longer work in the Composter, in that case we already updated w.o doing anything
if (compostChance != ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE.defaultReturnValue()) {
ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE[it] = compostChance
}
}
@JvmField
val APRICORN_DOOR = blockItem("apricorn_door", CobblemonBlocks.APRICORN_DOOR)
@@ -225,82 +220,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField val PEAT_BLOCK = noSettingsItem("peat_block")
@JvmField val RAZOR_CLAW = noSettingsItem("razor_claw")
@JvmField val RAZOR_FANG = noSettingsItem("razor_fang")
@JvmField val AUSPICIOUS_ARMOR = heldItem("auspicious_armor")
@JvmField val MALICIOUS_ARMOR = heldItem("malicious_armor")
private val berries = mutableMapOf<Identifier, BerryItem>()
// Plants
@JvmField val ORAN_BERRY = berryItem("oran", HealingBerryItem(CobblemonBlocks.ORAN_BERRY) { CobblemonMechanics.berries.oranRestoreAmount })
@JvmField val CHERI_BERRY = berryItem("cheri", StatusCuringBerryItem(CobblemonBlocks.CHERI_BERRY, Statuses.PARALYSIS))
@JvmField val CHESTO_BERRY = berryItem("chesto", StatusCuringBerryItem(CobblemonBlocks.CHESTO_BERRY, Statuses.SLEEP))
@JvmField val PECHA_BERRY = berryItem("pecha", StatusCuringBerryItem(CobblemonBlocks.PECHA_BERRY, Statuses.POISON, Statuses.POISON_BADLY))
@JvmField val RAWST_BERRY = berryItem("rawst", StatusCuringBerryItem(CobblemonBlocks.RAWST_BERRY, Statuses.BURN))
@JvmField val ASPEAR_BERRY = berryItem("aspear", StatusCuringBerryItem(CobblemonBlocks.ASPEAR_BERRY, Statuses.FROZEN))
@JvmField val PERSIM_BERRY = berryItem("persim", VolatileCuringBerryItem(CobblemonBlocks.PERSIM_BERRY, "confusion"))
@JvmField val RAZZ_BERRY = berryItem("razz", CobblemonBlocks.RAZZ_BERRY)
@JvmField val BLUK_BERRY = berryItem("bluk", CobblemonBlocks.BLUK_BERRY)
@JvmField val NANAB_BERRY = berryItem("nanab", CobblemonBlocks.NANAB_BERRY)
@JvmField val WEPEAR_BERRY = berryItem("wepear", CobblemonBlocks.WEPEAR_BERRY)
@JvmField val PINAP_BERRY = berryItem("pinap", CobblemonBlocks.PINAP_BERRY)
@JvmField val OCCA_BERRY = berryItem("occa", CobblemonBlocks.OCCA_BERRY)
@JvmField val PASSHO_BERRY = berryItem("passho", CobblemonBlocks.PASSHO_BERRY)
@JvmField val WACAN_BERRY = berryItem("wacan", CobblemonBlocks.WACAN_BERRY)
@JvmField val RINDO_BERRY = berryItem("rindo", CobblemonBlocks.RINDO_BERRY)
@JvmField val YACHE_BERRY = berryItem("yache", CobblemonBlocks.YACHE_BERRY)
@JvmField val CHOPLE_BERRY = berryItem("chople", CobblemonBlocks.CHOPLE_BERRY)
@JvmField val KEBIA_BERRY = berryItem("kebia", CobblemonBlocks.KEBIA_BERRY)
@JvmField val SHUCA_BERRY = berryItem("shuca", CobblemonBlocks.SHUCA_BERRY)
@JvmField val COBA_BERRY = berryItem("coba", CobblemonBlocks.COBA_BERRY)
@JvmField val PAYAPA_BERRY = berryItem("payapa", CobblemonBlocks.PAYAPA_BERRY)
@JvmField val TANGA_BERRY = berryItem("tanga", CobblemonBlocks.TANGA_BERRY)
@JvmField val CHARTI_BERRY = berryItem("charti", CobblemonBlocks.CHARTI_BERRY)
@JvmField val KASIB_BERRY = berryItem("kasib", CobblemonBlocks.KASIB_BERRY)
@JvmField val HABAN_BERRY = berryItem("haban", CobblemonBlocks.HABAN_BERRY)
@JvmField val COLBUR_BERRY = berryItem("colbur", CobblemonBlocks.COLBUR_BERRY)
@JvmField val BABIRI_BERRY = berryItem("babiri", CobblemonBlocks.BABIRI_BERRY)
@JvmField val CHILAN_BERRY = berryItem("chilan", CobblemonBlocks.CHILAN_BERRY)
@JvmField val ROSELI_BERRY = berryItem("roseli", CobblemonBlocks.ROSELI_BERRY)
@JvmField val LEPPA_BERRY = berryItem("leppa", PPRestoringBerryItem(CobblemonBlocks.LEPPA_BERRY) { CobblemonMechanics.berries.ppRestoreAmount })
@JvmField val LUM_BERRY = berryItem("lum", StatusCuringBerryItem(CobblemonBlocks.LUM_BERRY))
@JvmField val FIGY_BERRY = berryItem("figy", PortionHealingBerryItem(CobblemonBlocks.FIGY_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
@JvmField val WIKI_BERRY = berryItem("wiki", PortionHealingBerryItem(CobblemonBlocks.WIKI_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
@JvmField val MAGO_BERRY = berryItem("mago", PortionHealingBerryItem(CobblemonBlocks.MAGO_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
@JvmField val AGUAV_BERRY = berryItem("aguav", PortionHealingBerryItem(CobblemonBlocks.AGUAV_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
@JvmField val IAPAPA_BERRY = berryItem("iapapa", PortionHealingBerryItem(CobblemonBlocks.IAPAPA_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
@JvmField val SITRUS_BERRY = berryItem("sitrus", HealingBerryItem(CobblemonBlocks.SITRUS_BERRY) { CobblemonMechanics.berries.sitrusHealAmount })
@JvmField val TOUGA_BERRY = berryItem("touga", CobblemonBlocks.TOUGA_BERRY)
@JvmField val CORNN_BERRY = berryItem("cornn", CobblemonBlocks.CORNN_BERRY)
@JvmField val MAGOST_BERRY = berryItem("magost", CobblemonBlocks.MAGOST_BERRY)
@JvmField val RABUTA_BERRY = berryItem("rabuta", CobblemonBlocks.RABUTA_BERRY)
@JvmField val NOMEL_BERRY = berryItem("nomel", CobblemonBlocks.NOMEL_BERRY)
@JvmField val ENIGMA_BERRY = berryItem("enigma", CobblemonBlocks.ENIGMA_BERRY)
@JvmField val POMEG_BERRY = berryItem("pomeg", FriendshipRaisingBerryItem(CobblemonBlocks.POMEG_BERRY, Stats.HP))
@JvmField val KELPSY_BERRY = berryItem("kelpsy", FriendshipRaisingBerryItem(CobblemonBlocks.KELPSY_BERRY, Stats.ATTACK))
@JvmField val QUALOT_BERRY = berryItem("qualot", FriendshipRaisingBerryItem(CobblemonBlocks.QUALOT_BERRY, Stats.DEFENCE))
@JvmField val HONDEW_BERRY = berryItem("hondew", FriendshipRaisingBerryItem(CobblemonBlocks.HONDEW_BERRY, Stats.SPECIAL_ATTACK))
@JvmField val GREPA_BERRY = berryItem("grepa", FriendshipRaisingBerryItem(CobblemonBlocks.GREPA_BERRY, Stats.SPECIAL_DEFENCE))
@JvmField val TAMATO_BERRY = berryItem("tamato", FriendshipRaisingBerryItem(CobblemonBlocks.TAMATO_BERRY, Stats.SPEED))
@JvmField val SPELON_BERRY = berryItem("spelon", CobblemonBlocks.SPELON_BERRY)
@JvmField val PAMTRE_BERRY = berryItem("pamtre", CobblemonBlocks.PAMTRE_BERRY)
@JvmField val WATMEL_BERRY = berryItem("watmel", CobblemonBlocks.WATMEL_BERRY)
@JvmField val DURIN_BERRY = berryItem("durin", CobblemonBlocks.DURIN_BERRY)
@JvmField val BELUE_BERRY = berryItem("belue", CobblemonBlocks.BELUE_BERRY)
@JvmField val KEE_BERRY = berryItem("kee", CobblemonBlocks.KEE_BERRY)
@JvmField val MARANGA_BERRY = berryItem("maranga", CobblemonBlocks.MARANGA_BERRY)
@JvmField val HOPO_BERRY = berryItem("hopo", PPRestoringBerryItem(CobblemonBlocks.HOPO_BERRY) { CobblemonMechanics.berries.ppRestoreAmount })
@JvmField val LIECHI_BERRY = berryItem("liechi", CobblemonBlocks.LIECHI_BERRY)
@JvmField val GANLON_BERRY = berryItem("ganlon", CobblemonBlocks.GANLON_BERRY)
@JvmField val SALAC_BERRY = berryItem("salac", CobblemonBlocks.SALAC_BERRY)
@JvmField val PETAYA_BERRY = berryItem("petaya", CobblemonBlocks.PETAYA_BERRY)
@JvmField val APICOT_BERRY = berryItem("apicot", CobblemonBlocks.APICOT_BERRY)
@JvmField val LANSAT_BERRY = berryItem("lansat", CobblemonBlocks.LANSAT_BERRY)
@JvmField val STARF_BERRY = berryItem("starf", CobblemonBlocks.STARF_BERRY)
@JvmField val MICLE_BERRY = berryItem("micle", CobblemonBlocks.MICLE_BERRY)
@JvmField val CUSTAP_BERRY = berryItem("custap", CobblemonBlocks.CUSTAP_BERRY)
@JvmField val JABOCA_BERRY = berryItem("jaboca", CobblemonBlocks.JABOCA_BERRY)
@JvmField val ROWAP_BERRY = berryItem("rowap", CobblemonBlocks.ROWAP_BERRY)
@JvmField val BERRY_JUICE = this.create("berry_juice", BerryJuiceItem())
// ToDo enable me when malicious armor goes in the game
//@JvmField val AUSPICIOUS_ARMOR = heldItem("auspicious_armor")
// Medicine
@JvmField
@@ -332,26 +253,22 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val ROASTED_LEEK = create("roasted_leek", Item(Item.Settings().food(FoodComponent.Builder().snack().hunger(3).saturationModifier(0.3f).build())))
@JvmField
val BRAISED_VIVICHOKE = create("braised_vivichoke", Item(Item.Settings().food(FoodComponent.Builder().hunger(6).saturationModifier(0.6f).build())))
val BRAISED_VIVICHOKE = create("braised_vivichoke", Item(Item.Settings().maxCount(1).food(FoodComponent.Builder().hunger(6).saturationModifier(0.6f).build())))
@JvmField
val VIVICHOKE_DIP = create("vivichoke_dip", object : StewItem(Settings().maxCount(1)
val VIVICHOKE_DIP = create("vivichoke_dip", StewItem(Item.Settings().maxCount(1)
.food(FoodComponent.Builder()
.hunger(10)
.saturationModifier(1.2F)
.statusEffect(StatusEffectInstance(StatusEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsing(stack: ItemStack?, world: World?, user: LivingEntity?): ItemStack {
user?.clearStatusEffects()
return super.finishUsing(stack, world, user)
}
})
.statusEffect(StatusEffectInstance(StatusEffects.ABSORPTION, 600, 0), 1F)
.build()
)
))
@JvmField
val ENERGY_ROOT = compostableItem("energy_root", EnergyRootItem(CobblemonBlocks.ENERGY_ROOT, Item.Settings().food(FoodComponent.Builder().hunger(1).snack().saturationModifier(0.2f).build())))
val ENERGY_ROOT = create("energy_root", EnergyRootItem(CobblemonBlocks.ENERGY_ROOT, Item.Settings().food(FoodComponent.Builder().hunger(1).snack().saturationModifier(0.2f).build())))
@JvmField
val REVIVAL_HERB = compostableItem("revival_herb", RevivalHerbItem(CobblemonBlocks.REVIVAL_HERB))
val REVIVAL_HERB = create("revival_herb", RevivalHerbItem(CobblemonBlocks.REVIVAL_HERB))
@JvmField
val PEP_UP_FLOWER = compostableBlockItem("pep_up_flower", CobblemonBlocks.PEP_UP_FLOWER)
val PEP_UP_FLOWER = blockItem("pep_up_flower", CobblemonBlocks.PEP_UP_FLOWER)
@JvmField
val MEDICINAL_BREW = create("medicinal_brew", Item(Item.Settings()))
@JvmField
@@ -386,74 +303,72 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val PP_MAX = create("pp_max", PPUpItem(3))
@JvmField
val RED_MINT_SEEDS = mintSeed("red", MintType.RED.getCropBlock())
val RED_MINT_SEEDS = blockItem("red_mint_seeds", MintType.RED.getCropBlock())
@JvmField
val RED_MINT_LEAF = mintLeaf("red", MintLeafItem(MintType.RED))
val RED_MINT_LEAF = create("red_mint_leaf", MintLeafItem(MintType.RED))
@JvmField
val BLUE_MINT_SEEDS = mintSeed("blue", MintType.BLUE.getCropBlock())
val BLUE_MINT_SEEDS = blockItem("blue_mint_seeds", MintType.BLUE.getCropBlock())
@JvmField
val BLUE_MINT_LEAF = mintLeaf("blue", MintLeafItem(MintType.BLUE))
val BLUE_MINT_LEAF = create("blue_mint_leaf", MintLeafItem(MintType.BLUE))
@JvmField
val CYAN_MINT_SEEDS = mintSeed("cyan", MintType.CYAN.getCropBlock())
val CYAN_MINT_SEEDS = blockItem("cyan_mint_seeds", MintType.CYAN.getCropBlock())
@JvmField
val CYAN_MINT_LEAF = mintLeaf("cyan", MintLeafItem(MintType.CYAN))
val CYAN_MINT_LEAF = create("cyan_mint_leaf", MintLeafItem(MintType.CYAN))
@JvmField
val PINK_MINT_SEEDS = mintSeed("pink", MintType.PINK.getCropBlock())
val PINK_MINT_SEEDS = blockItem("pink_mint_seeds", MintType.PINK.getCropBlock())
@JvmField
val PINK_MINT_LEAF = mintLeaf("pink", MintLeafItem(MintType.PINK))
val PINK_MINT_LEAF = create("pink_mint_leaf", MintLeafItem(MintType.PINK))
@JvmField
val GREEN_MINT_SEEDS = mintSeed("green", MintType.GREEN.getCropBlock())
val GREEN_MINT_SEEDS = blockItem("green_mint_seeds", MintType.GREEN.getCropBlock())
@JvmField
val GREEN_MINT_LEAF = mintLeaf("green", MintLeafItem(MintType.GREEN))
val GREEN_MINT_LEAF = create("green_mint_leaf", MintLeafItem(MintType.GREEN))
@JvmField
val WHITE_MINT_SEEDS = mintSeed("white", MintType.WHITE.getCropBlock())
val WHITE_MINT_SEEDS = blockItem("white_mint_seeds", MintType.WHITE.getCropBlock())
@JvmField
val WHITE_MINT_LEAF = mintLeaf("white", MintLeafItem(MintType.WHITE))
val mints = mutableMapOf<String, MintItem>()
val WHITE_MINT_LEAF = create("white_mint_leaf", MintLeafItem(MintType.WHITE))
@JvmField
val LONELY_MINT = mintItem("lonely_mint", MintItem(Natures.LONELY))
val LONELY_MINT = create("lonely_mint", MintItem(Natures.LONELY))
@JvmField
val ADAMANT_MINT = mintItem("adamant_mint", MintItem(Natures.ADAMANT))
val ADAMANT_MINT = create("adamant_mint", MintItem(Natures.ADAMANT))
@JvmField
val NAUGHTY_MINT = mintItem("naughty_mint", MintItem(Natures.NAUGHTY))
val NAUGHTY_MINT = create("naughty_mint", MintItem(Natures.NAUGHTY))
@JvmField
val BRAVE_MINT = mintItem("brave_mint", MintItem(Natures.BRAVE))
val BRAVE_MINT = create("brave_mint", MintItem(Natures.BRAVE))
@JvmField
val BOLD_MINT = mintItem("bold_mint", MintItem(Natures.BOLD))
val BOLD_MINT = create("bold_mint", MintItem(Natures.BOLD))
@JvmField
val IMPISH_MINT = mintItem("impish_mint", MintItem(Natures.IMPISH))
val IMPISH_MINT = create("impish_mint", MintItem(Natures.IMPISH))
@JvmField
val LAX_MINT = mintItem("lax_mint", MintItem(Natures.LAX))
val LAX_MINT = create("lax_mint", MintItem(Natures.LAX))
@JvmField
val RELAXED_MINT = mintItem("relaxed_mint", MintItem(Natures.RELAXED))
val RELAXED_MINT = create("relaxed_mint", MintItem(Natures.RELAXED))
@JvmField
val MODEST_MINT = mintItem("modest_mint", MintItem(Natures.MODEST))
val MODEST_MINT = create("modest_mint", MintItem(Natures.MODEST))
@JvmField
val MILD_MINT = mintItem("mild_mint", MintItem(Natures.MILD))
val MILD_MINT = create("mild_mint", MintItem(Natures.MILD))
@JvmField
val RASH_MINT = mintItem("rash_mint", MintItem(Natures.RASH))
val RASH_MINT = create("rash_mint", MintItem(Natures.RASH))
@JvmField
val QUIET_MINT = mintItem("quiet_mint", MintItem(Natures.QUIET))
val QUIET_MINT = create("quiet_mint", MintItem(Natures.QUIET))
@JvmField
val CALM_MINT = mintItem("calm_mint", MintItem(Natures.CALM))
val CALM_MINT = create("calm_mint", MintItem(Natures.CALM))
@JvmField
val GENTLE_MINT = mintItem("gentle_mint", MintItem(Natures.GENTLE))
val GENTLE_MINT = create("gentle_mint", MintItem(Natures.GENTLE))
@JvmField
val CAREFUL_MINT = mintItem("careful_mint", MintItem(Natures.CAREFUL))
val CAREFUL_MINT = create("careful_mint", MintItem(Natures.CAREFUL))
@JvmField
val SASSY_MINT = mintItem("sassy_mint", MintItem(Natures.SASSY))
val SASSY_MINT = create("sassy_mint", MintItem(Natures.SASSY))
@JvmField
val TIMID_MINT = mintItem("timid_mint", MintItem(Natures.TIMID))
val TIMID_MINT = create("timid_mint", MintItem(Natures.TIMID))
@JvmField
val HASTY_MINT = mintItem("hasty_mint", MintItem(Natures.HASTY))
val HASTY_MINT = create("hasty_mint", MintItem(Natures.HASTY))
@JvmField
val JOLLY_MINT = mintItem("jolly_mint", MintItem(Natures.JOLLY))
val JOLLY_MINT = create("jolly_mint", MintItem(Natures.JOLLY))
@JvmField
val NAIVE_MINT = mintItem("naive_mint", MintItem(Natures.NAIVE))
val NAIVE_MINT = create("naive_mint", MintItem(Natures.NAIVE))
@JvmField
val SERIOUS_MINT = mintItem("serious_mint", MintItem(Natures.SERIOUS))
val SERIOUS_MINT = create("serious_mint", MintItem(Natures.SERIOUS))
@JvmField val X_ACCURACY = create("x_${Stats.ACCURACY.identifier.path}", XStatItem(Stats.ACCURACY))
@JvmField val X_ATTACK = create("x_${Stats.ATTACK.identifier.path}", XStatItem(Stats.ATTACK))
@@ -612,11 +527,11 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val WISE_GLASSES = heldItem("wise_glasses")
@JvmField
val MENTAL_HERB = compostableHeldItem("mental_herb")
val MENTAL_HERB = heldItem("mental_herb")
@JvmField
val MIRROR_HERB = compostableHeldItem("mirror_herb")
val MIRROR_HERB = heldItem("mirror_herb")
@JvmField
val POWER_HERB = compostableHeldItem("power_herb")
val POWER_HERB = heldItem("power_herb")
@JvmField
val WHITE_HERB = heldItem("white_herb")
@JvmField
@@ -629,18 +544,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val DESTINY_KNOT = heldItem("destiny_knot")
@JvmField
val EVERSTONE = heldItem("everstone")
@JvmField
val POWER_ANKLET = heldItem("power_anklet")
@JvmField
val POWER_BAND = heldItem("power_band")
@JvmField
val POWER_BELT = heldItem("power_belt")
@JvmField
val POWER_BRACER = heldItem("power_bracer")
@JvmField
val POWER_LENS = heldItem("power_lens")
@JvmField
val POWER_WEIGHT = heldItem("power_weight")
// Mulch
@JvmField
@@ -666,19 +569,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val POKEMON_MODEL = this.create("pokemon_model", PokemonItem())
//Keyitems
@JvmField
val PHOTODEX = photodexItem("photodex")
private fun blockItem(name: String, block: Block): BlockItem = this.create(name, BlockItem(block, Item.Settings()))
private fun noSettingsItem(name: String): CobblemonItem = this.create(name, CobblemonItem(Item.Settings()))
private fun photodexItem(name: String): PhotodexItem = this.create(name, PhotodexItem())
fun berries() = this.berries.toMap()
private fun mulchItem(name: String, mulchVariant: MulchVariant): MulchItem = this.create(name, MulchItem(mulchVariant))
private fun pokeBallItem(pokeBall: PokeBall): PokeBallItem {
@@ -699,75 +593,4 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
}
)
private fun compostable(item: Item, increaseLevelChance: Float) = Cobblemon.implementation.registerCompostable(item, increaseLevelChance)
private fun berryItem(name: String, berryBlock: BerryBlock): BerryItem {
val finalName = "${name}_berry"
val item = this.create(finalName, BerryItem(berryBlock))
compostable(item, .65f)
this.berries[cobblemonResource(finalName)] = item
return item
}
private fun berryItem(name: String, berryItem: BerryItem): BerryItem {
val finalName = "${name}_berry"
val item = this.create(finalName, berryItem)
compostable(item, .65f)
this.berries[cobblemonResource(finalName)] = item
return item
}
private fun mintItem(name: String, mintItem: MintItem): MintItem {
val item = this.create(name, mintItem)
mints[item.nature.displayName] = item
compostable(item, .65f)
return item
}
private fun apricronItem(name: String, apricornItem: ApricornItem): ApricornItem {
val finalName = "${name}_apricorn"
val item = this.create(finalName, apricornItem)
compostable(item, .65f)
return item
}
private fun apricronSeedItem(name: String, apricronSeedItem: ApricornSeedItem): ApricornSeedItem {
val finalName = "${name}_apricorn_seed"
val item = this.create(finalName, apricronSeedItem)
compostable(item, .65f)
return item
}
private fun mintSeed(name: String, mintBlock: MintBlock): Item {
val finalName = "${name}_mint_seeds"
val item = this.blockItem(finalName, mintBlock)
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, .65f)
return item
}
private fun compostableItem(name: String, item: Item? = null, increaseLevelChance: Float = .65f): Item {
val createdItem = this.create(name, item ?: Item(Item.Settings()))
compostable(createdItem, increaseLevelChance)
return createdItem
}
private fun compostableHeldItem(name: String, remappedName: String? = null, increaseLevelChance: Float = .65f): CobblemonItem {
val createdItem = heldItem(name, remappedName)
compostable(createdItem, increaseLevelChance)
return createdItem
}
private fun compostableBlockItem(name: String, block: Block, increaseLevelChance: Float = .65f): Item {
val createdItem = this.blockItem(name, block)
compostable(createdItem, increaseLevelChance)
return createdItem
}
}
}
@@ -11,8 +11,6 @@ package com.cobblemon.mod.common
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.mechanics.BerriesMechanic
import com.cobblemon.mod.common.mechanics.PotionsMechanic
import com.cobblemon.mod.common.mechanics.RemediesMechanic
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.cobblemonResource
@@ -32,19 +30,11 @@ object CobblemonMechanics : DataRegistry {
.create()
var remedies = RemediesMechanic()
var berries = BerriesMechanic()
var potions = PotionsMechanic()
override fun sync(player: ServerPlayerEntity) {}
override fun reload(manager: ResourceManager) {
remedies = loadMechanic(manager, "remedies", RemediesMechanic::class.java)
berries = loadMechanic(manager, "berries", BerriesMechanic::class.java)
potions = loadMechanic(manager, "potions", PotionsMechanic::class.java)
}
private fun <T> loadMechanic(manager: ResourceManager, name: String, clazz: Class<T>): T {
manager.getResourceOrThrow(cobblemonResource("mechanics/$name.json")).inputStream.use {
return gson.fromJson(it.reader(), clazz)
manager.getResourceOrThrow(cobblemonResource("mechanics/remedies.json")).inputStream.use {
remedies = gson.fromJson(it.reader(), RemediesMechanic::class.java)
}
}
}
@@ -13,14 +13,14 @@ import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
import com.cobblemon.mod.common.client.net.battle.*
import com.cobblemon.mod.common.client.net.callback.move.OpenMoveCallbackHandler
import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandler
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
import com.cobblemon.mod.common.client.net.callback.move.OpenMoveCallbackHandler
import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandler
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
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
@@ -56,7 +56,6 @@ import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPac
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.PropertiesCompletionRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
@@ -211,7 +210,6 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(HeldItemUpdatePacket.ID, HeldItemUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(PokemonStateUpdatePacket.ID, PokemonStateUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(TetheringUpdatePacket.ID, TetheringUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(TradeableUpdatePacket.ID, TradeableUpdatePacket::decode, PokemonUpdatePacketHandler())
// Evolution start
this.createClientBound(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler())
@@ -275,7 +273,6 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler)
this.createClientBound(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
// Effects
this.createClientBound(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler)
@@ -78,16 +78,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
@JvmField
val MEDICINE_SPRAY_USE = this.create("medicine_spray.use")
@JvmField
val BERRY_HARVEST = this.create("berry.harvest")
@JvmField
val BERRY_EAT = this.create("berry.eat")
@JvmField
val MULCH_PLACE = this.create("mulch.place")
@JvmField
val MULCH_REMOVE = this.create("mulch.remove")
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.of(cobblemonResource(name)))
}
@@ -32,7 +32,7 @@ object CobblemonTradeOffers {
* @return The resulting list.
*/
fun tradeOffersFor(profession: VillagerProfession): List<VillagerTradeOffer> = when (profession) {
VillagerProfession.FARMER -> listOf(VillagerTradeOffer(VillagerProfession.FARMER, 3, listOf(TradeOffers.SellItemFactory(CobblemonItems.VIVICHOKE_SEEDS, 24, 1, 1, 6))))
VillagerProfession.FARMER -> listOf(VillagerTradeOffer(VillagerProfession.FARMER, 3, listOf(TradeOffers.SellItemFactory(CobblemonItems.VIVICHOKE_SEEDS, 32, 1, 1, 6))))
else -> emptyList()
}
@@ -42,7 +42,7 @@ object CobblemonTradeOffers {
* @return The resulting list.
*/
fun resolveWanderingTradeOffers(): List<WandererTradeOffer> = listOf(
WandererTradeOffer(false, listOf(TradeOffers.SellItemFactory(CobblemonItems.VIVICHOKE_SEEDS, 24, 1, 1, 6)))
WandererTradeOffer(false, listOf(TradeOffers.SellItemFactory(CobblemonItems.VIVICHOKE_SEEDS, 32, 1, 1, 6)))
)
/**
@@ -47,13 +47,13 @@ object CobblemonCriteria {
val EVOLVE_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("pokemon_evolved"),
EvolvePokemonCriterionCondition::class.java
SimpleCountableCriterionCondition::class.java
)
)
val WIN_BATTLE = this.create(
BattleCountableCriterionTrigger(
SimpleCriterionTrigger(
cobblemonResource("battles_won"),
BattleCountableCriterionCondition::class.java
SimpleCountableCriterionCondition::class.java
)
)
@@ -71,41 +71,6 @@ object CobblemonCriteria {
)
)
val POKEMON_INTERACT = this.create(
SimpleCriterionTrigger(
cobblemonResource("pokemon_interact"),
PokemonInteractCriterion::class.java
)
)
val PARTY_CHECK = this.create(
SimpleCriterionTrigger(
cobblemonResource("party"),
PartyCheckCriterion::class.java
)
)
val LEVEL_UP = this.create(
SimpleCriterionTrigger(
cobblemonResource("level_up"),
LevelUpCriterionCondition::class.java
)
)
val PASTURE_USE = this.create(
SimpleCriterionTrigger(
cobblemonResource("pasture_use"),
PickStarterCriterionCondition::class.java
)
)
val TRADE_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("trade_pokemon"),
TradePokemonCriterionCondition::class.java
)
)
private fun <T : Criterion<*>> create(criteria: T): T = Cobblemon.implementation.registerCriteria(criteria)
}
@@ -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.advancement.criterion
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.util.toJsonArray
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
open class BattleCountableCriterionTrigger(identifier: Identifier, criterionClass: Class<BattleCountableCriterionCondition>) : SimpleCriterionTrigger<BattleCountableContext, BattleCountableCriterionCondition>(identifier, criterionClass) {
}
class BattleCountableCriterionCondition(id: Identifier, predicate: LootContextPredicate) : CountableCriterionCondition<BattleCountableContext>(id, predicate) {
private var battleTypes = mutableListOf("any")
override fun fromJson(json: JsonObject) {
super.fromJson(json)
if(!json.get("battle_types").isJsonNull) {
battleTypes.clear()
json.get("battle_types").asJsonArray.asList().forEach() {
battleTypes.add(it.asString)
}
}
}
override fun toJson(json: JsonObject) {
super.toJson(json)
json.add("battle_types", battleTypes.toJsonArray())
}
override fun matches(player: ServerPlayerEntity, context: BattleCountableContext): Boolean {
var typeCheck = false
val advancementData = Cobblemon.playerData.get(player).advancementData
if (battleTypes.isEmpty() || battleTypes.contains("any")) {
typeCheck = true
}
if (battleTypes.contains("pvp")) {
typeCheck = context.battle.isPvP
context.times = advancementData.totalPvPBattleVictoryCount
}
if (battleTypes.contains("pvw")) {
typeCheck = context.battle.isPvW
context.times = advancementData.totalPvWBattleVictoryCount
}
if (battleTypes.contains("pvn")) {
typeCheck = context.battle.isPvN
context.times = advancementData.totalPvWBattleVictoryCount
}
if (battleTypes.size > 1) {
context.times = advancementData.totalBattleVictoryCount
}
return context.times >= count && typeCheck
}
}
open class BattleCountableContext(times : Int, var battle : PokemonBattle) : CountableContext(times)
@@ -34,6 +34,6 @@ class CaughtPokemonCriterionCondition(id: Identifier, predicate: LootContextPred
}
override fun matches(player: ServerPlayerEntity, context: CountablePokemonTypeContext): Boolean {
return super.matches(player, context) && (context.type == type || type == "any")
return super.matches(player, context) && context.type == type
}
}
@@ -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.advancement.criterion
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
class EvolvePokemonCriterionCondition(id: Identifier, entity: LootContextPredicate) : CountableCriterionCondition<EvolvePokemonContext>(id, entity) {
var species = "any"
var evolution = "any"
override fun toJson(json: JsonObject) {
super.toJson(json)
json.addProperty("species", species)
json.addProperty("evolution", evolution)
}
override fun fromJson(json: JsonObject) {
super.fromJson(json)
species = json.get("species")?.asString ?: "any"
evolution = json.get("evolution")?.asString ?: "any"
}
override fun matches(player: ServerPlayerEntity, context: EvolvePokemonContext): Boolean {
return context.times >= count && (context.species == species.asIdentifierDefaultingNamespace() || species == "any") &&
(context.evolution == evolution.asIdentifierDefaultingNamespace() || evolution == "any")
}
}
open class EvolvePokemonContext(val species : Identifier, val evolution : Identifier, times: Int) : CountableContext(times)
@@ -1,41 +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.advancement.criterion
import com.cobblemon.mod.common.pokemon.Pokemon
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
class LevelUpCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<LevelUpContext>(id, entity) {
var level = 0
var evolved = true
override fun toJson(json: JsonObject) {
json.addProperty("level", level)
json.addProperty("has_evolved", evolved)
}
override fun fromJson(json: JsonObject) {
level = json.get("level")?.asInt ?: 0
evolved = json.get("has_evolved")?.asBoolean ?: true
}
override fun matches(player: ServerPlayerEntity, context: LevelUpContext): Boolean {
val preEvo = context.pokemon.preEvolution == null
val hasEvolution = !context.pokemon.evolutions.none()
var evolutionCheck = true
if (preEvo || hasEvolution) {
evolutionCheck = !(preEvo == hasEvolution)
}
return level == context.level && evolutionCheck == evolved
}
}
open class LevelUpContext(var level : Int, var pokemon : Pokemon)
@@ -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.advancement.criterion
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.party
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
class PartyCheckCriterion(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<PartyCheckContext>(id, entity){
val party = mutableListOf<Identifier>()
override fun toJson(json: JsonObject) {
json.add("party", JsonArray(party.size).also {
party.forEach { pokemon -> it.add(pokemon.toString()) }
})
}
override fun fromJson(json: JsonObject) {
party.clear()
json.getAsJsonArray("party").forEach { element ->
party.add(element.asString.asIdentifierDefaultingNamespace())
}
}
override fun matches(player: ServerPlayerEntity, context: PartyCheckContext): Boolean {
val playerParty = player.party()
val matches = mutableListOf<Identifier>()
party.forEach {
if (it == "any".asIdentifierDefaultingNamespace()) {
matches.add(it)
}
}
val partyCount = playerParty.count()
if (matches.containsAll(party) && party.size == partyCount && matches.size == partyCount) return true
playerParty.iterator().forEach {
if (party.contains(it.species.resourceIdentifier)) {
matches.add(it.species.resourceIdentifier)
}
}
return matches.containsAll(party) && matches.size == partyCount
}
}
open class PartyCheckContext(val party : PlayerPartyStore)
@@ -1,34 +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.advancement.criterion
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
open class PokemonInteractContext(var type: Identifier, var item : Identifier)
class PokemonInteractCriterion(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<PokemonInteractContext>(id, entity) {
var type = "any"
var item = "any"
override fun toJson(json: JsonObject) {
json.addProperty("type", type)
json.addProperty("item", item)
}
override fun fromJson(json: JsonObject) {
type = json.get("type")?.asString ?: "any"
item = json.get("item")?.asString ?: "any"
}
override fun matches(player: ServerPlayerEntity, context: PokemonInteractContext): Boolean {
return (context.type == type.asIdentifierDefaultingNamespace() || type == "any") && (context.item == item.asIdentifierDefaultingNamespace() || item == "any")
}
}
@@ -1,47 +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.advancement.criterion
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
class TradePokemonCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<TradePokemonContext>(id, entity) {
var traded = "any"
var received = "any"
var tradedHeldItem = "any"
var receivedHeldItem = "any"
override fun toJson(json: JsonObject) {
json.addProperty("traded", traded)
json.addProperty("received", received)
json.addProperty("traded_held_item", tradedHeldItem)
json.addProperty("received_held_item", receivedHeldItem)
}
override fun fromJson(json: JsonObject) {
traded = json.get("traded")?.asString ?: "any"
received = json.get("received")?.asString ?: "any"
tradedHeldItem = json.get("traded_held_item")?.asString ?: "minecraft:air"
receivedHeldItem = json.get("received_held_item")?.asString ?: "minecraft:air"
}
override fun matches(player: ServerPlayerEntity, context: TradePokemonContext): Boolean {
val heldItem1 = context.traded.heldItem().item.registryEntry.registryKey().value
val heldItem2 = context.received.heldItem().item.registryEntry.registryKey().value
return (context.traded.species.resourceIdentifier == traded.asIdentifierDefaultingNamespace() || traded == "any") &&
(context.received.species.resourceIdentifier == received.asIdentifierDefaultingNamespace() || received == "any") &&
(heldItem1 == tradedHeldItem.asIdentifierDefaultingNamespace() || heldItem1 == "minecraft:air".asIdentifierDefaultingNamespace()) &&
(heldItem2 == receivedHeldItem.asIdentifierDefaultingNamespace() || heldItem2 == "minecraft:air".asIdentifierDefaultingNamespace())
}
}
open class TradePokemonContext(val traded: Pokemon, val received: Pokemon)
@@ -35,7 +35,7 @@ interface SleepDepth {
override fun shouldWake(pokemonEntity: PokemonEntity): Boolean {
val nearbyPlayers = pokemonEntity.world.getPlayers(TargetPredicate.createNonAttackable(), pokemonEntity, Box.of(pokemonEntity.pos, 16.0, 16.0, 16.0))
return nearbyPlayers.any { !it.isSneaking }
return nearbyPlayers.none { !it.isSneaking }
}
}
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.api.battles.interpreter
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import java.util.UUID
@@ -196,11 +194,6 @@ class BattleMessage(rawMessage: String) {
return Effect.parse(data)
}
fun moveAt(index: Int): MoveTemplate? {
val argument = argumentAt(index)?.lowercase()?.replace("[^a-z0-9]".toRegex(), "") ?: return null
return Moves.getByName(argument)
}
/**
* Queries an optional argument with given [argumentName] for a 'pnx' that will be parsed into a [BattleActor] and [ActiveBattlePokemon].
*
@@ -34,20 +34,22 @@ import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
import com.cobblemon.mod.common.battles.interpreter.ContextManager
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.net.messages.client.battle.BattleEndPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleMusicPacket
import com.cobblemon.mod.common.pokemon.evolution.progress.DefeatEvolutionProgress
import com.cobblemon.mod.common.pokemon.evolution.requirements.DefeatRequirement
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.getPlayer
import java.io.File
import net.minecraft.entity.Entity
import java.util.UUID
import java.util.concurrent.ConcurrentLinkedQueue
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import java.io.File
import kotlin.io.path.Path
import kotlin.io.path.writeLines
import net.minecraft.server.network.ServerPlayerEntity
/**
* Individual battle instance
@@ -60,7 +62,7 @@ open class PokemonBattle(
val side1: BattleSide,
val side2: BattleSide
) {
/** Whether logging will be silenced for this battle. */
/** Whether or not logging will be silenced for this battle. */
var mute = true
init {
@@ -209,16 +211,11 @@ open class PokemonBattle(
faintedPokemons.forEach { faintedPokemon ->
for (opponentPokemon in opponentNonFaintedPokemons) {
val facedFainted = opponentPokemon.facedOpponents.contains(faintedPokemon)
val defeatProgress = DefeatEvolutionProgress()
val pokemon = opponentPokemon.effectedPokemon
if (facedFainted) {
pokemon.evolutions.forEach { evolution ->
evolution.requirements.filterIsInstance<DefeatRequirement>().forEach { defeatRequirement ->
if (defeatRequirement.target.matches(faintedPokemon.effectedPokemon)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DefeatEvolutionProgress && it.currentProgress().target == defeatRequirement.target }) { DefeatEvolutionProgress() }
progress.updateProgress(DefeatEvolutionProgress.Progress(defeatRequirement.target, progress.currentProgress().amount + 1))
}
}
}
if (facedFainted && defeatProgress.shouldKeep(pokemon)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DefeatEvolutionProgress && it.currentProgress().target.matches(faintedPokemon.effectedPokemon) }) { defeatProgress }
progress.updateProgress(DefeatEvolutionProgress.Progress(progress.currentProgress().target, progress.currentProgress().amount + 1))
}
val multiplier = when {
// ToDo when Exp. All is implement if enabled && !facedFainted return 2.0, probably should be a configurable value too, this will have priority over the Exp. Share
@@ -26,20 +26,13 @@ abstract class AIBattleActor(
super.sendUpdate(packet)
if (packet is BattleMakeChoicePacket) {
this.onChoiceRequested()
}
}
/**
* Called when the AI is requested to make a choice.
*/
open fun onChoiceRequested() {
try {
setActionResponses(request!!.iterate(this.activePokemon, battleAI::choose))
} catch (exception: IllegalActionChoiceException) {
LOGGER.error("AI was unable to choose a move, we're going to need to pass!")
exception.printStackTrace()
setActionResponses(request!!.iterate(this.activePokemon) { _, _, _ -> PassActionResponse })
try {
setActionResponses(request!!.iterate(this.activePokemon, battleAI::choose))
} catch (exception: IllegalActionChoiceException) {
LOGGER.error("AI was unable to choose a move, we're going to need to pass!")
exception.printStackTrace()
setActionResponses(request!!.iterate(this.activePokemon) { _, _, _ -> PassActionResponse })
}
}
}
}
@@ -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.api.berry
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.berry.spawncondition.BerrySpawnCondition
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.status.Status
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.adapters.CobblemonStatTypeAdapter
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import java.awt.Color
import net.minecraft.predicate.NumberRange
import net.minecraft.registry.RegistryKeys
import net.minecraft.registry.tag.TagKey
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.math.Box
import net.minecraft.util.math.Vec3d
import net.minecraft.world.biome.Biome
/**
* The data registry for [Berry].
*
* @author Licious
* @since November 28th, 2022
*/
object Berries : JsonDataRegistry<Berry> {
override val id: Identifier = cobblemonResource("berries")
override val type: ResourceType = ResourceType.SERVER_DATA
override val observable = SimpleObservable<Berries>()
override val gson = GsonBuilder()
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(MulchVariant::class.java, MulchVariantAdapter)
.registerTypeAdapter(NumberRange.FloatRange::class.java, FloatNumberRangeAdapter)
.registerTypeAdapter(Status::class.java, StatusAdapter)
.registerTypeAdapter(TypeToken.getParameterized(Collection::class.java, Box::class.java).type, BoxCollectionAdapter)
.registerTypeAdapter(Box::class.java, BoxAdapter)
.registerTypeAdapter(Vec3d::class.java, VerboseVec3dAdapter)
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.registerTypeAdapter(GrowthFactor::class.java, CobblemonGrowthFactorAdapter)
.registerTypeAdapter(IntRange::class.java, VerboseIntRangeAdapter)
.registerTypeAdapter(Color::class.java, LiteralHexColorAdapter)
.registerTypeAdapter(Stat::class.java, CobblemonStatTypeAdapter)
.registerTypeAdapter(TypeToken.getParameterized(TagKey::class.java, Biome::class.java).type, TagKeyAdapter(RegistryKeys.BIOME))
.registerTypeAdapter(BerrySpawnCondition::class.java, CobblemonBerrySpawnConditionAdapter)
.create()
override val typeToken: TypeToken<Berry> = TypeToken.get(Berry::class.java)
override val resourcePath = "berries"
private val berries = hashMapOf<Identifier, Berry>()
override fun reload(data: Map<Identifier, Berry>) {
this.berries.clear()
data.forEach { (identifier, berry) ->
try {
berry.identifier = identifier
berry.validate()
this.berries[identifier] = berry
} catch (e: Exception) {
Cobblemon.LOGGER.error("Skipped loading the {} berry", identifier, e)
}
}
Cobblemon.LOGGER.info("Loaded {} berries", this.berries.size)
this.observable.emit(this)
}
override fun sync(player: ServerPlayerEntity) {
BerryRegistrySyncPacket(this.all()).sendToPlayer(player)
}
fun all() = this.berries.values.toList()
/**
* Gets a berry if loaded.
*
* @param identifier The identifier of the berry.
* @return The [Berry] if loaded otherwise null.
*/
fun getByIdentifier(identifier: Identifier): Berry? = this.berries[identifier]
/**
* Gets a berry if loaded.
*
* @param name The path of the identifier of the berry under the [Cobblemon.MODID] namespace.
* @return The [Berry] if loaded otherwise null.
*/
fun getByName(name: String): Berry? = this.getByIdentifier(cobblemonResource(name))
}
@@ -1,347 +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.berry
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.berry.spawncondition.BerrySpawnCondition
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.pokemon.Nature
import com.cobblemon.mod.common.util.readBox
import com.cobblemon.mod.common.util.writeBox
import com.google.gson.annotations.SerializedName
import java.awt.Color
import java.util.EnumSet
import kotlin.math.min
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.client.model.ModelPart
import net.minecraft.entity.LivingEntity
import net.minecraft.network.PacketByteBuf
import net.minecraft.registry.tag.TagKey
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
import net.minecraft.util.math.Vec3d
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.World
import net.minecraft.world.biome.Biome
/**
* Represents the data behind a berry.
*
* @property identifier The [Identifier] of this berry.
* @property baseYield The [IntRange] possible for the berry tree before [bonusYield] is calculated.
* @property preferredBiomeTags The [TagKey]s of the berries preffered biomes. Determines spawning and yield
* @property growthTime The [IntRange] possible in minutes for how long the berry tree takes to grow.
* @property refreshRate The [IntRange] possible in minutes for how long the berry tree takes to regrow its berries once it has been harvested.
* @depreciated @property lifeCycles The [IntRange] possible for the berry to live for between harvests.
* @property favoriteMulches The types of mulches that benefit this berries yield
* @property growthFactors An array of [GrowthFactor]s that will affect this berry. The client is not aware of these.
* @property mutations A map of the partner berry as the key and the value as the resulting mutation with this berry.
* @property growthPoints A collection of [GrowthPoint]s for the berry flowers and fruit.
* @property sproutShapeBoxes A collection of [Box]es that make up the tree [VoxelShape] during the sprouting stages.
* @property matureShapeBoxes A collection of [Box]es that make up the tree [VoxelShape] during the mature stages.
* @property flavors The [Flavor] values.
* @property tintIndexes Determines tints at specific indexes if any.
* @property flowerModelIdentifier The [Identifier] for the model of the berry in flower form.
* @property flowerTexture The [Identifier] for the texture of the berry in flower form. This is resolved into a [ModelPart] on the client.
* @property fruitModelIdentifier The [Identifier] for the model of the berry in flower form. This is resolved into a [ModelPart] on the client.
* @property fruitTexture The [Identifier] for the texture of the berry in flower form.
*
* @throws IllegalArgumentException if the any yield range argument is not a positive range.
*/
class Berry(
identifier: Identifier,
val baseYield: IntRange,
val preferredBiomeTags: List<TagKey<Biome>>,
val growthTime: IntRange,
val refreshRate: IntRange,
//val lifeCycles: IntRange,
val favoriteMulches: EnumSet<MulchVariant>,
val growthFactors: Collection<GrowthFactor>,
val spawnConditions: List<BerrySpawnCondition>,
var growthPoints: Array<GrowthPoint>,
val randomizedGrowthPoints: Boolean = true,
val mutations: Map<Identifier, Identifier>,
@SerializedName("sproutShape")
private val sproutShapeBoxes: Collection<Box>,
@SerializedName("matureShape")
private val matureShapeBoxes: Collection<Box>,
private val flavors: Map<Flavor, Int>,
val tintIndexes: Map<Int, Color>,
@SerializedName("flowerModel")
val flowerModelIdentifier: Identifier,
val flowerTexture: Identifier,
@SerializedName("fruitModel")
val fruitModelIdentifier: Identifier,
val fruitTexture: Identifier,
val weight: Float
) {
@Transient
var identifier: Identifier = identifier
internal set
@Transient
private lateinit var shapedFlower: HashMap<Int, VoxelShape>
@Transient
private lateinit var shapedFruit: HashMap<Int, VoxelShape>
/**
* The [VoxelShape] of the tree during the sprouting stages.
*/
@Transient
lateinit var sproutShape: VoxelShape
private set
/**
* The [VoxelShape] of the tree during the mature stages.
*/
@Transient
lateinit var matureShape: VoxelShape
private set
init {
this.validate()
}
/**
* Finds the [BerryItem] that uses this berry for data.
*
* @return The [BerryItem] if existing.
*/
fun item(): BerryItem? = CobblemonItems.berries()[this.identifier]
/**
* Finds the [BerryBlock] that uses this berry for data.
*
* @return The [BerryBlock] if existing.
*/
fun block(): BerryBlock? = CobblemonBlocks.berries()[this.identifier]
/**
* Query the value of a certain flavor.
*
* @param flavor The [Flavor] being queried.
* @return The value if any or 0.
*/
fun flavor(flavor: Flavor): Int = this.flavors[flavor] ?: 0
fun dislikedBy(nature: Nature): Boolean {
val dislikedFlavor = nature.dislikedFlavor ?: return false
return flavor(dislikedFlavor) > 0
}
/**
* Calculates the yield for a berry tree being planted or replanted after a life cycle.
* Triggers [BerryYieldCalculationEvent].
*
* @param world The [World] the tree is present in.
* @param state The [BlockState] of the tree.
* @param pos The [BlockPos] of the tree.
* @param placer The [LivingEntity] tending to the tree, if any.
* @return The total berry stack count.
*/
fun calculateYield(world: World, state: BlockState, pos: BlockPos, placer: LivingEntity? = null): Int {
val base = this.baseYield.random()
val bonus = this.bonusYield(world, state, pos)
var yield = base + bonus.first
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
if (BerryBlock.getMulch(state) == MulchVariant.RICH) {
yield = min(yield + 1, maxYield())
treeEntity.decrementMulchDuration(world, pos, state)
}
val event = BerryYieldCalculationEvent(this, world, state, pos, placer, yield, bonus.second)
CobblemonEvents.BERRY_YIELD.post(event) { yield = it.yield }
return yield
}
/**
* Calculates and returns the minimum possible yield by summing [baseYield] and [growthFactors] minimum values.
*
* @return The minimum possible yield.
*/
fun minYield() = this.baseYield.first + this.growthFactors.sumOf { it.minYield() }
/**
* Calculates and returns the maximum possible yield by summing [baseYield] and [growthFactors] max values.
*
* @return The maximum possible yield.
*/
fun maxYield() = this.baseYield.last + this.growthFactors.sumOf { it.maxYield() }
/**
* Checks if this berry can mutate with the given [partner].
* For the berry the mutations may produce use [mutationWith].
*
* @param partner The [Berry] being checked as a possible mutation partner.
* @return If a mutation is possible.
*/
fun canMutateWith(partner: Berry) = this.mutationWith(partner) != null
/**
* Query for the possible mutation with the given [partner].
*
* @param partner The [Berry] being mutated with.
* @return The resulting [Berry] if the mutation is possible.
*/
fun mutationWith(partner: Berry): Berry? {
val berryIdentifier = this.mutations[partner.identifier] ?: return null
return Berries.getByIdentifier(berryIdentifier)
}
/**
* Query for a possible partner to mutate into the given [resulting].
*
* @param resulting The [Berry] the mutation should result in.
* @return The [Berry] that mutates with this berry into the given [resulting] if possible.
*/
fun partnerForMutation(resulting: Berry): Berry? {
return this.mutations.firstNotNullOfOrNull { (partner, result) ->
if (result == resulting.identifier)
Berries.getByIdentifier(partner)
else
null
}
}
// A cheat since gson doesn't invoke init block
internal fun validate() {
if (this.baseYield.first < 0 || this.baseYield.last < 0) {
throw IllegalArgumentException("A berry base yield must be a positive range")
}
if (this.growthTime.first < 0 || this.growthTime.last < 0) {
throw IllegalArgumentException("The growth time must be a positive range")
}
if (this.refreshRate.first < 0 || this.refreshRate.last < 0) {
throw IllegalArgumentException("The refresh rate must be a positive range")
}
/*
if (this.lifeCycles.first < 0 || this.lifeCycles.last < 0) {
throw IllegalArgumentException("A berry life cycle must be a positive range")
}
*/
this.growthFactors.forEach { it.validateArguments() }
val maxYield = this.maxYield()
if (this.growthPoints.size < maxYield) {
throw IllegalArgumentException("Anchor points must have enough elements for the max possible yield of $maxYield you've provided ${this.growthPoints.size} points")
}
this.shapedFlower = hashMapOf()
this.shapedFruit = hashMapOf()
this.sproutShape = this.createAndUniteShapes(this.sproutShapeBoxes)
this.matureShape = this.createAndUniteShapes(this.matureShapeBoxes)
}
internal fun encode(buffer: PacketByteBuf) {
buffer.writeIdentifier(this.identifier)
buffer.writeInt(this.baseYield.first)
buffer.writeInt(this.baseYield.last)
buffer.writeEnumSet(favoriteMulches, MulchVariant::class.java)
//buffer.writeInt(this.lifeCycles.first)
//buffer.writeInt(this.lifeCycles.last)
buffer.writeInt(this.growthTime.first)
buffer.writeInt(this.growthTime.last)
buffer.writeInt(this.refreshRate.first)
buffer.writeInt(this.refreshRate.last)
buffer.writeCollection(this.growthPoints.toList()) { writer, value ->
writer.writeDouble(value.position.x)
writer.writeDouble(value.position.y)
writer.writeDouble(value.position.z)
writer.writeDouble(value.rotation.x)
writer.writeDouble(value.rotation.y)
writer.writeDouble(value.rotation.z)
}
buffer.writeBoolean(this.randomizedGrowthPoints)
buffer.writeMap(this.mutations, { writer, key -> writer.writeIdentifier(key) }, { writer, value -> writer.writeIdentifier(value) })
buffer.writeCollection(this.sproutShapeBoxes) { writer, value ->
writer.writeBox(value)
}
buffer.writeCollection(this.matureShapeBoxes) { writer, value ->
writer.writeBox(value)
}
buffer.writeMap(this.flavors, { writer, key -> writer.writeEnumConstant(key) }, { writer, value -> writer.writeInt(value) })
buffer.writeMap(this.tintIndexes, { writer, key -> writer.writeInt(key) }, { writer, value -> writer.writeInt(value.rgb) })
buffer.writeIdentifier(this.flowerModelIdentifier)
buffer.writeIdentifier(this.flowerTexture)
buffer.writeIdentifier(this.fruitModelIdentifier)
buffer.writeIdentifier(this.fruitTexture)
}
/**
* Calculates the bonus yield for the berry tree.
*
* @param world The [World] the tree is present in.
* @param state The [BlockState] of the tree.
* @param pos The [BlockPos] of the tree.
* @return The bonus yield, the growth factors that passed.
*/
private fun bonusYield(world: World, state: BlockState, pos: BlockPos): Pair<Int, Collection<GrowthFactor>> {
var bonus = 0
val passed = arrayListOf<GrowthFactor>()
val mulchVariant = BerryBlock.getMulch(state)
val hasBiomeMulch = mulchVariant in favoriteMulches
this.growthFactors.forEach { factor ->
if (factor.isValid(world, state, pos)) {
bonus += factor.yield()
passed += factor
} else if (hasBiomeMulch) {
bonus += factor.yield()
}
}
return bonus to passed
}
private fun createAndUniteShapes(boxes: Collection<Box>): VoxelShape {
var shape: VoxelShape? = null
boxes.forEach { box ->
shape = if (shape == null) {
Block.createCuboidShape(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ)
} else {
VoxelShapes.union(shape, Block.createCuboidShape(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ))
}
}
return shape ?: VoxelShapes.fullCube()
}
companion object {
internal fun decode(buffer: PacketByteBuf): Berry {
val identifier = buffer.readIdentifier()
val baseYield = IntRange(buffer.readInt(), buffer.readInt())
val favMulchs = buffer.readEnumSet(MulchVariant::class.java)
//val lifeCycles = IntRange(buffer.readInt(), buffer.readInt())
val growthTime = IntRange(buffer.readInt(), buffer.readInt())
val refreshRate = IntRange(buffer.readInt(), buffer.readInt())
val growthPoints = buffer.readList { reader ->
GrowthPoint(Vec3d(reader.readDouble(), reader.readDouble(), reader.readDouble()), Vec3d(reader.readDouble(), reader.readDouble(), reader.readDouble()))
}.toTypedArray()
val randomizedGrowthPoints = buffer.readBoolean()
val mutations = buffer.readMap({ reader -> reader.readIdentifier() }, { reader -> reader.readIdentifier() })
val sproutShapeBoxes = buffer.readList { it.readBox() }
val matureShapeBoxes = buffer.readList { it.readBox() }
val flavors = buffer.readMap({ reader -> reader.readEnumConstant(Flavor::class.java) }, { reader -> reader.readInt() })
val tintIndexes = buffer.readMap({ reader -> reader.readInt() }, { reader -> Color(reader.readInt()) })
val flowerModelIdentifier = buffer.readIdentifier()
val flowerTexture = buffer.readIdentifier()
val fruitModelIdentifier = buffer.readIdentifier()
val fruitTexture = buffer.readIdentifier()
return Berry(identifier, baseYield, emptyList(), growthTime, refreshRate, favMulchs, emptySet(), emptyList(), growthPoints, randomizedGrowthPoints, mutations, sproutShapeBoxes, matureShapeBoxes, flavors, tintIndexes, flowerModelIdentifier, flowerTexture, fruitModelIdentifier, fruitTexture, 0F)
}
}
}
@@ -1,47 +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.berry
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.world.feature.BerryGroveFeature
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.world.biome.Biome
object BerryHelper {
private val CACHE_LOADER = object : CacheLoader<RegistryEntry<Biome>, List<BerryBlock>>() {
override fun load(key: RegistryEntry<Biome>): List<BerryBlock> {
return naturalBerries.filter { berryBlock ->
val berry = berryBlock.berry()
berry?.spawnConditions?.any { it.canSpawn(berry, key) } ?: false
}
}
}
private val naturalBerries = CobblemonBlocks.berries().values.filter {
(it.berry()?.spawnConditions?.size ?: 0) > 0
}
private val validBerryCache: LoadingCache<RegistryEntry<Biome>, List<BerryBlock>> = CacheBuilder.newBuilder()
.maximumSize(4)
.build(CACHE_LOADER)
fun getBerriesForBiome(biome: RegistryEntry<Biome>): List<BerryBlock> {
return validBerryCache.get(biome)
}
fun getNaturallyGeneratingBerries(): List<BerryBlock> {
return naturalBerries
}
}
@@ -1,26 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry
/**
* Represents the different flavors associated with berries and other Pokémon consumables.
* See the [Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Flavor) article for more information.
*
* @author Licious
* @since November 28th, 2022
*/
enum class Flavor {
SPICY,
DRY,
SWEET,
BITTER,
SOUR
}
@@ -1,61 +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.berry
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import net.minecraft.world.WorldView
/**
* Represents a condition that impacts extra berry yield.
*
* @author Licious
* @since December 2nd, 2022
*/
interface GrowthFactor {
/**
* Validates the arguments contained in this factor, this is invoked during loading.
*
* @throws IllegalArgumentException If an argument isn't valid.
*/
fun validateArguments()
/**
* Checks if this factor should yield a bonus.
*
* @param world The [WorldView] the berry tree is in.
* @param state The [BlockState] of the berry tree.
* @param pos The [BlockPos] of the berry tree.
* @return If the bonus should activate.
*/
fun isValid(world: WorldView, state: BlockState, pos: BlockPos): Boolean
/**
* Resolves the amount of bonus berries to grow if [isValid] was true.
*
* @return The amount of extra berries to grow.
*/
fun yield(): Int
/**
* Returns the minimum possible yield for this factor.
*
* @return The minimum possible yield for this factor.
*/
fun minYield(): Int
/**
* Returns the maximum possible yield for this factor.
*
* @return The maximum possible yield for this factor.
*/
fun maxYield(): Int
}
@@ -1,25 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry
import net.minecraft.util.math.Vec3d
/**
* Represents a position in a berry tree where a berry will flower then fruit.
*
* @property position The coordinates of the berry in the tree block.
* @property rotation The degree value for the rotation each axis.
*
* @author Licious
* @since December 19th, 2022
*/
data class GrowthPoint(
val position: Vec3d,
val rotation: Vec3d
)
@@ -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.api.berry.adapter
import com.cobblemon.mod.common.api.berry.GrowthFactor
import com.cobblemon.mod.common.util.adapters.CobblemonGrowthFactorAdapter
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import net.minecraft.util.Identifier
import kotlin.reflect.KClass
/**
* A type adapter for [GrowthFactor]s.
* For the default implementation see [CobblemonGrowthFactorAdapter].
*
* @author Licious
* @since December 2nd, 2022
*/
interface GrowthFactorAdapter : JsonDeserializer<GrowthFactor>, JsonSerializer<GrowthFactor> {
/**
* Register a [GrowthFactor] to be used by this adapter.
*
* @param type The [KClass] of the [GrowthFactor].
* @param identifier The expected [Identifier] in the parsed JSON.
*/
fun register(type: KClass<out GrowthFactor>, identifier: Identifier)
}
@@ -1,34 +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.berry.adapter
import com.cobblemon.mod.common.api.berry.GrowthFactor
import com.cobblemon.mod.common.api.berry.spawncondition.BerrySpawnCondition
import com.cobblemon.mod.common.util.adapters.CobblemonBerrySpawnConditionAdapter
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import net.minecraft.util.Identifier
import kotlin.reflect.KClass
/**
* A type adapter for [GrowthFactor]s.
* For the default implementation see [CobblemonBerrySpawnConditionAdapter].
*
* @author Apion
* @since August 14, 2023
*/
interface SpawnConditionAdapter : JsonDeserializer<BerrySpawnCondition>, JsonSerializer<BerrySpawnCondition> {
/**
* Register a [BerrySpawnCondition] to be used by this adapter.
*
* @param type The [KClass] of the [BerrySpawnCondition].
* @param identifier The expected [Identifier] in the parsed JSON.
*/
fun register(type: KClass<out BerrySpawnCondition>, identifier: Identifier)
}
@@ -1,30 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry.spawncondition
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.util.math.random.Random
import net.minecraft.world.biome.Biome
class AllBiomeCondition(val minGroveSize: Int, val maxGroveSize: Int) : BerrySpawnCondition{
override fun canSpawn(berry: Berry, biome: RegistryEntry<Biome>): Boolean {
return true
}
override fun getGroveSize(random: Random): Int {
return random.nextBetween(minGroveSize, maxGroveSize)
}
companion object {
val ID = cobblemonResource("all_biome")
}
}
@@ -1,20 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry.spawncondition
import com.cobblemon.mod.common.api.berry.Berry
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.util.math.random.Random
import net.minecraft.world.biome.Biome
interface BerrySpawnCondition {
fun getGroveSize(random: Random): Int
fun canSpawn(berry: Berry, biome: RegistryEntry<Biome>): Boolean
}
@@ -1,32 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry.spawncondition
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.util.math.random.Random
import net.minecraft.world.biome.Biome
import kotlin.math.min
class PreferredBiomeCondition(val minGroveSize: Int, val maxGroveSize: Int) : BerrySpawnCondition{
override fun getGroveSize(random: Random): Int {
return random.nextBetween(minGroveSize, maxGroveSize)
}
override fun canSpawn(berry: Berry, biome: RegistryEntry<Biome>): Boolean {
val preferredBiomeTags = berry.preferredBiomeTags
return preferredBiomeTags.any {biome.isIn(it)}
}
companion object {
val ID = cobblemonResource("preferred_biome")
}
}
@@ -13,10 +13,8 @@ import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.moves.Move
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.lang
import java.util.UUID
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.MutableText
/**
* Used for opening a party select screen for players which routes to move selection when they choose a Pokémon.
@@ -32,7 +30,6 @@ object PartyMoveSelectCallbacks {
@JvmOverloads
fun create(
player: ServerPlayerEntity,
partyTitle: MutableText = lang("ui.party"),
pokemon: List<Pair<PartySelectPokemonDTO, List<MoveSelectDTO>>>,
cancel: (ServerPlayerEntity) -> Unit = {},
handler: (ServerPlayerEntity, pokemonIndex: Int, PartySelectPokemonDTO, moveIndex: Int, MoveSelectDTO) -> Unit
@@ -43,13 +40,12 @@ object PartyMoveSelectCallbacks {
handler = handler
)
callbacks[player.uuid] = callback
player.sendPacket(OpenPartyMoveCallbackPacket(callback.uuid, partyTitle, callback.pokemon))
player.sendPacket(OpenPartyMoveCallbackPacket(callback.uuid, callback.pokemon))
}
@JvmOverloads
fun createFromPokemon(
player: ServerPlayerEntity,
partyTitle: MutableText = lang("ui.party"),
pokemon: List<Pokemon>,
moves: (Pokemon) -> List<Move> = { it.moveSet.getMoves() },
canSelectPokemon: (Pokemon) -> Boolean = { true },
@@ -66,7 +62,6 @@ object PartyMoveSelectCallbacks {
create(
player = player,
partyTitle = partyTitle,
pokemon = pokemonList,
cancel = cancel,
handler = { _, pkIndex, _, moveIndex, _ ->
@@ -12,10 +12,10 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.PokemonPropertyExtractor
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.lang
import java.util.UUID
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NbtCompound
@@ -36,7 +36,7 @@ object PartySelectCallbacks {
@JvmOverloads
fun create(
player: ServerPlayerEntity,
title: Text = lang("ui.party"),
title: Text = "".text(),
pokemon: List<PartySelectPokemonDTO>,
cancel: (ServerPlayerEntity) -> Unit = {},
handler: (ServerPlayerEntity, index: Int) -> Unit
@@ -61,14 +61,12 @@ object PartySelectCallbacks {
@JvmOverloads
fun createBattleSelect(
player: ServerPlayerEntity,
title: Text = lang("ui.party"),
pokemon: List<BattlePokemon>,
canSelect: (BattlePokemon) -> Boolean,
cancel: (ServerPlayerEntity) -> Unit = {},
handler: (BattlePokemon) -> Unit
) = create(
player = player,
title = title,
cancel = cancel,
pokemon = pokemon.map { pk -> PartySelectPokemonDTO(pk.effectedPokemon).also { it.enabled = canSelect(pk) } }
) { _, index -> handler(pokemon[index]) }
@@ -76,14 +74,12 @@ object PartySelectCallbacks {
@JvmOverloads
fun createFromPokemon(
player: ServerPlayerEntity,
title: Text = lang("ui.party"),
pokemon: List<Pokemon>,
canSelect: (Pokemon) -> Boolean,
cancel: (ServerPlayerEntity) -> Unit = {},
handler: (Pokemon) -> Unit
) = create(
player = player,
title = title,
cancel = cancel,
pokemon = pokemon.map { pk -> PartySelectPokemonDTO(pk).also { it.enabled = canSelect(pk) } }
) { _, index -> handler(pokemon[index]) }
@@ -121,43 +117,36 @@ class PartySelectCallback(
open class PartySelectPokemonDTO(
val pokemonProperties: PokemonProperties,
val aspects: Set<String>,
val heldItem: ItemStack = ItemStack.EMPTY,
var currentHealth: Int,
var maxHealth: Int,
var hpRatio: Float = 1F,
var enabled: Boolean
) {
@JvmOverloads
constructor(pokemon: Pokemon, enabled: Boolean = true): this(
pokemonProperties = pokemon.createPokemonProperties(
PokemonPropertyExtractor.ASPECTS,
PokemonPropertyExtractor.SPECIES,
PokemonPropertyExtractor.LEVEL,
PokemonPropertyExtractor.NICKNAME,
PokemonPropertyExtractor.POKEBALL,
PokemonPropertyExtractor.STATUS,
PokemonPropertyExtractor.STATUS
),
aspects = pokemon.aspects,
heldItem = pokemon.heldItemNoCopy(),
currentHealth = pokemon.currentHealth,
maxHealth = pokemon.hp,
hpRatio = pokemon.currentHealth / pokemon.hp.toFloat(),
enabled = enabled
)
constructor(buffer: PacketByteBuf): this(
pokemonProperties = PokemonProperties().loadFromNBT(buffer.readNbt() as NbtCompound),
aspects = buffer.readList { it.readString() }.toSet(),
heldItem = buffer.readItemStack(),
currentHealth = buffer.readInt(),
maxHealth = buffer.readInt(),
hpRatio = buffer.readFloat(),
enabled = buffer.readBoolean()
)
fun writeToBuffer(buffer: PacketByteBuf) {
buffer.writeNbt(pokemonProperties.saveToNBT())
buffer.writeCollection(aspects) { _, aspect -> buffer.writeString(aspect) }
buffer.writeItemStack(heldItem)
buffer.writeInt(currentHealth)
buffer.writeInt(maxHealth)
buffer.writeFloat(hpRatio)
buffer.writeBoolean(enabled)
}
}
@@ -8,9 +8,11 @@
package com.cobblemon.mod.common.api.data
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.util.endsWith
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.io.File
import java.util.concurrent.ExecutionException
import net.minecraft.resource.ResourceManager
import net.minecraft.util.Identifier
@@ -42,7 +44,20 @@ interface JsonDataRegistry<T> : DataRegistry {
val resourcePath: String
override fun reload(manager: ResourceManager) {
this.reload(Cobblemon.implementation.reloadJsonRegistry(this, manager))
val data = hashMapOf<Identifier, T>()
manager.findResources(this.resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
resource.inputStream.use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = Identifier(identifier.namespace, File(identifier.path).nameWithoutExtension)
try {
data[resolvedIdentifier] = this.gson.fromJson(reader, this.typeToken.type)
} catch (exception: Exception) {
throw ExecutionException("Error loading JSON for data: $identifier", exception)
}
}
}
}
this.reload(data)
}
/**
@@ -53,6 +68,6 @@ interface JsonDataRegistry<T> : DataRegistry {
fun reload(data: Map<Identifier, T>)
companion object {
const val JSON_EXTENSION = ".json"
private const val JSON_EXTENSION = ".json"
}
}
@@ -10,11 +10,11 @@ package com.cobblemon.mod.common.api.drop
import com.cobblemon.mod.common.api.events.CobblemonEvents.LOOT_DROPPED
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
import kotlin.random.Random
import net.minecraft.entity.LivingEntity
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.Vec3d
import kotlin.random.Random
/**
* A table of drops that can produce a list of [DropEntry]. You can produce a drop list from [getDrops] to
@@ -13,10 +13,6 @@ import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
import com.cobblemon.mod.common.api.events.battles.BattleStartedPostEvent
import com.cobblemon.mod.common.api.events.battles.BattleStartedPreEvent
import com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent
import com.cobblemon.mod.common.api.events.berry.BerryHarvestEvent
import com.cobblemon.mod.common.api.events.berry.BerryMutationOfferEvent
import com.cobblemon.mod.common.api.events.berry.BerryMutationResultEvent
import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
import com.cobblemon.mod.common.api.events.entity.PokemonEntityLoadEvent
import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveEvent
@@ -24,14 +20,11 @@ import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveToWorldEvent
import com.cobblemon.mod.common.api.events.entity.SpawnEvent
import com.cobblemon.mod.common.api.events.farming.ApricornHarvestEvent
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent
import com.cobblemon.mod.common.api.events.pokeball.PokeBallCaptureCalculatedEvent
import com.cobblemon.mod.common.api.events.pokeball.PokemonCatchRateEvent
import com.cobblemon.mod.common.api.events.pokeball.ThrownPokeballHitEvent
import com.cobblemon.mod.common.api.events.pokemon.*
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionAcceptedEvent
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEvent
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionDisplayEvent
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionTestedEvent
import com.cobblemon.mod.common.api.events.pokemon.interaction.ExperienceCandyUseEvent
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
@@ -44,7 +37,6 @@ import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.server.network.ServerPlayerEntity
@Suppress("unused")
object CobblemonEvents {
@JvmField
@@ -60,8 +52,6 @@ object CobblemonEvents {
@JvmField
val EVOLUTION_DISPLAY = EventObservable<EvolutionDisplayEvent>()
@JvmField
val EVOLUTION_TESTED = EventObservable<EvolutionTestedEvent>()
@JvmField
val EVOLUTION_COMPLETE = EventObservable<EvolutionCompleteEvent>()
@JvmField
val POKEMON_NICKNAMED = CancelableObservable<PokemonNicknamedEvent>()
@@ -69,10 +59,6 @@ object CobblemonEvents {
@JvmField
val THROWN_POKEBALL_HIT = CancelableObservable<ThrownPokeballHitEvent>()
@JvmField
val POKEMON_CATCH_RATE = EventObservable<PokemonCatchRateEvent>()
@JvmField
val POKE_BALL_CAPTURE_CALCULATED = EventObservable<PokeBallCaptureCalculatedEvent>()
@JvmField
val POKEMON_CAPTURED = EventObservable<PokemonCapturedEvent>()
// @JvmField
// val EGG_HATCH = EventObservable<HatchEggEvent>()
@@ -94,9 +80,6 @@ object CobblemonEvents {
@JvmField
val POKEMON_RECALLED = EventObservable<PokemonRecalledEvent>()
@JvmField
val TRADE_COMPLETED = EventObservable<TradeCompletedEvent>()
@JvmField
val LEVEL_UP_EVENT = EventObservable<LevelUpEvent>()
@@ -113,10 +96,7 @@ object CobblemonEvents {
val POKEMON_ENTITY_SPAWN = ENTITY_SPAWN
.pipe(
filter { it.entity is PokemonEntity },
map {
@Suppress("UNCHECKED_CAST")
it as SpawnEvent<PokemonEntity>
}
map { it as SpawnEvent<PokemonEntity> }
)
@JvmField
@@ -140,15 +120,6 @@ object CobblemonEvents {
@JvmField
val APRICORN_HARVESTED = EventObservable<ApricornHarvestEvent>()
// Berries
@JvmField
val BERRY_HARVEST = EventObservable<BerryHarvestEvent>()
@JvmField
val BERRY_MUTATION_OFFER = EventObservable<BerryMutationOfferEvent>()
@JvmField
val BERRY_MUTATION_RESULT = EventObservable<BerryMutationResultEvent>()
@JvmField
val BERRY_YIELD = EventObservable<BerryYieldCalculationEvent>()
@JvmField
val LEFTOVERS_CREATED = CancelableObservable<LeftoversCreatedEvent>()
@JvmField
@@ -1,27 +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.events.berry
import com.cobblemon.mod.common.api.berry.Berry
/**
* The base of all Berry related events.
*
* @author Licious
* @since November 28th, 2022
*/
interface BerryEvent {
/**
* The [Berry] related to the event trigger
*/
val berry: Berry
}
@@ -1,41 +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.events.berry
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import net.minecraft.block.BlockState
import net.minecraft.item.ItemStack
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
/**
* An event fired when [BerryBlockEntity.harvest] is invoked.
*
* @property berry The [Berry] the tree is attached to.
* @property player The [ServerPlayerEntity] harvesting the tree.
* @property world The [World] the tree is in.
* @property pos The [BlockPos] of the tree.
* @property state The [BlockState] of the tree.
* @property blockEntity The backing [BerryBlockEntity]-
* @property drops A collection of [ItemStack]s produced by this harvest.
*
* @author Licious
* @since November 28th, 2022
*/
data class BerryHarvestEvent(
override val berry: Berry,
val player: ServerPlayerEntity,
val world: World,
val pos: BlockPos,
val state: BlockState,
val blockEntity: BerryBlockEntity,
val drops: MutableList<ItemStack>
) : BerryEvent
@@ -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.api.events.berry
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.block.BerryBlock
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
/**
* The event fired when [BerryBlock.grow] is invoked and the tree passes to [BerryBlock.MATURE_AGE].
* This allows you to modify the possible mutations for the tree, for the event that decides which of these mutations is used see [BerryMutationResultEvent].
*
* @property world The [World] the berry tree is in.
* @property state The [BlockState] of the berry tree.
* @property pos The [BlockPos] of the berry tree.
* @property mutations The possible [Berry] mutations.
*
* @author Licious
* @since January 19th, 2022
*/
data class BerryMutationOfferEvent(
override val berry: Berry,
val world: World,
val state: BlockState,
val pos: BlockPos,
val mutations: MutableSet<Berry>
) : BerryEvent
@@ -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.api.events.berry
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.block.BerryBlock
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
/**
* The event fired when [BerryBlock.grow] is invoked and the tree passes to [BerryBlock.MATURE_AGE].
* This allows you to modify the possible mutations for the tree, for the event that decides which of these mutations is used see [BerryMutationResultEvent].
*
* @property world The [World] the berry tree is in.
* @property state The [BlockState] of the berry tree.
* @property pos The [BlockPos] of the berry tree.
* @property mutations The possible [Berry] mutations.
* @property pickedMutation The [Berry] chosen to mutate into, if null the mutation will not occur.
*
* @author Licious
* @since January 19th, 2022
*/
data class BerryMutationResultEvent(
override val berry: Berry,
val world: World,
val state: BlockState,
val pos: BlockPos,
val mutations: Set<Berry>,
var pickedMutation: Berry?
) : BerryEvent
@@ -1,58 +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.events.berry
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.api.berry.GrowthFactor
import net.minecraft.block.BlockState
import net.minecraft.entity.LivingEntity
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
/**
* The event fired when [Berry.calculateYield] is invoked.
*
* @property world The [World] the berry tree is in.
* @property state The [BlockState] of the berry tree.
* @property pos The [BlockPos] of the berry tree.
* @property placer The [LivingEntity] triggering the calculation.
* @property yield The current yield of berries.
* @property passedGrowthFactors The [Berry.growthFactors] where [GrowthFactor.isValid] was true.
*
* @author Licious
* @since November 28th, 2022
*/
class BerryYieldCalculationEvent(
override val berry: Berry,
val world: World,
val state: BlockState,
val pos: BlockPos,
val placer: LivingEntity?,
yield: Int,
val passedGrowthFactors: Collection<GrowthFactor>
) : BerryEvent {
/**
* The amount of berries this tree will yield.
*
* @throws IllegalArgumentException If a new value exceeds [Berry.maxYield] or be lesser than 0.
*/
var yield: Int = yield
set(value) {
val max = this.berry.maxYield()
if (value > max) {
throw IllegalArgumentException("Cannot set the berry yield for ${this.berry.identifier} above $max")
}
if (value < 0) {
throw IllegalArgumentException("A berry tree cannot yield a negative amount of berries")
}
field = value
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.events.farming
import com.cobblemon.mod.common.api.apricorn.Apricorn
import net.minecraft.block.BlockState
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
@@ -19,7 +18,6 @@ import net.minecraft.util.math.BlockPos
*/
class ApricornHarvestEvent(
val player: ServerPlayerEntity,
val apricorn: Apricorn,
val world: ServerWorld,
val pos: BlockPos
) {
@@ -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.api.events.pokeball
import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.entity.LivingEntity
import net.minecraft.server.network.ServerPlayerEntity
/**
* Event fired when a Poké Ball has completed its capture calculation and is about to begin shaking
* or breaking free. The result of the capture can be changed by replacing [captureResult].
*
* @author Hiroku
* @since August 20th, 2023
*/
class PokeBallCaptureCalculatedEvent(
val thrower: LivingEntity,
val pokemonEntity: PokemonEntity,
val pokeBallEntity: EmptyPokeBallEntity,
var captureResult: CaptureContext
) {
fun ifPlayer(action: PokeBallCaptureCalculatedEvent.(player: ServerPlayerEntity) -> Unit) {
if (thrower is ServerPlayerEntity) {
action(this, thrower)
}
}
}
@@ -1,30 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokeball
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.entity.LivingEntity
/**
* Event fired when a capture calculator is accessing a Pokémon's catch rate. The [catchRate] property can
* be changed to adjust the rate.
*
* If you want to prevent a Pokémon from being caught, you should be handling the [PokeBallCaptureCalculatedEvent]
* event instead.
*
* @author Hiroku
* @since August 20th, 2023
*/
class PokemonCatchRateEvent(
val thrower: LivingEntity,
val pokeBallEntity: EmptyPokeBallEntity,
val pokemonEntity: PokemonEntity,
var catchRate: Float
)
@@ -8,12 +8,10 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.network.ServerPlayerEntity
data class PokemonCapturedEvent (
val pokemon: Pokemon,
val player: ServerPlayerEntity,
val pokeBallEntity: EmptyPokeBallEntity
val player: ServerPlayerEntity
)
@@ -1,14 +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.events.pokemon
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.trade.TradeParticipant
class TradeCompletedEvent(val tradeParticipant1 : TradeParticipant, val tradeParticipant1Pokemon : Pokemon, val tradeParticipant2 : TradeParticipant, val tradeParticipant2Pokemon : Pokemon)
@@ -23,6 +23,6 @@ import com.cobblemon.mod.common.pokemon.Pokemon
* @since April 28th, 2022
*/
data class EvolutionAcceptedEvent(
override val pokemon: Pokemon,
override val evolution: Evolution
) : Cancelable(), EvolutionEvent
val pokemon: Pokemon,
val evolution: Evolution
) : Cancelable()
@@ -21,6 +21,6 @@ import com.cobblemon.mod.common.pokemon.Pokemon
* @since October 2nd, 2022
*/
data class EvolutionCompleteEvent(
override val pokemon: Pokemon,
override val evolution: Evolution
) : EvolutionEvent
val pokemon: Pokemon,
val evolution: Evolution
)
@@ -25,7 +25,7 @@ import com.cobblemon.mod.common.pokemon.Pokemon
* @since May 3rd, 2022
*/
data class EvolutionDisplayEvent(
override val pokemon: Pokemon,
val pokemon: Pokemon,
var display: EvolutionDisplay,
override val evolution: Evolution
) : EvolutionEvent
val evolution: Evolution
)
@@ -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.events.pokemon.evolution
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.pokemon.Pokemon
/**
* The root of the various evolution related events.
*/
interface EvolutionEvent {
/**
* The Pokémon impacted in this event.
*/
val pokemon: Pokemon
/**
* The evolution triggering this event.
*/
val evolution: Evolution
}
@@ -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.api.events.pokemon.evolution
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.pokemon.Pokemon
/**
* An event fired when [Evolution.test] has been performed.
* The final state [result] will be the return value of the test function.
*
* @property pokemon The [Pokemon] being tested against.
* @property evolution The [Evolution] instance performing the test.
* @property originalResult The base result from the Cobblemon implementation.
* @property result The final value returned by [Evolution.test].
*/
data class EvolutionTestedEvent(
override val pokemon: Pokemon,
override val evolution: Evolution,
val originalResult: Boolean,
var result: Boolean
) : EvolutionEvent
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.api.text.font
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay.Companion.PORTRAIT_DIAMETER
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Species
@@ -122,27 +121,16 @@ fun drawText(
y: Number,
centered: Boolean = false,
colour: Int,
shadow: Boolean = true,
pMouseX: Number? = null,
pMouseY: Number? = null
): Boolean {
shadow: Boolean = true
) {
val comp = if (font == null) text else text.setStyle(text.style.withFont(font))
val textRenderer = MinecraftClient.getInstance().textRenderer
var x = x
val width = textRenderer.getWidth(comp)
if (centered) {
val width = textRenderer.getWidth(comp)
x = x.toDouble() - width / 2
}
context.drawText(textRenderer, comp, x.toInt(), y.toInt(), colour, shadow)
var isHovered = false
if (pMouseY != null && pMouseX != null) {
if (pMouseX.toInt() >= x.toInt() && pMouseX.toInt() <= x.toInt() + width &&
pMouseY.toInt() >= y.toInt() && pMouseY.toInt() <= y.toInt() + textRenderer.fontHeight
) {
isHovered = true
}
}
return isHovered
}
fun drawText(
@@ -191,11 +179,7 @@ fun drawPortraitPokemon(
partialTicks: Float
) {
val model = PokemonModelRepository.getPoser(species.resourceIdentifier, aspects)
val texture = PokemonModelRepository.getTexture(species.resourceIdentifier, aspects, state?.animationSeconds ?: 0F)
val context = RenderContext()
PokemonModelRepository.getTextureNoSubstitute(species.resourceIdentifier, aspects, 0f).let { it -> context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, species.getForm(aspects).baseScale)
val texture = PokemonModelRepository.getTexture(species.resourceIdentifier, aspects, state)
val renderType = model.getLayer(texture)
@@ -231,12 +215,11 @@ fun drawPortraitPokemon(
val packedLight = LightmapTextureManager.pack(11, 7)
model.withLayerContext(immediate, state, PokemonModelRepository.getLayers(species.resourceIdentifier, aspects)) {
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.DEFAULT_UV, 1F, 1F, 1F, 1F)
model.render(matrixStack, buffer, packedLight, OverlayTexture.DEFAULT_UV, 1F, 1F, 1F, 1F)
immediate.draw()
}
matrixStack.pop()
model.setDefault()
DiffuseLighting.enableGuiDepthLighting()
}
@@ -1,48 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.interaction
import net.minecraft.entity.Entity
import net.minecraft.item.ItemStack
import net.minecraft.server.network.ServerPlayerEntity
/**
* An interaction that will affect an [Entity].
* These need to be triggered.
*
* @param T The type of the [Entity] this interaction will affect.
*/
interface EntityInteraction<T : Entity> {
/**
* Fired when a [ServerPlayerEntity] interacts with the target entity.
*
* @param player The [ServerPlayerEntity] interacting with the [entity].
* @param entity The [Entity] of type [T] being interacted.
* @param stack The [ItemStack] used in this interaction.
* @return true if the interaction was successful and no further interactions should be processed
*/
fun onInteraction(player: ServerPlayerEntity, entity: T, stack: ItemStack): Boolean
/**
* Decreases the stack size by a given amount.
* The stack size should be validated beforehand.
* If the [player] is in creative mode the decrement won't be performed.
*
* @param player The [ServerPlayerEntity] that caused the interaction, this is used to check for creative mode.
* @param stack The [ItemStack] being mutated.
* @param amount The amount to deduct from the stack, default is 1.
*/
fun consumeItem(player: ServerPlayerEntity, stack: ItemStack, amount: Int = 1) {
if (!player.isCreative) {
stack.decrement(amount)
}
}
}
@@ -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.interaction
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import kotlin.reflect.KClass
import net.minecraft.util.Identifier
/**
* A type adapter for [EntityInteraction]s.
* For the default implementation with Pokémon entities see [CobblemonPokemonEntityInteractionTypeAdapter].
*
* @param T The type of [EntityInteraction].
*
* @author Licious
* @since November 30th, 2022
*/
interface EntityInteractionTypeAdapter<T : EntityInteraction<*>> : JsonDeserializer<T>, JsonSerializer<T> {
fun registerInteraction(identifier: Identifier, type: KClass<out T>)
}
@@ -6,8 +6,12 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
{
use(battle, pokemon, itemId, data) {
pokemon.removeVolatile(data[0]);
}
package com.cobblemon.mod.common.api.item
enum class Flavor {
SPICY,
DRY,
SWEET,
BITTER,
SOUR
}
@@ -98,11 +98,6 @@ interface PokemonAndMoveSelectingItem {
fun canUseOnMove(move: Move): Boolean
fun interactWithSpecific(player: ServerPlayerEntity, stack: ItemStack, pokemon: Pokemon): TypedActionResult<ItemStack>? {
if (player.isSneaking) {
return TypedActionResult.pass(stack)
}
MoveSelectCallbacks.create(
player = player,
moves = pokemon.moveSet.toList(),
@@ -151,4 +146,4 @@ interface PokemonAndMoveSelectingItem {
return TypedActionResult.success(stack)
}
}
}
@@ -8,8 +8,6 @@
package com.cobblemon.mod.common.api.item
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.PokemonInteractContext
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.callback.PartySelectCallbacks
import com.cobblemon.mod.common.api.text.red
@@ -24,7 +22,6 @@ import com.cobblemon.mod.common.util.isHeld
import com.cobblemon.mod.common.util.isLookingAt
import com.cobblemon.mod.common.util.party
import net.minecraft.item.ItemStack
import net.minecraft.registry.Registries
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.TypedActionResult
import net.minecraft.util.math.Box
@@ -62,17 +59,15 @@ interface PokemonSelectingItem {
return interactWithSpecificBattle(player, stack, battlePokemon)
}
} ?: run {
if (!player.isSneaking) {
return if (entity != null) {
val pokemon = entity.pokemon
if (entity.ownerUuid == player.uuid) {
applyToPokemon(player, stack, pokemon) ?: TypedActionResult.pass(stack)
} else {
TypedActionResult.fail(stack)
}
if (entity != null) {
val pokemon = entity.pokemon
if (entity.ownerUuid == player.uuid) {
return applyToPokemon(player, stack, pokemon) ?: TypedActionResult.pass(stack)
} else {
interactGeneral(player, stack)
return TypedActionResult.fail(stack)
}
} else {
return interactGeneral(player, stack)
}
}
@@ -94,7 +89,6 @@ interface PokemonSelectingItem {
if (!player.isCreative) {
stack.decrement(1)
}
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(battlePokemon.entity!!.pokemon.species.resourceIdentifier, Registries.ITEM.getId(stack.item)))
}
}
@@ -112,19 +106,16 @@ interface PokemonSelectingItem {
}
fun interactGeneral(player: ServerPlayerEntity, stack: ItemStack): TypedActionResult<ItemStack> {
val party = player.party().toList()
if (party.isEmpty()) {
return TypedActionResult.fail(stack)
}
PartySelectCallbacks.createFromPokemon(
player = player,
pokemon = party,
pokemon = player.party().toList(),
canSelect = ::canUseOnPokemon,
handler = { pk ->
if (stack.isHeld(player)) {
applyToPokemon(player, stack, pk)
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(pk.species.resourceIdentifier, Registries.ITEM.getId(stack.item)))
if (!player.isCreative) {
stack.decrement(1)
}
}
}
)
@@ -18,8 +18,6 @@ import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtElement
import net.minecraft.nbt.NbtList
import net.minecraft.network.PacketByteBuf
import kotlin.math.min
class MoveSet : Iterable<Move> {
val observable = SimpleObservable<MoveSet>()
private var emit = true
@@ -65,11 +63,6 @@ class MoveSet : Iterable<Move> {
update()
}
fun partialHeal() {
getMoves().forEach { it.currentPp = min((it.currentPp + (it.maxPp / 2)), it.maxPp)}
update()
}
fun clear() {
doWithoutEmitting {
for (i in 0 until MOVE_COUNT){
@@ -8,24 +8,38 @@
package com.cobblemon.mod.common.api.mulch
import net.minecraft.util.StringIdentifiable
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.item.MulchItem
/**
* Represents the different types of Mulch implemented in the mod.
*
*/
enum class MulchVariant(val duration: Int = -1) : StringIdentifiable {
enum class MulchVariant {
COARSE,
GROWTH(3),
GROWTH,
HUMID,
LOAMY,
PEAT,
RICH(3),
RICH,
SANDY,
SURPRISE(3),
NONE;
SURPRISE;
override fun asString(): String {
return name.lowercase()
/**
* Resolves the item version of the Mulch.
*
* @return The [MulchItem] equivalent.
*/
fun item(): MulchItem = when (this) {
COARSE -> CobblemonItems.COARSE_MULCH
GROWTH -> CobblemonItems.GROWTH_MULCH
HUMID -> CobblemonItems.HUMID_MULCH
LOAMY -> CobblemonItems.LOAMY_MULCH
PEAT -> CobblemonItems.PEAT_MULCH
RICH -> CobblemonItems.RICH_MULCH
SANDY -> CobblemonItems.SANDY_MULCH
SURPRISE -> CobblemonItems.SURPRISE_MULCH
}
}
}
@@ -65,7 +65,6 @@ object CobblemonPermissions {
val TEST_PARTY_SLOT = this.create("${COMMAND_PREFIX}testpartyslot", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val CLEAR_PARTY = this.create("${COMMAND_PREFIX}clearparty", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val CLEAR_PC = this.create("${COMMAND_PREFIX}clearpc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
fun all(): Iterable<Permission> = this.permissions
@@ -8,12 +8,9 @@
package com.cobblemon.mod.common.api.pokeball.catching.calculators
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokeball.PokemonCatchRateEvent
import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.entity.LivingEntity
/**
@@ -27,6 +24,7 @@ import net.minecraft.entity.LivingEntity
* @since November 30, 2021
*/
interface CaptureCalculator {
/**
* The literal ID of this calculator.
* Used when registering the calculator to the registry in order to be used by the game rule.
@@ -39,22 +37,10 @@ interface CaptureCalculator {
* Processes a capture attempt with the given params.
*
* @param thrower The [LivingEntity] that threw the [PokeBall].
* @param pokeBallEntity The [EmptyPokeBallEntity] used.
* @param target The target [PokemonEntity] attempting to be captured.
* @return a [CaptureContext] that is the result of the capture attempt.
* @param pokeBall The [PokeBall] used.
* @param target The target [Pokemon] attempting to be captured.
* @return
*/
fun processCapture(thrower: LivingEntity, pokeBallEntity: EmptyPokeBallEntity, target: PokemonEntity) : CaptureContext
fun processCapture(thrower: LivingEntity, pokeBall: PokeBall, target: Pokemon) : CaptureContext
fun getCatchRate(thrower: LivingEntity, pokeBallEntity: EmptyPokeBallEntity, target: PokemonEntity, catchRate: Float): Float {
val event = PokemonCatchRateEvent(
thrower = thrower,
pokemonEntity = target,
pokeBallEntity = pokeBallEntity,
catchRate = catchRate
)
CobblemonEvents.POKEMON_CATCH_RATE.post(event)
return event.catchRate
}
}
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.api.pokemon
import com.cobblemon.mod.common.api.berry.Flavor
import com.cobblemon.mod.common.api.item.Flavor
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.pokemon.Nature
import com.cobblemon.mod.common.util.cobblemonResource
@@ -166,16 +166,6 @@ object Natures {
return allNatures.find { nature -> nature.name == name }
}
/**
* Utility method to get a nature by string
* @return a nature type or null
*/
fun getNature(identifier: String): Nature? {
val nature = getNature(cobblemonResource(identifier))
if(nature != null) return nature
return getNature(Identifier(identifier))
}
/**
* Helper function for a random Nature
* @return a random nature type
@@ -15,21 +15,16 @@ import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.EVs
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.IVs
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.pokemon.*
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
import com.cobblemon.mod.common.pokemon.status.PersistentStatusContainer
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.isInt
import com.cobblemon.mod.common.util.splitMap
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import kotlin.random.Random
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtElement
import net.minecraft.nbt.NbtList
@@ -91,10 +86,6 @@ open class PokemonProperties {
props.ability = parseStringOfRegistry(keyPairs, listOf("ability")) { Abilities.get(it)?.name }
props.status = parseStringOfRegistry(keyPairs, listOf("status")) { (Statuses.getStatus(it) ?: Statuses.getStatus(it.asIdentifierDefaultingNamespace()))?.showdownName }
props.nickname = parseText(keyPairs, listOf("nickname", "nick"))
props.teraType = parseStringOfRegistry(keyPairs, listOf("tera_type", "tera")) { ElementalTypes.get(it)?.name }
props.dmaxLevel = parseIntProperty(keyPairs, listOf("dmax_level", "dmax"))?.coerceIn(0, Cobblemon.config.maxDynamaxLevel)
props.gmaxFactor = parseBooleanProperty(keyPairs, listOf("gmax_factor", "gmax"))
props.tradeable = parseBooleanProperty(keyPairs, listOf("tradeable", "tradable"))
val maybeIVs = IVs()
val maybeEVs = EVs()
@@ -156,10 +147,9 @@ open class PokemonProperties {
}
private fun parseSpeciesIdentifier(keyPairs: MutableList<Pair<String, String?>>): String? {
fun cleanSpeciesName(string: String) = string.lowercase().replace("[^a-z0-9_:]".toRegex(), "")
val matched = getMatchedKeyPair(keyPairs, listOf("species"))
if (matched != null) {
val value = matched.second?.let(::cleanSpeciesName) ?: return null
val value = matched.second?.lowercase() ?: return null
return if (value.lowercase() == "random") {
"random"
} else {
@@ -178,7 +168,7 @@ open class PokemonProperties {
"random"
} else {
try {
val identifier = cleanSpeciesName(pair.first).asIdentifierDefaultingNamespace()
val identifier = pair.first.asIdentifierDefaultingNamespace()
val found = PokemonSpecies.getByIdentifier(identifier) ?: return@find false
if (found.resourceIdentifier.namespace == Cobblemon.MODID) found.resourceIdentifier.path else found.resourceIdentifier.toString()
} catch (e: InvalidIdentifierException) {
@@ -256,10 +246,6 @@ open class PokemonProperties {
var ability: String? = null
var aspects: Set<String> = emptySet()
var status: String? = null
var teraType: String? = null
var dmaxLevel: Int? = null
var gmaxFactor: Boolean? = null
var tradeable: Boolean? = null
var ivs: IVs? = null
var evs: EVs? = null
@@ -301,19 +287,14 @@ open class PokemonProperties {
customProperties.forEach { it.apply(pokemon) }
ivs?.let { ivs ->
ivs.forEach { stat ->
pokemon.setIV(stat.key, stat.value)
pokemon.ivs[stat.key] = stat.value
}
}
evs?.let { evs ->
evs.forEach { stat ->
pokemon.setEV(stat.key, stat.value)
pokemon.evs[stat.key] = stat.value
}
}
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemon.teraType = type } }
dmaxLevel?.let { pokemon.dmaxLevel = it }
gmaxFactor?.let { pokemon.gmaxFactor = it }
tradeable?.let { pokemon.tradeable = it }
pokemon.updateAspects()
}
fun apply(pokemonEntity: PokemonEntity) {
@@ -341,19 +322,14 @@ open class PokemonProperties {
customProperties.forEach { it.apply(pokemonEntity) }
ivs?.let { ivs ->
ivs.forEach { stat ->
pokemonEntity.pokemon.setIV(stat.key, stat.value)
pokemonEntity.pokemon.ivs[stat.key] = stat.value
}
}
evs?.let { evs ->
evs.forEach { stat ->
pokemonEntity.pokemon.setEV(stat.key, stat.value)
pokemonEntity.pokemon.evs[stat.key] = stat.value
}
}
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemonEntity.pokemon.teraType = type } }
dmaxLevel?.let { pokemonEntity.pokemon.dmaxLevel = it }
gmaxFactor?.let { pokemonEntity.pokemon.gmaxFactor = it }
tradeable?.let { pokemonEntity.pokemon.tradeable = it }
pokemonEntity.pokemon.updateAspects()
}
fun matches(pokemon: Pokemon): Boolean {
@@ -387,10 +363,6 @@ open class PokemonProperties {
evs?.forEach{ stat ->
if (stat.value != pokemon.evs[stat.key]) { return false }
}
teraType?.takeIf { it != pokemon.teraType.name }?.let { return false }
dmaxLevel?.takeIf { it != pokemon.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != pokemon.gmaxFactor }?.let { return false }
tradeable?.takeIf { it != pokemon.tradeable }?.let { return false }
return customProperties.none { !it.matches(pokemon) }
}
@@ -423,10 +395,6 @@ open class PokemonProperties {
evs?.forEach{ stat ->
if (stat.value != pokemonEntity.pokemon.evs[stat.key]) { return false }
}
teraType?.takeIf { it != pokemonEntity.pokemon.teraType.name }?.let { return false }
dmaxLevel?.takeIf { it != pokemonEntity.pokemon.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != pokemonEntity.pokemon.gmaxFactor }?.let { return false }
tradeable?.takeIf { it != pokemonEntity.pokemon.tradeable }?.let { return false }
return customProperties.none { !it.matches(pokemonEntity) }
}
@@ -467,33 +435,24 @@ open class PokemonProperties {
if (stat.value != propertiesEVs[stat.key]) { return false }
}
}
teraType?.takeIf { it != properties.teraType }?.let { return false }
dmaxLevel?.takeIf { it != properties.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != properties.gmaxFactor }?.let { return false }
tradeable?.takeIf { it != properties.tradeable }?.let { return false }
return true
}
fun create(): Pokemon {
val pokemon = Pokemon()
apply(pokemon)
pokemon.initialize()
roll(pokemon)
// Force the setter to initialize it
pokemon.species = pokemon.species
// This should only be a thing once we have moveset control in properties until then a creation should require a moveset init.
/*
if (pokemon.moveSet.none { it != null }) {
pokemon.initializeMoveset()
}
*/
pokemon.initializeMoveset()
return pokemon
}
// TEST YOUR LUCK!
fun roll(pokemon: Pokemon) {
val baseTypes = pokemon.species.types.toList()
if (this.shiny == null) pokemon.shiny = Cobblemon.config.shinyRate.checkRate()
if (this.teraType == null) pokemon.teraType =
if (Cobblemon.config.teraTypeRate.checkRate()) ElementalTypes.all().filter { !baseTypes.contains(it) }.random()
else baseTypes.random()
}
// If the config value is at least 1, then do 1/x and use that as the property chance
private fun Float.checkRate(): Boolean = this >= 1 && (Random.Default.nextFloat() < 1 / this)
fun createEntity(world: World): PokemonEntity {
return PokemonEntity(world, create())
}
@@ -514,10 +473,6 @@ open class PokemonProperties {
status?.let { nbt.putString(DataKeys.POKEMON_STATUS_NAME, it) }
ivs?.let { nbt.put(DataKeys.POKEMON_IVS, it.saveToNBT(NbtCompound())) }
evs?.let { nbt.put(DataKeys.POKEMON_EVS, it.saveToNBT(NbtCompound())) }
teraType?.let { nbt.putString(DataKeys.POKEMON_TERA_TYPE, it) }
dmaxLevel?.let { nbt.putInt(DataKeys.POKEMON_DMAX_LEVEL, it) }
gmaxFactor?.let { nbt.putBoolean(DataKeys.POKEMON_GMAX_FACTOR, it) }
tradeable?.let { nbt.putBoolean(DataKeys.POKEMON_TRADEABLE, it) }
val custom = NbtList()
customProperties.map { NbtString.of(it.asString()) }.forEach { custom.add(it) }
nbt.put(DataKeys.POKEMON_PROPERTIES_CUSTOM, custom)
@@ -539,10 +494,6 @@ open class PokemonProperties {
status = if (tag.contains(DataKeys.POKEMON_STATUS_NAME)) tag.getString(DataKeys.POKEMON_STATUS_NAME) else null
ivs = if (tag.contains(DataKeys.POKEMON_IVS)) ivs?.loadFromNBT(tag.getCompound(DataKeys.POKEMON_IVS)) as IVs? else null
evs = if (tag.contains(DataKeys.POKEMON_EVS)) evs?.loadFromNBT(tag.getCompound(DataKeys.POKEMON_EVS)) as EVs? else null
teraType = if (tag.contains(DataKeys.POKEMON_TERA_TYPE)) tag.getString(DataKeys.POKEMON_TERA_TYPE) else null
dmaxLevel = if (tag.contains(DataKeys.POKEMON_DMAX_LEVEL)) tag.getInt(DataKeys.POKEMON_DMAX_LEVEL) else null
gmaxFactor = if (tag.contains(DataKeys.POKEMON_GMAX_FACTOR)) tag.getBoolean(DataKeys.POKEMON_GMAX_FACTOR) else null
tradeable = if (tag.contains(DataKeys.POKEMON_TRADEABLE)) tag.getBoolean(DataKeys.POKEMON_TRADEABLE) else null
val custom = tag.getList(DataKeys.POKEMON_PROPERTIES_CUSTOM, NbtElement.STRING_TYPE.toInt())
// This is kinda gross
custom.forEach { customProperties.addAll(parse(it.asString()).customProperties) }
@@ -566,10 +517,6 @@ open class PokemonProperties {
status?.let { json.addProperty(DataKeys.POKEMON_STATUS_NAME, it) }
ivs?.let { json.add(DataKeys.POKEMON_IVS, it.saveToJSON(JsonObject())) }
evs?.let { json.add(DataKeys.POKEMON_EVS, it.saveToJSON(JsonObject())) }
teraType?.let { json.addProperty(DataKeys.POKEMON_TERA_TYPE, it) }
dmaxLevel?.let { json.addProperty(DataKeys.POKEMON_DMAX_LEVEL, it) }
gmaxFactor?.let { json.addProperty(DataKeys.POKEMON_GMAX_FACTOR, it) }
tradeable?.let { json.addProperty(DataKeys.POKEMON_TRADEABLE, it) }
val custom = JsonArray()
customProperties.map { it.asString() }.forEach { custom.add(it) }
json.add(DataKeys.POKEMON_PROPERTIES_CUSTOM, custom)
@@ -592,10 +539,6 @@ open class PokemonProperties {
status = json.get(DataKeys.POKEMON_STATUS_NAME)?.asString
ivs?.loadFromJSON(json.getAsJsonObject(DataKeys.POKEMON_IVS))
evs?.loadFromJSON(json.getAsJsonObject(DataKeys.POKEMON_EVS))
teraType = json.get(DataKeys.POKEMON_TERA_TYPE)?.asString
dmaxLevel = json.get(DataKeys.POKEMON_DMAX_LEVEL)?.asInt
gmaxFactor = json.get(DataKeys.POKEMON_GMAX_FACTOR)?.asBoolean
tradeable = json.get(DataKeys.POKEMON_TRADEABLE)?.asBoolean
val custom = json.get(DataKeys.POKEMON_PROPERTIES_CUSTOM)?.asJsonArray
// This is still kinda gross
custom?.forEach { customProperties.addAll(parse(it.asString).customProperties) }
@@ -622,10 +565,6 @@ open class PokemonProperties {
evs?.forEach{ stat ->
pieces.add("${stat.key}_ev=${stat.value}")
}
teraType?.let { pieces.add("tera_type=$it") }
dmaxLevel?.let { pieces.add("dmax_level=$it") }
gmaxFactor?.let { pieces.add("gmax_factor=$it") }
tradeable?.let { pieces.add("tradeable=$it") }
customProperties.forEach { pieces.add(it.asString()) }
return pieces.joinToString(separator)
}
@@ -34,6 +34,7 @@ import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Species
@@ -41,8 +42,7 @@ import com.cobblemon.mod.common.pokemon.SpeciesAdditions
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonPreEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonRequirementAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.NbtItemPredicateAdapter
import com.cobblemon.mod.common.pokemon.evolution.predicate.NbtItemPredicate
import com.cobblemon.mod.common.pokemon.helditem.CobblemonEmptyHeldItemManager
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.cobblemonResource
@@ -95,7 +95,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
.registerTypeAdapter(StatusEffect::class.java, RegistryElementAdapter<StatusEffect> { Registries.STATUS_EFFECT })
.registerTypeAdapter(NbtItemPredicate::class.java, NbtItemPredicateAdapter)
.disableHtmlEscaping()
.enableComplexMapKeySerialization()
.create()
@@ -239,14 +238,11 @@ object PokemonSpecies : JsonDataRegistry<Species> {
val heightm = form?.height ?: species.height
val weightkg = form?.weight ?: species.weight
// This is ugly, but we already have it hardcoded in the mod anyway
val maxHP = if (species.showdownId() == "shedinja") 1 else null
val maxHp = if (species.showdownId() == "shedinja") 1 else null
val canGigantamax: String? = if (form?.gigantamaxMove != null) form.gigantamaxMove.name else null
val cannotDynamax = form?.dynamaxBlocked ?: species.dynamaxBlocked
// ToDo battleOnly
// ToDo changesFrom
val requiredMove = form?.requiredMove
val requiredItem = form?.requiredItem
val requiredItems = form?.requiredItems
}
private fun createShowdownName(species: Species): String {
@@ -10,14 +10,11 @@ package com.cobblemon.mod.common.api.pokemon.effect.adapter
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffect
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry
import com.cobblemon.mod.common.pokemon.effects.PotionBaseEffect
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import java.lang.reflect.Type
import net.minecraft.registry.Registries
import net.minecraft.util.Identifier
object ShoulderEffectAdapter: JsonDeserializer<ShoulderEffect> {
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): ShoulderEffect {
@@ -26,18 +23,7 @@ object ShoulderEffectAdapter: JsonDeserializer<ShoulderEffect> {
} else {
json.asJsonObject.get("type").asString to json.asJsonObject
}
val effect = ShoulderEffectRegistry.get(typeId) ?: run {
try {
val effectId = Identifier(typeId.replace("-", "_").replace("slow_fall", "slow_falling"))
val registry = Registries.STATUS_EFFECT
val effect = registry.get(effectId)
if (effect != null) {
return PotionBaseEffect(effect, 0, true, false, false)
}
} catch (_: Exception) {}
throw IllegalArgumentException("Cannot find shoulder effect with type '$typeId'")
}
val effect = ShoulderEffectRegistry.get(typeId) ?: throw IllegalArgumentException("Cannot find shoulder effect with type '$typeId'")
return context.deserialize(obj, effect)
}
}
@@ -9,9 +9,9 @@
package com.cobblemon.mod.common.api.pokemon.evolution
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.abilities.AbilityPool
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEvent
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionTestedEvent
import com.cobblemon.mod.common.api.moves.BenchedMove
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
@@ -67,12 +67,7 @@ interface Evolution : EvolutionLike {
* @param pokemon The [Pokemon] being queried.
* @return If the [Evolution] can start.
*/
fun test(pokemon: Pokemon): Boolean {
val result = this.requirements.all { requirement -> requirement.check(pokemon) }
val event = EvolutionTestedEvent(pokemon, this, result, result)
CobblemonEvents.EVOLUTION_TESTED.post(event)
return event.result
}
fun test(pokemon: Pokemon) = this.requirements.all { requirement -> requirement.check(pokemon) }
/**
* Starts this evolution or queues it if [optional] is true.
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.pokemon.stats
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
/**
@@ -54,24 +53,9 @@ interface EvCalculator {
object Generation8EvCalculator : EvCalculator {
private val powerItems = mapOf(
Stats.SPEED to CobblemonItemTags.POWER_ANKLET,
Stats.SPECIAL_DEFENCE to CobblemonItemTags.POWER_BAND,
Stats.DEFENCE to CobblemonItemTags.POWER_BELT,
Stats.ATTACK to CobblemonItemTags.POWER_BRACER,
Stats.SPECIAL_ATTACK to CobblemonItemTags.POWER_LENS,
Stats.HP to CobblemonItemTags.POWER_WEIGHT
)
override fun calculate(battlePokemon: BattlePokemon, opponentPokemon: BattlePokemon): Map<Stat, Int> {
val heldItem = battlePokemon.effectedPokemon.heldItemNoCopy()
val evYield = mutableMapOf<Stat, Int>()
for ((stat, value) in opponentPokemon.originalPokemon.form.evYield) {
val boost = if (!heldItem.isEmpty && heldItem.isIn(powerItems[stat])) 8 else 0
evYield[stat] = evYield.getOrDefault(stat, 0) + value + boost
}
return evYield
// ToDo Once held items and EV related items are implemented update this
return opponentPokemon.originalPokemon.form.evYield
}
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.spawning
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.config
import com.cobblemon.mod.common.api.spawning.mixins.CachedOnlyChunkAccessor
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.api.spawning.spawner.SpawningArea
@@ -80,7 +81,8 @@ object CobblemonSpawningProspector : SpawningProspector {
for (x in area.baseX until area.baseX + area.length) {
for (z in area.baseZ until area.baseZ + area.width) {
val query = chunks.computeIfAbsent(Pair(getSectionCoord(x), getSectionCoord(z))) {
world.getChunk(it.first, it.second, ChunkStatus.FULL, false)
val manager = world.chunkManager as CachedOnlyChunkAccessor
manager.`cobblemon$request`(it.first, it.second, ChunkStatus.FULL)
} ?: continue
var canSeeSky = world.isSkyVisibleAllowingSea(pos.set(x, yRange.first, z))
@@ -88,7 +90,7 @@ object CobblemonSpawningProspector : SpawningProspector {
val state = query.getBlockState(pos.set(x, y, z))
blocks[x - area.baseX][y - baseY][z - area.baseZ] = WorldSlice.BlockData(
state = state,
light = world.getLightLevel(pos)
light = state.getOpacity(world, pos)
)
if (canSeeSky) {
@@ -14,7 +14,9 @@ import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.feature.SeasonFeatureHandler
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.weightedSelection
import kotlin.random.Random
/**
* A [SpawnAction] that will spawn a single [PokemonEntity].
@@ -33,6 +35,10 @@ class PokemonSpawnAction(
if (props.level == null) {
props.level = detail.getDerivedLevelRange().random()
}
if (props.shiny == null) {
// If the config value is at least 1, then do 1/x and use that as the shiny chance
props.shiny = Cobblemon.config.shinyRate.takeIf { it >= 1 }?.let { Random.Default.nextFloat() < 1 / it }
}
val heldItems = detail.heldItems?.takeIf { it.isNotEmpty() }?.toMutableList() ?: mutableListOf()
val heldItem = if (heldItems.isNotEmpty()) {
val until100 = 1 - heldItems.sumOf { it.percentage / 100 }
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.lang
import com.google.gson.annotations.SerializedName
import kotlin.math.ceil
@@ -49,8 +48,6 @@ class PokemonSpawnDetail : SpawnDetail() {
override fun getName(): MutableText {
displayName?.let { return it.asTranslated() }
val speciesString = pokemon.species
if (speciesString != null) {
if (speciesString.lowercase() == "random") {
@@ -28,12 +28,13 @@ abstract class SpawnAction<T : Entity>(
) {
abstract fun createEntity(): T?
open fun run() {
fun run() {
ctx.influences.forEach { it.affectAction(this) }
val e = createEntity() ?: return
e.setPosition(ctx.position.toVec3d().add(0.5, 1.0, 0.5))
entity.emit(e)
CobblemonEvents.ENTITY_SPAWN.postThen(SpawnEvent(e, ctx), ifSucceeded = { ctx.world.spawnEntity(e) })
CobblemonEvents.ENTITY_SPAWN.post(SpawnEvent(e, ctx))
ctx.world.spawnEntity(e)
}
/**
@@ -46,4 +47,4 @@ abstract class SpawnAction<T : Entity>(
val entity: SimpleObservable<T> = SimpleObservable<T>().apply {
subscribe { entity -> ctx.afterSpawn(entity) }
}
}
}
@@ -0,0 +1,20 @@
/*
* 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.spawning.influence
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules.DO_POKEMON_SPAWNING
open class GameRuleInfluence : SpawningInfluence {
override fun affectSpawnable(detail: SpawnDetail, ctx: SpawningContext): Boolean {
return detail !is PokemonSpawnDetail || ctx.world.gameRules.getBoolean(DO_POKEMON_SPAWNING)
}
}
@@ -6,12 +6,13 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block
package com.cobblemon.mod.common.api.spawning.mixins
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import net.minecraft.world.chunk.Chunk
import net.minecraft.world.chunk.ChunkStatus
interface CachedOnlyChunkAccessor {
fun `cobblemon$request`(x: Int, z: Int, status: ChunkStatus): Chunk?
interface PreEmptsExplosion {
fun whenExploded(world: World, state: BlockState, pos: BlockPos)
}
@@ -17,9 +17,9 @@ import com.cobblemon.mod.common.api.spawning.context.calculators.AreaSpawningCon
import com.cobblemon.mod.common.api.spawning.context.calculators.SpawningContextCalculator.Companion.prioritizedAreaCalculators
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.mixins.CachedOnlyChunkAccessor
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.isBoxLoaded
import com.cobblemon.mod.common.util.squeezeWithinBounds
import com.cobblemon.mod.common.util.toVec3f
import net.minecraft.entity.ai.pathing.NavigationType
@@ -30,8 +30,6 @@ import net.minecraft.util.math.Vec3d
import net.minecraft.world.World
import net.minecraft.world.chunk.Chunk
import net.minecraft.world.chunk.ChunkStatus
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules.DO_POKEMON_SPAWNING
import net.minecraft.util.math.ChunkPos
/**
* A type of [TickingSpawner] that operates within some area. When this spawner type
@@ -60,23 +58,13 @@ abstract class AreaSpawner(
var resolver: AreaContextResolver = Cobblemon.areaContextResolver
var contextCalculators: List<AreaSpawningContextCalculator<*>> = prioritizedAreaCalculators
override fun run(cause: SpawnCause): Pair<SpawningContext, SpawnDetail>? {
val area = getArea(cause)
if (area?.world?.gameRules?.getBoolean(DO_POKEMON_SPAWNING) == false) {
return null
}
val constrainedArea = if (area != null) constrainArea(area) else null
if (constrainedArea != null) {
val areaBox = Box.of(Vec3d(constrainedArea.getCenter().toVec3f()), CHUNK_REACH * 16.0 * 2, 1000.0, CHUNK_REACH * 16.0 * 2)
if (!constrainedArea.world.isBoxLoaded(areaBox)) {
return null
}
val numberNearby = constrainedArea.world.getEntitiesByClass(
PokemonEntity::class.java,
areaBox
Box.of(Vec3d(constrainedArea.getCenter().toVec3f()), CHUNK_REACH * 16.0 * 2, 1000.0, CHUNK_REACH * 16.0 * 2)
) { true }.size
val chunksCovered = CHUNK_REACH * CHUNK_REACH
@@ -125,11 +113,7 @@ abstract class AreaSpawner(
val originalY = area.baseY
val (chunkX, chunkZ) = Pair(ChunkSectionPos.getSectionCoord(area.baseX), ChunkSectionPos.getSectionCoord(area.baseZ))
// if the chunk isn't loaded, we don't want to go further & we don't want the getChunk function below to load/create the chunk.
if (!area.world.isChunkLoaded(ChunkPos.toLong(chunkX, chunkZ))) return null
val chunk = area.world.getChunk(chunkX, chunkZ, ChunkStatus.FULL) ?: return null
val chunk = (area.world.chunkManager as CachedOnlyChunkAccessor).`cobblemon$request`(chunkX, chunkZ, ChunkStatus.FULL) ?: return null
var valid = isValidStartPoint(area.world, chunk, basePos)
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.spawning.spawner
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.SpawnerManager
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.influence.GameRuleInfluence
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import net.minecraft.server.network.ServerPlayerEntity
@@ -25,7 +26,7 @@ import net.minecraft.server.network.ServerPlayerEntity
*/
object PlayerSpawnerFactory {
var spawns: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
var influenceBuilders = mutableListOf<(player: ServerPlayerEntity) -> SpawningInfluence?>({ PlayerLevelRangeInfluence(it, variation = 5) })
var influenceBuilders = mutableListOf<(player: ServerPlayerEntity) -> SpawningInfluence?>({ PlayerLevelRangeInfluence(it, variation = 5) }, { GameRuleInfluence() })
fun create(spawnerManager: SpawnerManager, player: ServerPlayerEntity): PlayerSpawner {
val influences = influenceBuilders.mapNotNull { it(player) }
@@ -33,4 +34,4 @@ object PlayerSpawnerFactory {
it.influences.addAll(influences)
}
}
}
}
@@ -32,12 +32,12 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
override fun iterator() = pokemon.iterator()
override fun get(position: BottomlessPosition) = position.currentIndex
.takeIf { it in pokemon.indices }
.takeIf { it < pokemon.size && it >= 0 }
?.let { pokemon[it] }
override fun getFirstAvailablePosition() = BottomlessPosition(pokemon.size)
override fun isValidPosition(position: BottomlessPosition) = position.currentIndex >= 0
operator fun get(index: Int) = index.takeIf { it in pokemon.indices }?.let { pokemon[it] }
operator fun get(index: Int) = index.takeIf { it >= 0 && it < pokemon.size }?.let { pokemon[it] }
override fun getObservingPlayers() = emptySet<ServerPlayerEntity>()
override fun sendTo(player: ServerPlayerEntity) {}
@@ -58,12 +58,7 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
override fun loadFromNBT(nbt: NbtCompound): BottomlessStore {
var i = -1
while (nbt.contains(DataKeys.STORE_SLOT + ++i)) {
val pokemonNBT = nbt.getCompound(DataKeys.STORE_SLOT + i)
try {
pokemon.add(Pokemon().loadFromNBT(pokemonNBT))
} catch(_: InvalidSpeciesException) {
handleInvalidSpeciesNBT(pokemonNBT)
}
pokemon.add(Pokemon().loadFromNBT(nbt.getCompound(DataKeys.STORE_SLOT + i)))
}
return this
}
@@ -76,12 +71,7 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
override fun loadFromJSON(json: JsonObject): BottomlessStore {
var i = -1
while (json.has(DataKeys.STORE_SLOT + ++i)) {
val pokemonJSON = json.getAsJsonObject(DataKeys.STORE_SLOT + i)
try {
pokemon.add(Pokemon().loadFromJSON(pokemonJSON))
} catch (_: InvalidSpeciesException) {
handleInvalidSpeciesJSON(pokemonJSON)
}
pokemon.add(Pokemon().loadFromJSON(json.getAsJsonObject(DataKeys.STORE_SLOT + i)))
}
return this
}
@@ -102,16 +92,11 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
this.pokemon.add(pokemon)
storeChangeObservable.emit(Unit)
} else if (position.currentIndex in 0 until this.pokemon.size) {
var startIndex = position.currentIndex;
if (pokemon != null) {
this.pokemon.add(position.currentIndex, pokemon)
startIndex += 1;
} else {
this.pokemon.removeAt(position.currentIndex)
}
for(i in startIndex until this.pokemon.size) {
this.pokemon[i].storeCoordinates.set(StoreCoordinates(this, BottomlessPosition(i)))
}
storeChangeObservable.emit(Unit)
}
}
@@ -1,19 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.storage
import net.minecraft.util.Identifier
/**
* An exception when trying to deserialize a Pokemon instance with an identifier that doesn't seem to be in the registry.
*
* @author Hiroku
* @since October 9th, 2023
*/
class InvalidSpeciesException(val identifier: Identifier) : IllegalStateException("Invalid species: $identifier")
@@ -146,10 +146,8 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
operator fun get(uuid: UUID) = find { it.uuid == uuid }
open fun handleInvalidSpeciesNBT(nbt: NbtCompound) {}
abstract fun saveToNBT(nbt: NbtCompound): NbtCompound
abstract fun loadFromNBT(nbt: NbtCompound): PokemonStore<T>
open fun handleInvalidSpeciesJSON(json: JsonObject) {}
abstract fun saveToJSON(json: JsonObject): JsonObject
abstract fun loadFromJSON(json: JsonObject): PokemonStore<T>
abstract fun savePositionToNBT(position: T, nbt: NbtCompound)
@@ -121,12 +121,8 @@ class ReforgedConversion(val base: Path) : CobblemonConverter<NbtCompound> {
evs[Stats.SPECIAL_DEFENCE] = nbt.getInt("EVSpecialDefense")
evs[Stats.SPEED] = nbt.getInt("EVSpeed")
ivs.forEach { stat ->
result.setIV(stat.key, stat.value)
}
evs.forEach { stat ->
result.setEV(stat.key, stat.value)
}
result.evs = evs
result.ivs = ivs
for (move in nbt.getList("Moveset", 10)) {
val compound = move as NbtCompound
@@ -1,102 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.storage.adapter.database
import com.cobblemon.mod.common.api.storage.PokemonStore
import com.cobblemon.mod.common.api.storage.StorePosition
import com.cobblemon.mod.common.api.storage.adapter.CobblemonAdapterParent
import com.cobblemon.mod.common.api.storage.adapter.flatfile.FileStoreAdapter
import com.cobblemon.mod.common.api.storage.adapter.flatfile.JSONStoreAdapter
import com.cobblemon.mod.common.api.storage.adapter.flatfile.NBTStoreAdapter
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.util.server
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.mongodb.client.MongoClient
import com.mongodb.client.MongoCollection
import com.mongodb.client.model.ReplaceOptions
import net.minecraft.util.WorldSavePath
import org.bson.Document
import java.util.UUID
/**
* A [FileStoreAdapter] for MongoDB. This allows a [PokemonStore] to be serialized to a MongoDB database.
*
* @author Pebbles
* @since August 23rd, 2023
*/
@Suppress("MemberVisibilityCanBePrivate")
open class MongoDBStoreAdapter(
protected val mongoClient: MongoClient,
protected val databaseName: String,
) : CobblemonAdapterParent<JsonObject>(), FileStoreAdapter<JsonObject> {
protected val gson: Gson = this.createGson()
override fun <E : StorePosition, T : PokemonStore<E>> serialize(store: T): JsonObject = store.saveToJSON(JsonObject())
override fun save(storeClass: Class<out PokemonStore<*>>, uuid: UUID, serialized: JsonObject) {
val document = Document.parse(this.gson.toJson(serialized))
document["uuid"] = uuid.toString()
val collection = getCollection(storeClass)
val filter = Document("uuid", uuid.toString())
collection.replaceOne(filter, document, ReplaceOptions().upsert(true))
}
override fun <E : StorePosition, T : PokemonStore<E>> provide(storeClass: Class<T>, uuid: UUID): T? {
val server = server()!!
val pokemonStoreRoot = server.getSavePath(WorldSavePath.ROOT).resolve("pokemon").toFile()
val jsonAdapter = JSONStoreAdapter(
pokemonStoreRoot.absolutePath,
useNestedFolders = true,
folderPerClass = true
)
val nbtAdapter = NBTStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
// 1. Check if data exists in MongoDB
val collection = getCollection(storeClass)
val filter = Document("uuid", uuid.toString())
val document = collection.find(filter).first()
if (document != null) {
val json = this.gson.fromJson(document.toJson(), JsonObject::class.java)
val store = storeClass.getConstructor(UUID::class.java).newInstance(uuid)
store.loadFromJSON(json)
return store
}
// 2. Fallback to checking JSON and NBT
val nbtStore = nbtAdapter.provide(storeClass, uuid)
if (nbtStore != null) {
save(storeClass, uuid, serialize(nbtStore))
return nbtStore
}
val jsonStore = jsonAdapter.provide(storeClass, uuid)
if (jsonStore != null) {
save(storeClass, uuid, serialize(jsonStore))
return jsonStore
}
return null
}
protected open fun createGson(): Gson = Gson()
protected open fun getCollection(storeClass: Class<out PokemonStore<*>>): MongoCollection<Document> {
val collectionName = when (storeClass) {
PlayerPartyStore::class.java -> "PlayerPartyCollection"
PCStore::class.java -> "PCCollection"
else -> "OtherCollection"
}
return this.mongoClient.getDatabase(this.databaseName).getCollection(collectionName)
}
}
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.storage.adapter.flatfile
package com.cobblemon.mod.common.api.storage.adapter.flatifle
import com.cobblemon.mod.common.api.storage.PokemonStore
import com.cobblemon.mod.common.api.storage.StorePosition
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.storage.adapter.flatfile
package com.cobblemon.mod.common.api.storage.adapter.flatifle
import com.cobblemon.mod.common.api.storage.PokemonStore
import com.cobblemon.mod.common.api.storage.StorePosition
@@ -48,11 +48,7 @@ open class JSONStoreAdapter(
val br = BufferedReader(FileReader(file))
val json = gson.fromJson<JsonObject>(br)
br.close()
val store = try {
storeClass.getConstructor(UUID::class.java, UUID::class.java).newInstance(uuid, uuid)
} catch (exception: NoSuchMethodException) {
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
}
val store = storeClass.getConstructor(UUID::class.java).newInstance(uuid)
store.loadFromJSON(json)
store
} catch (e: Exception) {
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.storage.adapter.flatfile
package com.cobblemon.mod.common.api.storage.adapter.flatifle
import com.cobblemon.mod.common.api.storage.PokemonStore
import com.cobblemon.mod.common.api.storage.StorePosition
@@ -31,12 +31,7 @@ open class NBTStoreAdapter(
override fun <E : StorePosition, T : PokemonStore<E>> serialize(store: T) = store.saveToNBT(NbtCompound())
override fun save(file: File, serialized: NbtCompound) = NbtIo.writeCompressed(serialized, file)
override fun <E, T : PokemonStore<E>> load(file: File, storeClass: Class<out T>, uuid: UUID): T? {
val store = try {
storeClass.getConstructor(UUID::class.java, UUID::class.java).newInstance(uuid, uuid)
} catch (exception: NoSuchMethodException) {
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
}
val store = storeClass.getConstructor(UUID::class.java).newInstance(uuid)
return try {
val nbt = NbtIo.readCompressed(file)
store.loadFromNBT(nbt)
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.storage.adapter.flatfile
package com.cobblemon.mod.common.api.storage.adapter.flatifle
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.storage.PokemonStore

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