Compare commits

..
Author SHA1 Message Date
apion 1fde1e4428 Merged main into 1.20.4 2024-04-25 10:18:17 -04:00
apion a2d574300c Merge branch 'main' into 1.20.4
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/block/TumblestoneBlock.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/chest/GildedChestBlock.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/entity/GildedChestBlockEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/Summary.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
#	forge/src/main/java/com/cobblemon/mod/forge/mixin/LootManagerMixin.java
#	gradle/libs.versions.yml
2024-04-25 08:31:40 -04:00
Deltric 00eca1c019 fix: battle gui rendering bugs and removed old method 2024-03-17 01:02:43 -05:00
Deltric babfb43750 Merge remote-tracking branch 'origin/1.20.4' into 1.20.4
# Conflicts:
#	common/build.gradle.kts
#	common/src/main/kotlin/com/cobblemon/mod/common/block/chest/GildedChestBlock.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/startselection/StarterSelectionScreen.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
2024-03-17 00:25:21 -05:00
Deltric 3aa94fcd2a fixed two leftover errors from merging in main 2024-03-17 00:19:25 -05:00
Deltric 20a866a0e3 draft: rough port with some bugs to fix and code to clean up. 2024-03-16 23:39:29 -05:00
Deltric 67f2146c3c remove baked in adorn for compatibility mod 2024-03-16 23:27:16 -05:00
Deltric ad49106132 initial gradle changes 2024-03-16 23:27:09 -05:00
Deltric 064df9c26e draft: rough port with some bugs to fix and code to clean up. 2024-03-16 23:15:12 -05:00
Deltric ea2936f204 remove baked in adorn for compatibility mod 2024-03-02 17:44:25 -06:00
Deltric 9d7eef9bcd initial gradle changes 2024-03-02 17:18:50 -06:00
1597 changed files with 5203 additions and 10882 deletions
-3
View File
@@ -21,8 +21,6 @@ build
# other
eclipse
run
runClient
runServer
# Files from Forge MDK
forge*changelog.txt
@@ -36,7 +34,6 @@ common/src/main/resources/assets/cobblemon/showdown
*/generated/resources/*
/fabric/src/generated/resources/cobblemon-common.accesswidener
/.ignored/
common/.fabric
# MacOS
.DS_Store
+71 -118
View File
@@ -1,80 +1,42 @@
# Changelog
## [1.5.1](#1-5-1)
### Additions
- Added unique send out particles for Cherish, Dream, Beast, and Ancient Origin balls.
- Made Wooloo and Dubwool dye-able like sheep
### Changes
- Sounds for Relic Coin Sacks have been correctly renamed. Relic Coin Pouches received new sounds for breaking and placing.
- Readjusted Petilil portraits
- Changed "dimensions" spawn condition to check with dimension IDs instead of effects, so custom dimension IDs can be used.
### Fixes
- Fixed a bug in which adding organic material to the fossil tank via right click was adding the full count of the stack currently in hand
- Fixed a niche issue where some properties of entities were not initialized correctly, causing Pokémon that appeared to be level 1 until you battle them.
- Fixed Fossilized Drake being missing from the Fossils item tag.
- Fixed Gilded Chest block entity not being cleared on block break
- Fixed sherd brokenness on Forge/Supplementaries incompat
- Fixed Fossil Compartment crash with Jade / WAILA forks
- Fixed PCStore#resize not allowing PC boxes size reduction.
- Fixed pasture block PC lookups when the player is offline.
- Fixed an untranslated battle message that occurs when using a move that just ran out of PP (e.g. Fire Blast that just got spited mid-turn).
- Fixed held items being eaten even when the held item evolutions are already unlocked.
- Fixed Hisuian Decidueye not being Grass/Fighting
- Fixed both Decidueye forms learning both Triple Arrows and Spirit Shackle
- Fixed Pineco being unable to evolve into Shulker Forretress
- Fixed Kabutops T-posing when underwater. It still does not have proper swimming animations yet though.
- Fixed Pidgey's missing walk animation.
- Fixed recoil evo condition.
- Fixed issue where poser debug tools didnt work on JSON posers
- Fixed issue where gilded chests dont close when going far away
### Developer
- Changed SpawnAction#complete to return a nullable generic R (the spawn action result) instead of a boolean. More information this way.
### Data Pack & Resource Pack Creators
- Added support for MoLang conditions for quirks and poses.
- Changed the AttackDefenceRatio requirement to StatCompare and StatEqual.
## [1.5.0 - The Ruins and Revival Update (May 12th, 2024)](#1-5-0)
#### "You're telling me that Mojang has added archaeology to the game? Hmm... that gives me an idea."
## [1.5.0](#1-5-0)
### Additions
- Added 17 ruin structures, where you can find Pokémon-themed Armor Trims and Pottery Sherds, Tumblestones, and more.
- Added 23 fossil structures, where you can brush Suspicious Sand/Gravel for [Fossils](https://wiki.cobblemon.com/index.php/Fossil). You can add [custom fossils using datapacks](https://wiki.cobblemon.com/index.php/Fossils_File).
- Added the Data Monitor, Fossil Compartment, and Restoration Tank blocks. Placed in the correct formation, you can use these to create a [Restoration Machine](https://wiki.cobblemon.com/index.php/Resurrection_Machine) where you can bring fossils back to life. Use organic material to fill the tank first!
- Added 3 variants of [Tumblestone](https://wiki.cobblemon.com/index.php/Tumblestone), which can be planted near Lava or Magma to grow harvestable Tumblestone Clusters.
- Added 22 fossil structures, where you can brush Suspicious Sand/Gravel for Fossils.
- Added the Data Monitor, Fossil Compartment, and Restoration Tank. Placed in the correct formation, you can use these to create a Restoration Machine where you can bring fossils back to life.
- Added 3 variants of Tumblestones, which can be planted near Lava or Magma to grow harvestable Tumblestone Clusters.
- Added Tumblestone Blocks, a storage block crafted from 9 Tumblestones.
- Added Ancient Poké Balls, which are aesthetic variants crafted from Tumblestones.
- Added the Ancient Feather, Wing, and Jet Balls, which fly further than regular Poké Balls.
- Added the Ancient Heavy, Leaden, and Gigaton Balls, which are heavier and don't fly as far as regular Poké Balls. These will receive more functionality in a future update.
- Added visual effects for many Poké Balls when sending out or capturing Pokémon.
- Added a new send-out ball toss animation that showcases the ball used for the Pokémon.
- Added simple move animations for Pokémon in battle with a proof of concept (Flamethrower) for more complicated animations. More and more specific animations will come in future updates.
- Added Original Trainer to the summary menu. To edit the OT through commands, you must specify originaltrainertype=<Player/NPC>, and originaltrainer=<Username or UUID/NPC Name>
- Added Original Trainer functionality. To edit the OT through commands, you must specify originaltrainertype=<Player/NPC>, and originaltrainer=<Username or UUID/NPC Name>
- Added support for planting Apricorn trees, berries, and mints in [Botany Pots](https://modrinth.com/mod/botany-pots)
- Added the hidden "True Vivillionaire" advancement.
- Added the [Display Case](https://wiki.cobblemon.com/index.php/Display_Case) block. Use it to display your archaeological findings, Poké Balls, or other items.
- Added 6 Pokémon-themed Pottery Sherds, obtained from ruin structures. Sherds... sh-erds. Weird word.
- The "Poké Ball" Vivillon variant is now obtainable by evolving a Spewpa after obtaining the "Aspiring Vivillionaire" advancement.
- Added dynamic lighting support for any Species or Forms when using [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) or [Dynamic Lights Reforged](https://www.curseforge.com/minecraft/mc-mods/dynamiclights-reforged), at this time we don't ship with any defaults for this feature but addon creators can make use of this feature.
- Added a Display Case block! Use it to display your archaeological findings, or other items.
- Added 6 Pokémon-themed Pottery Sherds, obtained from ruin structures.
- Added a Pokémon-themed armor trim, obtained from ruin structures.
- Added Nether Fire Stone Ore to, surprisingly, the Nether.
- Added Terracotta Sun Stone Ore to Badlands biomes.
- You can now find Nether Fire Stone Ore in, surprisingly, the Nether.
- You'll also find Terracotta Sun Stone Ore in Badlands biomes.
- Added effects for many Poké Balls when sending out Pokémon or capturing.
- Added a new send-out ball toss animation that showcases the ball used for the Pokémon.
- Added simple move animations for Pokémon in battle with a proof of concept for more complicated animations.
- Added the EV Boosting Feathers.
- Added [Gilded Chests](https://wiki.cobblemon.com/index.php/Gilded_Chest), available in all Apricorn colors.
- Added Relic Coins, Relic Coin Pouches, and Relic Coin Sacks. Gimmighoul might be interested in these.
- Added Type Gems. These will have additional uses later, so you should start collecting them.
- Added various new tags for searchability and better compatibility.
- Added the [Ability Capsule](https://bulbapedia.bulbagarden.net/wiki/Ability_Capsule) and [Patch](https://bulbapedia.bulbagarden.net/wiki/Ability_Patch), these have no acquisition method for now and only work as intended with the traditional ability format of 1 to 2 common abilities and 1 hidden ability.
- Added Gilded Chests, available in all Apricorn colors.
- Added Relic Coins, Relic Coin Pouches, and Relic Coin Sacks.
- Added Type Gems. These items hold no functionality for now. Start collecting though...
- Added cries to Meowth, Alolan Meowth, Galaraian Meowth,
- Completed the advancement for placing a Tumblestone near Lava or Magma to make it grow.
- Added new held items: Ability Shield, Absorb Bulb, Air Balloon, Binding Band, Blunder Policy, Cell Battery, Covert Cloak, Damp Rock, Eject Button, Eviolite, Expert Belt, Float Stone, Focus Sash, Heat Rock, Icy Rock, Iron Ball, Light Ball, Loaded Dice, Red Card, Shell Bell, Sticky Barb, Smooth Rock, Soothe Bell and Weakness Policy.
- Added Hisui starters to the starter selection menu. They come inside of ancient Poké Balls.
- Added a bubble quirk to Krabby that only plays during clear sunsets. It looks familiar...
- Added Hisui starters to the starter selection menu.
- Added bubble quirk to Krabby that only play during clear sunset's.
- Added forfeit option to PVP battles.
- Added visual changes for entities affected by Illusion, Imposter, or Transform in battle.
- Added shoulder mounting for Squirtle, Ralts, Roggenrola, Charcadet, Sizzlipede, Litwick, Cutiefly, Flabebe, Flittle, and Scatterbug
- Added species data for Hydrapple, Iron Boulder, Iron Crown, Pecharunt, Raging Bolt, and Terapagos
- Added various new tags for search-ability and better mod compatibility.
- Added the [Ability Capsule](https://bulbapedia.bulbagarden.net/wiki/Ability_Capsule) and [Patch](https://bulbapedia.bulbagarden.net/wiki/Ability_Patch). These have no way of getting them (for now) and only work as intended with the traditional ability format of 1 to 2 common abilities and 1 hidden ability.
### Pokémon Added
#### Gen 2
@@ -205,7 +167,7 @@
#### Gen 9
- Gimmighoul
- Using Relic Coins, you can increase Gimmighoul's Coin Stash. Upon reaching 999, it can be evolved into Gholdengo.
- Additionally, if you give Gimmighoul a Netherite Scrap, you can increase its Netherite Stash. But what will that do to when you evolve it?
- Additionally, if you give Gimmighoul a Netherite Scrap, you can increase its Netherite Stash. Who knows what might happen if it fills up?
- Gholdengo
#### Cobblemon Exclusives
@@ -218,8 +180,6 @@
- Hisui Bias Goomy
- Hisui Bias Petilil
These are our compromise for having both regular and Hisuian forms of these Pokémon in the game. Hisui Biased variations will evolve into the Hisuian evolutions under most conditions. They are otherwise purely cosmetic differences.
### Added cries to the following Pokémon
- Alolan Raticate
- Vulpix, Ninetales
@@ -285,86 +245,82 @@ These are our compromise for having both regular and Hisuian forms of these Pok
### Changes
- Alolan Pikachu and Exeggcute changed to Alola Bias.
- Alolan Pikachu and Exeggcute changed to Alola bias.
- Pokémon now transition more smoothly between different poses.
- Updated models for Mimikyu, Alcremie, Squirtle, Wartortle, Blastoise, Pidgey, Pidgeotto, Pidgeot, Omanyte, Omastar, Growlithe, Arcanine, Nidoran Male, Nidoran Female, Lapras, Swinub, Piloswine, Basculin, Rowlet, Dartrix, Decidueye, Kabuto, Kabutops, Piplup, Prinplup, Elekid, Electabuzz, Cubone, Marowak, Paras, Mawile, Drifloon, Drifblim, Venonat, Venomoth, Yanma, Yanmega, Psyduck, Dusknoir, Diglett, Dugtrio, Natu, Xatu, Oshawott, Samurott, Torchic, Grovyle, Sceptile, Sizzlipede, Mismagius, and Raboot.
- Updated models for Mimikyu, Alcremie, Squirtle, Wartortle, Blastoise, Pidgey, Pidgeotto, Pidgeot, Omanyte, Omastar, Growlithe, Arcanine, Nidoran Male, Nidoran Female, Lapras, Swinub, Piloswine, Basculin, Rowlet, Dartrix, Decidueye, Kabuto, Kabutops, Piplup, Prinplup, Elekid, Electabuzz, Cubone, Marowak, Paras, Mawile, Drifloon, Drifblim, Venonat, Venomoth, Yanma, Yanmega, Psyduck, Dusknoir, Diglett, Dugtrio, Natu, Xatu, Oshawott, Samurott, Torchic, Grovyle, Sceptile, Sizzlipede, and Raboot.
- Updated animations for Squirtle, Wartortle, Blastoise, Pidgey, Pidgeotto, Diglett, Dugtrio, Magikarp, Dragonite, Omanyte, Omastar, Kabuto, Pinsir, Chinchou, Lanturn, Ralts, Kirlia, Gardevoir, Gallade, Buizel, Garchomp, Pumpkaboo, Falinks, Lechonk, Litwick, Lampent, Chandelure, Carnivine, Galarian Meowth, Galarian Rapidash, Venonat, Venomoth, Phanphy, Donphan, Dratini, Dragonair, Happiny, Chansey, Blissey, Volbeat, Illumise, Numel, Sizzlipede, Centiskorch, Obstagoon, Timburr, Gurdurr, Conkeldurr, Drifloon, and Drifblim.
- Resized Drifloon.
- Added more held items to the held item tag, and Metal Coat to the held item tab.
- Added all ores to the modloader ore tags, and added tags for each ore type to the mod (both blocks and items).
- Changed the Destiny Knot to be crafted with a Ghast Tear rather than a diamond. We have big plans for the Destiny Knot...
- You can now punch grown apricorns to harvest them. We saw so many content creators breaking apricorns with their fists that it was making us die inside.
- The "Poké Ball" Vivillon variant is now obtainable by evolving a Spewpa after obtaining the "Aspiring Vivillionaire" advancement.
- Changed the Destiny Knot to be crafted with a Ghast Tear, rather than a diamond.
- You can now punch grown apricorns to harvest them.
- Renamed the "Vivillionaire" advancement to "Aspiring Vivillionaire"
- Removed the Inferno, Forsaken, Poké Ball, and Void patterns from the Aspiring Vivillionaire advancement.
- Updated the biome tags for Spewpa's evolutions into the various Vivillon patterns. You should mostly be getting one evolution option at a time now.
- Updated movesets to add Gen 9 DLC moves.
- Apricorn Sprouts can now be planted directly on Apricorn leaves to create a new Apricorn of the same color.
- Added tooltips to interaction GUIs to make it clearer what they do.
- Poké Ball recipes now use tags for their center ingredient, rather than specifically Copper Ingots, Iron Ingots, Gold Ingots, and Diamonds, allowing for further datapack customisation and mod compatibility.
- Starter Toast now closes once the corresponding button is pressed once.
- Moved some items into the vanilla Food & Drinks category.
- Poké Ball recipes now use tags for their center ingredient, rather than specificied Copper Ingot, Iron Ingot, Gold Ingot, and Diamond, allowing for further datapack customisation.
- Starter Toast now closes once corresponding button is pressed once.
- Moved some items in the vanilla Food & Drinks category.
- Stacked Cobblemon Potion Bases can now be quick moved into Brewing Stands.
- Changed the Healing Machine recipe to swap the Max Revive for a Revive so that it is a bit easier to craft.
- Upped the rate that Gastly drop Ghast Tears from 2.5% to 5%, further helping the crafting of a Healing Machine.
- Changed Healer recipe to swap the Max Revive for a Revive.
- Upped the rate that Gastly drop Ghast Tears from 2.5% to 5%.
- Pokémon with a low Blaze Powder drop chance now drop it more frequently in the Nether.
- Reorganised a few advancements.
- Pokémon entity shadows now scale when the Pokémon is being sent out or recalled.
- Remade Torkoal particles, now they only appear when in battle.
- Improved the shiny icon within summary and PC interfaces.
- Changed the Poison Barb sprite so that it doesn't look like a sword when held. It's a Poison Barb, not a Poison Sword. Wait, that gives me an idea.
- Moon Stone ore generates more frequently in Taigas and has been added to Magical and Spooky biomes. Ooo... Sorry.
- Updated shiny icon within summary and PC interfaces.
- Changed the Poison Barb sprite so that it doesn't look like a sword when held.
- Moon Stone ore generates more frequently in Taigas and has been added to Magical and Spooky biomes.
- Density, Season, and Nether biome tags have been moved and renamed within the biome tags directory.
- Added particles to Lucario when in battle. Lucario is special.
- Movesets updated for Blueberry DLC.
- Added particles to Lucario when in battle.
### Fixes
- Re-added the recipe for the Iron vitamin. Well, technically it's a mineral.
- Resized Snorlax
- Re-added the recipe for the Iron Vitamin.
- Cleaned up empty evolutions declaration in species files (no, I'm not listing all 169 by name).
- Fixed Exeggcute and Pikachu not being able to evolve.
- Fixed evolution priority for Exeggcute and Pikachu regional evolutions. Using a thunder stone on Kantonian Pikachu on the beach will give Alolan Raichu. A second thunder stone would then give Kantonian Raichu.
- Fixed battle text for the Poison Touch Ability.
- Finally found Sudowoodo and Bonsly. They were missing spawn data. Same with Drifloon and Litwick. Uh... sorry about that.
- Fixed battle lang for Poison Touch Ability
- Fixed Cubone not being able to evolve into Alolan Marowak.
- Fixed Squawkabilly forms not being recognized.
- Fixed the bounce animation for Poké Balls being delayed by like, a full second. It's enough to drive you mad.
- Note blocks now play the correct sound on the Apricorn Plank-based blocks.
- Fixed the bounce animation for Poké Balls being delayed by like, a full second.
- Note blocks now play the correct sound on the Apricorn Plank based blocks.
- Fixed an issue with transformed parts in posers causing whacky positional issues.
- Fixed Poké Balls freezing in an open state if you're looking away at the moment that it's meant to close. This actually fixes some other things but it's hard to explain.
- Fixed thrown Poké Balls technically having no name.
- Fixed very many issues with Bedrock particle effects. There are probably many more. Life goes on.
- Fixed Arbok patterns not being as specific in spawning as they were intended to be.
- Fixed Poké Balls freezing in an open state if you look away when it's meant to close.
- Adds a translation string name to the Empty Pokeball entity.
- Fixed very many issues with Bedrock particle effects.
- Fixed Arbok patterns not being as specific in spawning as they were intended.
- Fixed Pokémon not avoiding danger at all when pathfinding.
- Fixed Pokémon pathing over snow layers and carpets.
- Fixed the Sobble line not being able to swim or breathe underwater.
- Fixed mod incompatibility with [Just Enough Resources](https://modrinth.com/mod/just-enough-resources-jer).
- Fixed mod incompatibility with [Visual Overhaul](https://modrinth.com/mod/visual-overhaul).
- Fixed an issue that allowed Pokémon to spawn on rails.
- Fixed Sobble line not being able to swim or breathe underwater.
- Fixed incompatibility with [Just Enough Resources](https://modrinth.com/mod/just-enough-resources-jer).
- Fixed incompatibility with [Visual Overhaul](https://modrinth.com/mod/visual-overhaul).
- Fixed an issue that allowed Pokemon to spawn on rails.
- Fixed the data set on Pokémon when abilities are given via properties such as in ``/spawnpokemon``.
- Fixed Pokémon sometimes losing hidden abilities when evolving. This fix will only take effect for newly created Pokémon because of technical reasons that were explained to me but which I then forgot.
- Fixed capture messages not displaying in battle.
- Fixed the Illusion ability causing the wrong Pokémon to be targeted in battle.
- Fixed battles started with fainted party members causing a soft-lock on defeat.
- Fixed some issues with eyes in the faint animations of Hitmonlee and Phantump
- Fixed missing evolution moves, for example Stone Axe for Kleavor.
- Cleaned up empty evolutions declaration in species data (And no, I'm not listing all 169. No I don't care what you pay me, I'm not doing it).
- Fixed non-consumable held items being consumed or swapped in battle permanently.
- Fixed sometimes Pokémon losing hidden abilities when evolving, this fix will only take effect to newly created Pokémon.
- Fixed capture messages not displaying.
- Fixed illusion ability causing the wrong Pokémon to be targeted in battle.
- Battles starting with fainted party members no longer cause a softlock on defeat.
- Fixed some issues with eyes in the faint animations for Hitmonlee and Phantump
### Developer
- Significantly changed the way the properties in PokemonEntity work. This could break some plugins (not in a big way, but changes will be needed for some things).
- Rebuilt the scheduling API to more clearly force side choices and temporal frames of reference for tasks. The developers say that how it works is cool but I stopped listening.
- Added dialogue API and data registry. This is powerful, and there is a full example in the Cobblemon mod you can trigger using /opendialogue \<username\> cobblemon:example. The example JSON is inside the data folder of the mod.
- Significantly changed the way properties in PokemonEntity work. This could break some plugins (not in a big way, but changes will be needed).
- Rebuilt the scheduling API to more clearly force side choices and temporal frames of reference for tasks.
- Added dialogue API and data registry.
- Opened up the ItemDropEntry class to allow for it to be extended.
- Added a new "advancement" evolution variant that takes an advancement identifier and succeeds if the player has the advancement.
- Made the spawning API capable of non-entity spawning. You can make it 'spawn' commands if you want. Go nuts.
- Made PokemonProperties.parse more interoperable with Java. Save yourself and stop using Java.
- Made spawning API capable of non-entity spawning
- Made PokemonProperties.parse more interoperable with Java.
- Added the ``HeldItemEvent``, this comes with 2 implementation ``HeldItemEvent.Pre`` and ``HeldItemEvent.Post``.
- API to support dynamic lighting in any mod that implements this feature has been added to all Species & Forms, see LightingData class for details.
- Added a Mixin for the BrewingStandScreenHandler to fix the incorrect handling of quickMove in Fabric.
- Corrections to the ability implementation have been made that make ``Ability.forced`` function as intended and never reroll an ability for a Pokémon, please check your implementations to ensure the corrected behavior is intended for your use case.
- ``Pokemon.ability`` no longer has a public setter. Please migrate to using ``Pokemon#updateAbility``, this handles the ability coordinate storage for you when necessary.
- ``Pokemon.ability`` no longer has a public setter, please migrate to using ``Pokemon#updateAbility``, this handles the ability coordinate storage for you when necessary.
- ``Ability.forced``, ``Ability.index`` and ``Ability.priority`` have all had their setters internalized, there is no longer any need to manually adjust these migrate to the method mentioned above to handle that process for you.
- ``AbilityChanger`` has been added to API alongside some implementations, this is subject to change as the ability capsule and patch currently only expect the traditional behaviour in the Pokémon games of 1 or 2 regular abilities and 1 hidden ability.
- ``Pokemon#rollAbility`` has been added which rerolls for a legal ability for the Pokémon.
- `docs/cobblemon-tags` tags and spawn-presets can now be auto-generated from the current tags in the Cobblemon mod with the `generateSpawnPresetList.py` and `generateTagList.py` scripts.
- ``AbilityChanger`` has been added to API alongside some implementations, this is subject to change as the ability capsule and patch currently only expect the traditional behaviour in the Pokémon games of 1 to 2 regular abilities and 1 hidden.
- ``Pokemon#rollAbility`` has been added, this rerolls a legal ability for the Pokémon.
- `docs/cobblemon-tags` tags and spawn-presets can now be auto-generated from the current tags in the cobblemon mod with the `generateSpawnPresetList.py` and `generateTagList.py` scripts.
- `public_spawns_to_json.py` script has been added to give the powerful spawn.json generator a simplistic UI.
- Updated Showdown with Gen 9 DLC.
- Changed mixins to be Java 17 for compatibility level instead of 16, removing a warning from startup logs.
- Updated Showdown with Gen 9 DLC.
### Datapack & Resourcepack Creators
- The maximum amount of fossils that can fit in the Fossil Analyzer can be adjusted in the config.
@@ -534,7 +490,6 @@ These are our compromise for having both regular and Hisuian forms of these Pok
Thank you so much to all of our community translators that bring the mod to the rest of the world!
## [1.4.0 - The Friends and Farms Update (October 13th, 2023)](#1-4-0)
#### "No, we don't provide the friends."
### 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).
@@ -1075,7 +1030,6 @@ Thank you so much to all of our community translators that bring the mod to the
- All the translators that contributed are amazing.
## [1.3.0 - The Foundation Update (March 17th, 2023)](#1-3-0)
#### "Now we can start doing the really cool stuff."
### Dependencies
- Upgraded Fabric API dependence to 0.75.1+1.19.2
@@ -1225,7 +1179,6 @@ Thank you so much to all of our community translators that bring the mod to the
- Thank you to all of the fantastic volunteer translators for taking the time to help with this!
## [1.2.0 - The Customization Update (January 1st, 2023)](#1-2-0)
#### "There are going to be so many fakemon..."
### Additions
- Added models for Natu and Xatu, Murkrow and Honchkrow, Wailmer and Wailord.
- Added new PC interface and it is beautiful.
@@ -1339,4 +1292,4 @@ Thank you so much to all of our community translators that bring the mod to the
- Added species, ability, and move translations for `ko_ko`, `jp_jp`, `fr_fr`, `es_es`, `it_it`, and `zh_cn`.
## [1.0.0 (November 12th, 2022)](#1-0-0)
- Initial release.
- Initial release.
@@ -11,12 +11,10 @@ repositories {
dependencies {
implementation(libs.kotlin)
implementation(libs.indra.common)
implementation(libs.licenser)
implementation(libs.shadow)
implementation(libs.loom)
implementation(libs.architectury)
implementation(libs.architecturyPlugin)
implementation(libs.blossom)
implementation(libs.ideaExt)
+10
View File
@@ -0,0 +1,10 @@
import ca.stellardrift.build.configurate.ConfigFormats
import ca.stellardrift.build.configurate.catalog.PolyglotVersionCatalogExtension
plugins {
id("ca.stellardrift.polyglot-version-catalogs") version "5.0.1"
}
extensions.configure<PolyglotVersionCatalogExtension> {
from(ConfigFormats.YAML, file("../gradle/libs.versions.yml"))
}
@@ -9,31 +9,18 @@
*/
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import utilities.ACCESS_WIDENER
val accessWidenerFile = "src/main/resources/cobblemon-common.accesswidener"
plugins {
id("java")
id("java-library")
id("net.kyori.indra")
id("net.kyori.indra.git")
java
`java-library`
id("org.cadixdev.licenser")
id("dev.architectury.loom")
id("architectury-plugin")
kotlin("jvm")
}
group = rootProject.group
version = rootProject.version
description = rootProject.description
indra {
javaVersions {
minimumToolchain(17)
target(17)
}
}
repositories {
mavenCentral()
//JEI
@@ -45,6 +32,11 @@ license {
header(rootProject.file("HEADER"))
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
architectury {
minecraft = project.property("mc_version").toString()
}
@@ -65,7 +57,8 @@ dependencies {
tasks {
withType<JavaCompile> {
options.compilerArgs.add("-Xlint:-processing,-classfile,-serial")
options.encoding = "UTF-8"
options.release.set(17)
}
withType<KotlinCompile> {
@@ -0,0 +1,32 @@
plugins {
id("cobblemon.base-conventions")
id("com.github.johnrengelman.shadow")
}
val bundle: Configuration by configurations.creating {
isCanBeConsumed = false
isCanBeResolved = true
}
tasks {
jar {
archiveBaseName.set("Cobblemon-${project.name}")
archiveClassifier.set("dev-slim")
}
shadowJar {
archiveClassifier.set("dev-shadow")
archiveBaseName.set("Cobblemon-${project.name}")
configurations = listOf(bundle)
mergeServiceFiles()
}
remapJar {
dependsOn(shadowJar)
inputFile.set(shadowJar.flatMap { it.archiveFile })
archiveBaseName.set("Cobblemon-${project.name}")
archiveVersion.set("${rootProject.version}")
}
}
@@ -1,9 +1,9 @@
import utilities.VersionType
import utilities.writeVersion
import extensions.VersionType
import extensions.writeVersion
plugins {
id("java")
id("java-library")
java
`java-library`
id("maven-publish")
id("dev.architectury.loom")
}
@@ -0,0 +1,21 @@
plugins {
`java-library`
}
tasks {
val collectJars by registering(Copy::class) {
val tasks = subprojects.filter { it.path != ":common" }.map { it.tasks.named("remapJar") }
dependsOn(tasks)
from(tasks)
into(buildDir.resolve("libs"))
}
assemble {
dependsOn(collectJars)
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
@@ -1,3 +1,2 @@
package utilities
const val ACCESS_WIDENER = "src/main/resources/cobblemon-common.accesswidener"
@@ -1,4 +1,4 @@
package utilities
package extensions
import org.gradle.api.Project
+3 -2
View File
@@ -1,12 +1,13 @@
plugins {
base
id("cobblemon.root-conventions")
id ("net.nemerosa.versioning") version "2.8.2"
}
group = "com.cobblemon.mod"
version = "${project.property("mod_version")}+${project.property("mc_version")}"
val isSnapshot = project.property("snapshot")?.equals("true") ?: false
if (isSnapshot) {
val fixedBranchName = versioning.info.branch.substringAfter("/")
version = "$version-${fixedBranchName}-${versioning.info.build}"
version = "$version-${versioning.info.branch}-${versioning.info.build}"
}
+23 -19
View File
@@ -1,5 +1,5 @@
import utilities.isSnapshot
import utilities.version
import extensions.isSnapshot
import extensions.version
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
@@ -20,7 +20,7 @@ plugins {
id("net.kyori.blossom")
id("org.jetbrains.gradle.plugin.idea-ext")
id("net.nemerosa.versioning") version "2.8.2"
id ("net.nemerosa.versioning") version "2.8.2"
}
architectury {
@@ -34,26 +34,29 @@ repositories {
}
dependencies {
implementation(libs.bundles.kotlin)
modImplementation(libs.fabric.loader)
implementation(libs.stdlib)
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)
modCompileOnly(libs.lambDynamicLights) { isTransitive = false }
// Integrations
compileOnlyApi(libs.jei.api)
modCompileOnly(libs.bundles.fabric.integrations.compileOnly) {
isTransitive = false
}
// Flywheel has no common dep so just pick one and don't use any platform specific code in common
// modCompileOnly(libs.flywheelFabric)
// Showdown
// For Showdown
modCompileOnly(libs.graal)
// Data Storage
modCompileOnly(libs.bundles.mongo)
//shadowCommon group: 'commons-io', name: 'commons-io', version: '2.6'
// Unit Testing
testImplementation(libs.bundles.unitTesting)
// For datastore
modCompileOnly(libs.mongoDriverCore)
modCompileOnly(libs.mongoDriverSync)
testImplementation(libs.fabricJunit)
testImplementation(libs.junitParams)
testImplementation(libs.mockito)
testImplementation(libs.mockk)
testImplementation(libs.classgraph)
}
tasks.withType<Test> {
@@ -89,7 +92,8 @@ sourceSets {
property("gitCommit", versioning.info.commit)
property("branch", versioning.info.branch)
System.getProperty("buildNumber")?.let { property("buildNumber", it) }
property("timestamp", OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss")) + " UTC")
}
}
}
}
}
@@ -26,6 +26,7 @@ public final class CrashReportMixin {
cobblemon.add("Is Snapshot", CobblemonBuildDetails.SNAPSHOT);
cobblemon.add("Git Commit", CobblemonBuildDetails.INSTANCE.smallCommitHash() + " (" + "https://gitlab.com/cable-mc/cobblemon/-/commit/" + CobblemonBuildDetails.GIT_COMMIT + ")");
cobblemon.add("Branch", CobblemonBuildDetails.BRANCH);
cobblemon.add("Timestamp", CobblemonBuildDetails.TIMESTAMP);
cobblemon.addStackTrace(builder);
builder.append("\n\n");
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.CobblemonClient;
import net.minecraft.client.render.entity.EntityRenderDispatcher;
import net.minecraft.client.render.entity.EntityRenderer;
import net.minecraft.client.util.SkinTextures;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.resource.ResourceManager;
import org.spongepowered.asm.mixin.Mixin;
@@ -24,7 +25,7 @@ import java.util.Map;
@Mixin(EntityRenderDispatcher.class)
public class EntityRenderDispatcherMixin {
@Shadow private Map<String, EntityRenderer<? extends PlayerEntity>> modelRenderers;
@Shadow private Map<SkinTextures.Model, EntityRenderer<? extends PlayerEntity>> modelRenderers;
@Inject(
method = "reload",
@@ -33,4 +34,4 @@ public class EntityRenderDispatcherMixin {
public void resourceManagerReloadHook(ResourceManager resourceManager, CallbackInfo ci) {
CobblemonClient.INSTANCE.onAddLayer(this.modelRenderers);
}
}
}
@@ -21,13 +21,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Mouse.class)
public class PartyScrollMixin {
@Shadow
private double eventDeltaWheel;
private double eventDeltaVerticalWheel;
@Inject(
method = "onMouseScroll",
at = @At(
value = "FIELD",
target="Lnet/minecraft/client/Mouse;eventDeltaWheel:D",
target="Lnet/minecraft/client/Mouse;eventDeltaVerticalWheel:D",
opcode = Opcodes.PUTFIELD,
ordinal = 2,
shift = At.Shift.BEFORE
@@ -36,18 +36,18 @@ public class PartyScrollMixin {
)
public void onMouseScroll(long window, double horizontal, double vertical, CallbackInfo ci) {
if (PartySendBinding.INSTANCE.getWasDown()) {
int i = (int)eventDeltaWheel;
int i = (int)eventDeltaVerticalWheel;
if (i > 0) {
while (i-- > 0) CobblemonClient.INSTANCE.getStorage().shiftSelected(false);
ci.cancel();
eventDeltaWheel = 0;
eventDeltaVerticalWheel = 0;
PartySendBinding.INSTANCE.actioned();
} else if (i < 0) {
while (i++ < 0) CobblemonClient.INSTANCE.getStorage().shiftSelected(true);
ci.cancel();
eventDeltaWheel = 0;
eventDeltaVerticalWheel = 0;
PartySendBinding.INSTANCE.actioned();
}
}
}
}
}
@@ -15,15 +15,8 @@ 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;
@@ -33,9 +26,6 @@ import java.util.*;
@Mixin(StructurePoolBasedGenerator.StructurePoolGenerator.class)
public abstract class StructurePoolGeneratorMixin {
@Final
@Mutable
@Shadow Deque<StructurePoolBasedGenerator.ShapedPoolStructurePiece> structurePieces;
Map<String, Integer> generatedStructureGroupCounts;
@@ -103,7 +93,7 @@ public abstract class StructurePoolGeneratorMixin {
generatedStructureGroupCounts = new HashMap<>();
}
@ModifyVariable(method = "generatePiece", at = @At("STORE"), ordinal = 1)
@ModifyVariable(method = "generatePiece(Lnet/minecraft/structure/PoolStructurePiece;Lorg/apache/commons/lang3/mutable/MutableObject;IZLnet/minecraft/world/HeightLimitView;Lnet/minecraft/world/gen/noise/NoiseConfig;Lnet/minecraft/structure/pool/alias/StructurePoolAliasLookup;)V", at = @At("STORE"), ordinal = 1)
private Iterator<StructurePoolElement> reduceStructurePoolElementIterator(Iterator<StructurePoolElement> iterator) {
List<StructurePoolElement> reducedList = new ArrayList<>();
@@ -123,7 +113,7 @@ public abstract class StructurePoolGeneratorMixin {
return reducedList.iterator();
}
@ModifyVariable(method = "generatePiece", at = @At("STORE"), ordinal = 1)
@ModifyVariable(method = "generatePiece(Lnet/minecraft/structure/PoolStructurePiece;Lorg/apache/commons/lang3/mutable/MutableObject;IZLnet/minecraft/world/HeightLimitView;Lnet/minecraft/world/gen/noise/NoiseConfig;Lnet/minecraft/structure/pool/alias/StructurePoolAliasLookup;)V", at = @At("HEAD"), ordinal = 0, argsOnly = true)
private PoolStructurePiece injected(PoolStructurePiece poolStructurePiece) {
Identifier structureLocationKey = getCobblemonOnlyLocation(poolStructurePiece.getPoolElement());
if (structureLocationKey != null) {
@@ -133,42 +123,42 @@ public abstract class StructurePoolGeneratorMixin {
}
// 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);
}
// @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);
@@ -214,4 +204,4 @@ public abstract class StructurePoolGeneratorMixin {
//
// return reducedList;
// }
}
}
@@ -1,33 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.invoker;
import com.cobblemon.mod.common.integration.adorn.AdornCompatibility;
import java.util.List;
import juuxel.adorn.block.variant.BlockVariantSet;
import juuxel.adorn.block.variant.BlockVariantSets;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//This is a yucky hack but better than what we were doing before
@Mixin(BlockVariantSets.class)
public abstract class AdornRegisterInvoker {
@Final
@Shadow(remap = false)
private static List<BlockVariantSet> variantSets;
@Inject(method = "register()V", at = @At("HEAD"), remap = false)
void register(CallbackInfo ci) {
variantSets.add(AdornCompatibility.INSTANCE);
}
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.mixin.invoker;
import net.minecraft.block.*;
import net.minecraft.resource.featuretoggle.FeatureFlag;
import net.minecraft.sound.BlockSoundGroup;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@@ -19,23 +18,23 @@ import org.spongepowered.asm.mixin.gen.Invoker;
public interface BlocksInvoker {
@Invoker("createLogBlock")
static PillarBlock createLogBlock(MapColor topMapColor, MapColor sideMapColor) {
static Block createLogBlock(MapColor topMapColor, MapColor sideMapColor) {
throw new UnsupportedOperationException();
}
@Invoker("createLeavesBlock")
static LeavesBlock createLeavesBlock(BlockSoundGroup soundGroup) {
static Block createLeavesBlock(BlockSoundGroup soundGroup) {
throw new UnsupportedOperationException();
}
@Invoker("createWoodenButtonBlock")
static ButtonBlock createWoodenButtonBlock(BlockSetType blockSetType, FeatureFlag... requiredFeatures) {
static Block createWoodenButtonBlock(BlockSetType blockSetType) {
throw new UnsupportedOperationException();
}
@Invoker("createFlowerPotBlock")
static FlowerPotBlock createFlowerPotBlock(Block flower, FeatureFlag... requiredFeatures) {
static Block createFlowerPotBlock(Block flower) {
throw new UnsupportedOperationException();
}
}
}
@@ -11,14 +11,13 @@ package com.cobblemon.mod.common.mixin.invoker;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.BlockSetType;
import net.minecraft.block.DoorBlock;
import net.minecraft.sound.SoundEvent;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(DoorBlock.class)
public interface DoorBlockInvoker {
@Invoker("<init>")
static DoorBlock cobblemon$create(AbstractBlock.Settings settings, BlockSetType blockSetType) {
static DoorBlock cobblemon$create(BlockSetType blockSetType, AbstractBlock.Settings settings) {
throw new UnsupportedOperationException();
}
}
}
@@ -11,14 +11,13 @@ package com.cobblemon.mod.common.mixin.invoker;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.BlockSetType;
import net.minecraft.block.PressurePlateBlock;
import net.minecraft.sound.SoundEvent;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(PressurePlateBlock.class)
public interface PressurePlateBlockInvoker {
@Invoker("<init>")
static PressurePlateBlock cobblemon$create(PressurePlateBlock.ActivationRule type, AbstractBlock.Settings settings, BlockSetType blockSetType) {
static PressurePlateBlock cobblemon$create(BlockSetType type, AbstractBlock.Settings settings) {
throw new UnsupportedOperationException();
}
}
}
@@ -11,14 +11,13 @@ package com.cobblemon.mod.common.mixin.invoker;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.BlockSetType;
import net.minecraft.block.TrapdoorBlock;
import net.minecraft.sound.SoundEvent;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(TrapdoorBlock.class)
public interface TrapdoorBlockInvoker {
@Invoker("<init>")
static TrapdoorBlock cobblemon$create(AbstractBlock.Settings settings, BlockSetType blockSetType) {
static TrapdoorBlock cobblemon$create(BlockSetType blockSetType, AbstractBlock.Settings settings) {
throw new UnsupportedOperationException();
}
}
}
@@ -10,6 +10,7 @@ object CobblemonBuildDetails {
const val GIT_COMMIT = "{{ gitCommit | default("Not Specified") }}"
const val BRANCH = "{{ branch | default("Not Specified") }}"
const val TIMESTAMP = "{{ timestamp }}"
fun smallCommitHash(): String {
return this.GIT_COMMIT.substring(0, 7)
@@ -102,6 +102,7 @@ import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.sherds.CobblemonSherds
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.trade.TradeManager
import com.cobblemon.mod.common.util.DataKeys
@@ -178,6 +179,7 @@ object Cobblemon {
if(CobblemonBuildDetails.SNAPSHOT) {
this.LOGGER.info(" - Git Commit: ${smallCommitHash()} (https://gitlab.com/cable-mc/cobblemon/-/commit/${CobblemonBuildDetails.GIT_COMMIT})")
this.LOGGER.info(" - Branch: ${CobblemonBuildDetails.BRANCH}")
this.LOGGER.info(" - Timestamp: ${CobblemonBuildDetails.TIMESTAMP}")
}
implementation.registerPermissionValidator()
@@ -234,7 +236,7 @@ object Cobblemon {
val player = event.player
val block = player.world.getBlockState(event.pos).block
player.party().forEach { pokemon ->
pokemon.lockedEvolutions
pokemon.evolutions
.filterIsInstance<BlockClickEvolution>()
.forEach { evolution ->
evolution.attemptEvolution(pokemon, BlockClickEvolution.BlockInteractionContext(block, player.world))
@@ -415,6 +417,7 @@ object Cobblemon {
).ifSuccessful { it.mute = true }
}
CobblemonSherds.registerSherds()
}
fun getLevel(dimension: RegistryKey<World>): World? {
@@ -115,11 +115,11 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
@JvmField
val APRICORN_FENCE = this.create("apricorn_fence", FenceBlock(AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).instrument(Instrument.BASS).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD)))
@JvmField
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).instrument(Instrument.BASS).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD), APRICORN_WOOD_TYPE))
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(APRICORN_WOOD_TYPE, AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD)))
@JvmField // Note At the time of 1.20.0 we don't need our own BlockSetType for Apricorn wood
val APRICORN_BUTTON = this.create("apricorn_button", BlocksInvoker.createWoodenButtonBlock(BlockSetType.OAK))
@JvmField
val APRICORN_PRESSURE_PLATE = this.create("apricorn_pressure_plate", PressurePlateBlockInvoker.`cobblemon$create`(PressurePlateBlock.ActivationRule.EVERYTHING, AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).instrument(Instrument.BASS).noCollision().strength(0.5f).sounds(BlockSoundGroup.WOOD), APRICORN_BLOCK_SET_TYPE))
val APRICORN_PRESSURE_PLATE = this.create("apricorn_pressure_plate", PressurePlateBlockInvoker.`cobblemon$create`(APRICORN_BLOCK_SET_TYPE, AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).noCollision().strength(0.5f).sounds(BlockSoundGroup.WOOD)))
@JvmField
val APRICORN_SIGN = this.create("apricorn_sign", CobblemonSignBlock(AbstractBlock.Settings.copy(Blocks.OAK_SIGN), APRICORN_WOOD_TYPE))
@JvmField
@@ -133,9 +133,9 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
@JvmField
val APRICORN_STAIRS = this.create("apricorn_stairs", StairsBlockInvoker.`cobblemon$create`(APRICORN_PLANKS.defaultState, AbstractBlock.Settings.copy(APRICORN_PLANKS)))
@JvmField
val APRICORN_DOOR = this.create("apricorn_door", DoorBlockInvoker.`cobblemon$create`(AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).instrument(Instrument.BASS).strength(3.0F).sounds(BlockSoundGroup.WOOD).nonOpaque(), APRICORN_BLOCK_SET_TYPE))
val APRICORN_DOOR = this.create("apricorn_door", DoorBlockInvoker.`cobblemon$create`(APRICORN_BLOCK_SET_TYPE, AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).instrument(Instrument.BASS).strength(3.0F).sounds(BlockSoundGroup.WOOD).nonOpaque()))
@JvmField
val APRICORN_TRAPDOOR = this.create("apricorn_trapdoor", TrapdoorBlockInvoker.`cobblemon$create`(AbstractBlock.Settings.create().mapColor(MapColor.OAK_TAN).instrument(Instrument.BASS).strength(3.0F).sounds(BlockSoundGroup.WOOD).nonOpaque().allowsSpawning { _, _, _, _ -> false }, APRICORN_BLOCK_SET_TYPE))
val APRICORN_TRAPDOOR = this.create("apricorn_trapdoor", TrapdoorBlockInvoker.`cobblemon$create`(APRICORN_BLOCK_SET_TYPE, AbstractBlock.Settings.create().mapColor(MapColor.OAK_TAN).instrument(Instrument.BASS).strength(3.0F).sounds(BlockSoundGroup.WOOD).nonOpaque().allowsSpawning { _, _, _, _ -> false }))
private val PLANT_PROPERTIES = AbstractBlock.Settings.create().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.GRASS).pistonBehavior(PistonBehavior.DESTROY)
@JvmField
@@ -234,7 +234,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
"relic_coin_pouch",
CoinPouchBlock(
AbstractBlock.Settings.create()
.sounds(CobblemonSounds.RELIC_COIN_POUCH_SOUNDS)
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
.pistonBehavior(PistonBehavior.DESTROY)
.strength(0.4f)
.nonOpaque(), true
@@ -245,7 +245,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
"relic_coin_sack",
CoinPouchBlock(
AbstractBlock.Settings.create()
.sounds(CobblemonSounds.RELIC_COIN_SACK_SOUNDS)
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
.pistonBehavior(PistonBehavior.DESTROY)
.strength(0.4f), false
))
@@ -560,7 +560,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
/**
* Calls helper method from Vanilla
*/
private fun log(name: String, arg: MapColor = MapColor.DIRT_BROWN, arg2: MapColor = MapColor.DIRT_BROWN): PillarBlock {
private fun log(name: String, arg: MapColor = MapColor.DIRT_BROWN, arg2: MapColor = MapColor.DIRT_BROWN): Block {
val block = BlocksInvoker.createLogBlock(arg, arg2)
return this.create(name, block)
}
@@ -580,16 +580,16 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
return block
}
private fun evolutionStoneOre(name: String) = this.create(name, ExperienceDroppingBlock(AbstractBlock.Settings.copy(Blocks.IRON_ORE), UniformIntProvider.create(1, 2)))
private fun evolutionStoneOre(name: String) = this.create(name, ExperienceDroppingBlock(UniformIntProvider.create(1, 2), AbstractBlock.Settings.copy(Blocks.IRON_ORE)))
private fun deepslateEvolutionStoneOre(name: String) = this.create(name, ExperienceDroppingBlock(AbstractBlock.Settings.copy(Blocks.DEEPSLATE_IRON_ORE), UniformIntProvider.create(1, 2)))
private fun deepslateEvolutionStoneOre(name: String) = this.create(name, ExperienceDroppingBlock(UniformIntProvider.create(1, 2), AbstractBlock.Settings.copy(Blocks.DEEPSLATE_IRON_ORE)))
/**
* Helper method for creating leaves
* copied over from Vanilla
*/
private fun leaves(name: String): LeavesBlock {
private fun leaves(name: String): Block {
val block = BlocksInvoker.createLeavesBlock(BlockSoundGroup.GRASS)
return this.create(name, block)
}
}
}
@@ -146,7 +146,7 @@ interface CobblemonImplementation {
* @param criteria
* @return
*/
fun <T : Criterion<*>> registerCriteria(criteria: T): T
fun <T : Criterion<*>> registerCriteria(id: String, criteria: T): T
/**
* TODO
@@ -240,4 +240,4 @@ enum class Environment {
CLIENT,
SERVER
}
}
@@ -667,8 +667,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val CHOICE_SPECS = heldItem("choice_specs")
@JvmField
val CLEANSE_TAG = heldItem("cleanse_tag")
// @JvmField
// val CLEAR_AMULET = heldItem("clear_amulet")
@JvmField
val CLEAR_AMULET = heldItem("clear_amulet")
@JvmField
val COVERT_CLOAK = heldItem("covert_cloak")
@JvmField
@@ -697,16 +697,16 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val FOCUS_BAND = heldItem("focus_band")
@JvmField
val FOCUS_SASH = heldItem("focus_sash")
// @JvmField
// val GRIP_CLAW = heldItem("grip_claw")
@JvmField
val GRIP_CLAW = heldItem("grip_claw")
@JvmField
val HARD_STONE = heldItem("hard_stone")
@JvmField
val HEAVY_DUTY_BOOTS = heldItem("heavy_duty_boots")
@JvmField
val IRON_BALL = heldItem("iron_ball")
// @JvmField
// val LAGGING_TAIL = heldItem("lagging_tail")
@JvmField
val LAGGING_TAIL = heldItem("lagging_tail")
@JvmField
val LEFTOVERS = heldItem("leftovers")
@JvmField
@@ -719,12 +719,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val LOADED_DICE = heldItem("loaded_dice")
@JvmField
val LUCKY_EGG = heldItem("lucky_egg")
// @JvmField
// val LUMINOUS_MOSS = heldItem("luminous_moss")
@JvmField
val LUMINOUS_MOSS = heldItem("luminous_moss")
@JvmField
val MAGNET = heldItem("magnet")
// @JvmField
// val METRONOME = heldItem("metronome")
@JvmField
val METRONOME = heldItem("metronome")
@JvmField
val MIRACLE_SEED = heldItem("miracle_seed")
@JvmField
@@ -747,10 +747,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val POWER_LENS = heldItem("power_lens")
@JvmField
val POWER_WEIGHT = heldItem("power_weight")
// @JvmField
// val PROTECTIVE_PADS = heldItem("protective_pads")
// @JvmField
// val PUNCHING_GLOVE = heldItem("punching_glove")
@JvmField
val PROTECTIVE_PADS = heldItem("protective_pads")
@JvmField
val PUNCHING_GLOVE = heldItem("punching_glove")
@JvmField
val QUICK_CLAW = heldItem("quick_claw")
@JvmField
@@ -759,16 +759,16 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val RING_TARGET = heldItem("ring_target")
@JvmField
val ROCKY_HELMET = heldItem("rocky_helmet")
// @JvmField
// val ROOM_SERVICE = heldItem("room_service")
@JvmField
val ROOM_SERVICE = heldItem("room_service")
@JvmField
val SAFETY_GOGGLES = heldItem("safety_goggles")
// @JvmField
// val SCOPE_LENS = heldItem("scope_lens")
@JvmField
val SCOPE_LENS = heldItem("scope_lens")
@JvmField
val SHARP_BEAK = heldItem("sharp_beak")
// @JvmField
// val SHED_SHELL = heldItem("shed_shell")
@JvmField
val SHED_SHELL = heldItem("shed_shell")
@JvmField
val SHELL_BELL = heldItem("shell_bell")
@JvmField
@@ -785,24 +785,24 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val SOOTHE_BELL = heldItem("soothe_bell")
@JvmField
val STICKY_BARB = heldItem("sticky_barb")
// @JvmField
// val TERRAIN_EXTENDER = heldItem("terrain_extender")
// @JvmField
// val THROAT_SPRAY = heldItem("throat_spray")
@JvmField
val TERRAIN_EXTENDER = heldItem("terrain_extender")
@JvmField
val THROAT_SPRAY = heldItem("throat_spray")
@JvmField
val TOXIC_ORB = heldItem("toxic_orb")
@JvmField
val TWISTED_SPOON = heldItem("twisted_spoon")
// @JvmField
// val UTILITY_UMBRELLA = heldItem("utility_umbrella")
@JvmField
val UTILITY_UMBRELLA = heldItem("utility_umbrella")
@JvmField
val WEAKNESS_POLICY = heldItem("weakness_policy")
// @JvmField
// val WIDE_LENS = heldItem("wide_lens")
@JvmField
val WIDE_LENS = heldItem("wide_lens")
@JvmField
val WISE_GLASSES = heldItem("wise_glasses")
// @JvmField
// val ZOOM_LENS = heldItem("zoom_lens")
@JvmField
val ZOOM_LENS = heldItem("zoom_lens")
@JvmField
val MENTAL_HERB = compostableHeldItem("mental_herb", null, 1F)
@JvmField
@@ -122,18 +122,13 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
val FOSSIL_MACHINE_UNPROTECTED = this.create("fossil_machine.unprotected")
@JvmField
val RELIC_COIN_SACK_BREAK = this.create("relic_coin_sack.break")
val COIN_POUCH_BREAK = this.create("coin_pouch.break")
@JvmField
val RELIC_COIN_SACK_HIT = this.create("relic_coin_sack.hit")
val COIN_POUCH_HIT = this.create("coin_pouch.hit")
@JvmField
val RELIC_COIN_SACK_STEP = this.create("relic_coin_sack.step")
val COIN_POUCH_STEP = this.create("coin_pouch.step")
@JvmField
val RELIC_COIN_SACK_PLACE = this.create("relic_coin_sack.place")
@JvmField
val RELIC_COIN_POUCH_BREAK = this.create("relic_coin_pouch.break")
@JvmField
val RELIC_COIN_POUCH_PLACE = this.create("relic_coin_pouch.place")
val COIN_POUCH_PLACE = this.create("coin_pouch.place")
@JvmField
val TUMBLESTONE_BREAK = this.create("tumblestone.break")
@JvmField
@@ -194,22 +189,13 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
val GILDED_CHEST_PLACE = this.create("gilded_chest.place")
@JvmField
val RELIC_COIN_SACK_SOUNDS = BlockSoundGroup(1f, 1f,
RELIC_COIN_SACK_BREAK,
RELIC_COIN_SACK_STEP,
RELIC_COIN_SACK_PLACE,
RELIC_COIN_SACK_HIT,
RELIC_COIN_SACK_STEP
val COIN_POUCH_SOUNDS = BlockSoundGroup(1f, 1f,
COIN_POUCH_BREAK,
COIN_POUCH_STEP,
COIN_POUCH_PLACE,
COIN_POUCH_HIT,
COIN_POUCH_STEP
)
@JvmField
val RELIC_COIN_POUCH_SOUNDS = BlockSoundGroup(1f, 1f,
RELIC_COIN_POUCH_BREAK,
RELIC_COIN_SACK_STEP,
RELIC_COIN_POUCH_PLACE,
RELIC_COIN_SACK_HIT,
RELIC_COIN_SACK_STEP
)
@JvmField
val IMPACT_NORMAL = this.create("impact.normal")
@JvmField
@@ -9,8 +9,18 @@
package com.cobblemon.mod.common.advancement
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.advancement.criterion.*
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.advancement.criterion.AspectCriterion
import com.cobblemon.mod.common.advancement.criterion.BattleCountableCriterion
import com.cobblemon.mod.common.advancement.criterion.CaughtPokemonCriterion
import com.cobblemon.mod.common.advancement.criterion.CountableCriterion
import com.cobblemon.mod.common.advancement.criterion.EvolvePokemonCriterion
import com.cobblemon.mod.common.advancement.criterion.LevelUpCriterion
import com.cobblemon.mod.common.advancement.criterion.PartyCheckCriterion
import com.cobblemon.mod.common.advancement.criterion.PlantTumblestoneCriterion
import com.cobblemon.mod.common.advancement.criterion.PokemonCriterion
import com.cobblemon.mod.common.advancement.criterion.PokemonInteractCriterion
import com.cobblemon.mod.common.advancement.criterion.SimpleCriterionTrigger
import com.cobblemon.mod.common.advancement.criterion.TradePokemonCriterion
import net.minecraft.advancement.criterion.Criterion
/**
@@ -20,107 +30,37 @@ import net.minecraft.advancement.criterion.Criterion
* @since October 26th, 2022
*/
object CobblemonCriteria {
val PICK_STARTER = this.create(
SimpleCriterionTrigger(
cobblemonResource("pick_starter"),
PickStarterCriterionCondition::class.java
)
)
val CATCH_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("catch_pokemon"),
CaughtPokemonCriterionCondition::class.java
)
)
val CATCH_SHINY_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("catch_shiny_pokemon"),
SimpleCountableCriterionCondition::class.java
)
)
val EGG_HATCH = this.create(
SimpleCriterionTrigger(
cobblemonResource("eggs_hatched"),
SimpleCountableCriterionCondition::class.java
)
)
val EVOLVE_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("pokemon_evolved"),
EvolvePokemonCriterionCondition::class.java
)
)
val WIN_BATTLE = this.create(
BattleCountableCriterionTrigger(
cobblemonResource("battles_won"),
BattleCountableCriterionCondition::class.java
)
)
val PICK_STARTER = this.create("pick_starter", SimpleCriterionTrigger(PokemonCriterion.CODEC))
val DEFEAT_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("pokemon_defeated"),
SimpleCountableCriterionCondition::class.java
)
)
val CATCH_POKEMON = this.create("catch_pokemon", SimpleCriterionTrigger(CaughtPokemonCriterion.CODEC))
val COLLECT_ASPECT = this.create(
AspectCriterionTrigger(
cobblemonResource("aspects_collected"),
AspectCriterionCondition::class.java
)
)
val CATCH_SHINY_POKEMON = this.create("catch_shiny_pokemon", SimpleCriterionTrigger(CountableCriterion.CODEC))
val POKEMON_INTERACT = this.create(
SimpleCriterionTrigger(
cobblemonResource("pokemon_interact"),
PokemonInteractCriterion::class.java
)
)
val EGG_HATCH = this.create("eggs_hatched", SimpleCriterionTrigger(CountableCriterion.CODEC))
val PARTY_CHECK = this.create(
SimpleCriterionTrigger(
cobblemonResource("party"),
PartyCheckCriterion::class.java
)
)
val EVOLVE_POKEMON = this.create("pokemon_evolved", SimpleCriterionTrigger(EvolvePokemonCriterion.CODEC))
val LEVEL_UP = this.create(
SimpleCriterionTrigger(
cobblemonResource("level_up"),
LevelUpCriterionCondition::class.java
)
)
val WIN_BATTLE = this.create("battles_won", SimpleCriterionTrigger(BattleCountableCriterion.CODEC))
val PASTURE_USE = this.create(
SimpleCriterionTrigger(
cobblemonResource("pasture_use"),
PickStarterCriterionCondition::class.java
)
)
val DEFEAT_POKEMON = this.create("pokemon_defeated", SimpleCriterionTrigger(CountableCriterion.CODEC))
val RESURRECT_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("resurrect_pokemon"),
PickStarterCriterionCondition::class.java
)
)
val COLLECT_ASPECT = this.create("aspects_collected", SimpleCriterionTrigger(AspectCriterion.CODEC))
val TRADE_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("trade_pokemon"),
TradePokemonCriterionCondition::class.java
)
)
val POKEMON_INTERACT = this.create("pokemon_interact", SimpleCriterionTrigger(PokemonInteractCriterion.CODEC))
val PARTY_CHECK = this.create("party", SimpleCriterionTrigger(PartyCheckCriterion.CODEC))
val LEVEL_UP = this.create("level_up", SimpleCriterionTrigger(LevelUpCriterion.CODEC))
val PASTURE_USE = this.create("pasture_use", SimpleCriterionTrigger(PokemonCriterion.CODEC))
val RESURRECT_POKEMON = this.create("resurrect_pokemon", SimpleCriterionTrigger(PokemonCriterion.CODEC))
val TRADE_POKEMON = this.create("trade_pokemon", SimpleCriterionTrigger(TradePokemonCriterion.CODEC))
// Advancement criteria for [grow_tumblestone.json]
val PLANT_TUMBLESTONE = this.create(
SimpleCriterionTrigger(
cobblemonResource("plant_tumblestone"),
PlantTumblestoneCriterionCondition::class.java
)
)
val PLANT_TUMBLESTONE = this.create("plant_tumblestone", SimpleCriterionTrigger(PlantTumblestoneCriterion.CODEC))
private fun <T : Criterion<*>> create(criteria: T): T = Cobblemon.implementation.registerCriteria(criteria)
private fun <T : Criterion<*>> create(id: String, criteria: T): T = Cobblemon.implementation.registerCriteria(id, criteria)
}
@@ -8,38 +8,59 @@
package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
open class AspectCriterionTrigger(identifier: Identifier, criterionClass: Class<AspectCriterionCondition>) : SimpleCriterionTrigger<MutableMap<Identifier, MutableSet<String>>, AspectCriterionCondition>(identifier, criterionClass) {
class AspectCriterion(
playerCtx: Optional<LootContextPredicate>,
val pokemon: Identifier,
val aspects: List<String>
): SimpleCriterionCondition<MutableMap<Identifier, MutableSet<String>>>(playerCtx) {
}
class AspectCriterionCondition(id: Identifier, predicate: LootContextPredicate) : SimpleCriterionCondition<MutableMap<Identifier, MutableSet<String>>>(id, predicate) {
var pokemon = Identifier("cobblemon:pikachu")
var aspects = mutableListOf<String>()
override fun toJson(json: JsonObject) {
json.add("aspects", JsonArray(aspects.size).also {
aspects.forEach { aspect -> it.add(aspect) }
})
json.addProperty("pokemon", pokemon.toString())
}
override fun fromJson(json: JsonObject) {
aspects.clear()
json.getAsJsonArray("aspects").forEach { element ->
aspects.add(element.asString)
}
pokemon = json.get("pokemon").asString.asIdentifierDefaultingNamespace()
companion object {
val CODEC: Codec<AspectCriterion> = RecordCodecBuilder.create { it.group(
Codecs.createStrictOptionalFieldCodec(LootContextPredicate.CODEC, "player").forGetter(AspectCriterion::playerCtx),
Codecs.createStrictOptionalFieldCodec(Identifier.CODEC, "pokemon", Identifier("cobblemon:pikachu")).forGetter(AspectCriterion::pokemon),
Codecs.createStrictOptionalFieldCodec(Codec.STRING.listOf(), "aspects", listOf()).forGetter(AspectCriterion::aspects)
) .apply(it, ::AspectCriterion) }
}
override fun matches(player: ServerPlayerEntity, context: MutableMap<Identifier, MutableSet<String>>): Boolean {
val caughtAspects = context.getOrDefault(pokemon, mutableSetOf())
return this.aspects.all { it in caughtAspects }
}
}
}
//open class AspectCriterionTrigger(identifier: Identifier, criterionClass: Class<AspectCriterionCondition>) : SimpleCriterionTrigger<MutableMap<Identifier, MutableSet<String>>, AspectCriterionCondition>(identifier, criterionClass) {
//
//}
//
//class AspectCriterionCondition(id: Identifier, predicate: LootContextPredicate) : SimpleCriterionCondition<MutableMap<Identifier, MutableSet<String>>>(id, predicate) {
// var pokemon = Identifier("cobblemon:pikachu")
// var aspects = mutableListOf<String>()
// override fun toJson(json: JsonObject) {
// json.add("aspects", JsonArray(aspects.size).also {
// aspects.forEach { aspect -> it.add(aspect) }
// })
// json.addProperty("pokemon", pokemon.toString())
// }
//
// override fun fromJson(json: JsonObject) {
// aspects.clear()
// json.getAsJsonArray("aspects").forEach { element ->
// aspects.add(element.asString)
// }
// pokemon = json.get("pokemon").asString.asIdentifierDefaultingNamespace()
// }
//
// override fun matches(player: ServerPlayerEntity, context: MutableMap<Identifier, MutableSet<String>>): Boolean {
// val caughtAspects = context.getOrDefault(pokemon, mutableSetOf())
// return this.aspects.all { it in caughtAspects }
// }
//
//}
@@ -10,58 +10,105 @@ 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 com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
open class BattleCountableCriterionTrigger(identifier: Identifier, criterionClass: Class<BattleCountableCriterionCondition>) : SimpleCriterionTrigger<BattleCountableContext, BattleCountableCriterionCondition>(identifier, criterionClass) {
open class BattleCountableContext(var battle : PokemonBattle, times : Int) : CountableContext(times)
}
class BattleCountableCriterion(
playerCtx: Optional<LootContextPredicate>,
val battleTypes: List<String>,
count: Int
): CountableCriterion<BattleCountableContext>(playerCtx, count) {
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())
companion object {
val CODEC: Codec<BattleCountableCriterion> = RecordCodecBuilder.create { it.group(
Codecs.createStrictOptionalFieldCodec(LootContextPredicate.CODEC, "player").forGetter(BattleCountableCriterion::playerCtx),
Codecs.createStrictOptionalFieldCodec(Codec.STRING.listOf(), "battle_types", listOf("any")).forGetter(BattleCountableCriterion::battleTypes),
Codecs.createStrictOptionalFieldCodec(Codec.INT, "count", 0).forGetter(BattleCountableCriterion::count)
).apply(it, ::BattleCountableCriterion) }
}
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
return typeCheck && super.matches(player, context)
}
}
open class BattleCountableContext(times : Int, var battle : PokemonBattle) : CountableContext(times)
//open class BattleCountableCriterionTrigger(identifier: Identifier, criterionClass: Class<BattleCountableCriterion>) : SimpleCriterionTrigger<BattleCountableContext, BattleCountableCriterion>(identifier, criterionClass) {
//
//}
//
//class BattleCountableCriterion(id: Identifier, predicate: LootContextPredicate) : CountableCriterion<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
// }
//}
@@ -8,10 +8,13 @@
package com.cobblemon.mod.common.advancement.criterion
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.EntityPredicate
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
/**
* A context that is used when you require a [CountableContext] along with some type string.
@@ -21,19 +24,38 @@ import net.minecraft.util.Identifier
*/
open class CountablePokemonTypeContext(times: Int, var type: String) : CountableContext(times)
class CaughtPokemonCriterionCondition(id: Identifier, predicate: LootContextPredicate) : CountableCriterionCondition<CountablePokemonTypeContext>(id, predicate) {
var type = "any"
override fun toJson(json: JsonObject) {
super.toJson(json)
json.addProperty("type", type)
}
class CaughtPokemonCriterion(
playerCtx: Optional<LootContextPredicate>,
val type: String,
count: Int
): CountableCriterion<CountablePokemonTypeContext>(playerCtx, count) {
override fun fromJson(json: JsonObject) {
super.fromJson(json)
type = json.get("type")?.asString ?: "any"
companion object {
val CODEC: Codec<CaughtPokemonCriterion> = RecordCodecBuilder.create { it.group(
Codecs.createStrictOptionalFieldCodec(EntityPredicate.LOOT_CONTEXT_PREDICATE_CODEC, "player").forGetter(CaughtPokemonCriterion::playerCtx),
Codecs.createStrictOptionalFieldCodec(Codec.STRING, "type", "any").forGetter(CaughtPokemonCriterion::type),
Codecs.createStrictOptionalFieldCodec(Codec.INT, "count", 0).forGetter(CaughtPokemonCriterion::count)
).apply(it, ::CaughtPokemonCriterion) }
}
override fun matches(player: ServerPlayerEntity, context: CountablePokemonTypeContext): Boolean {
return super.matches(player, context) && (context.type == type || type == "any")
}
}
}
//class CaughtPokemonCriterionCondition(id: Identifier, predicate: LootContextPredicate) : CountableCriterionCondition<CountablePokemonTypeContext>(id, predicate) {
// var type = "any"
// override fun toJson(json: JsonObject) {
// super.toJson(json)
// json.addProperty("type", type)
// }
//
// override fun fromJson(json: JsonObject) {
// super.fromJson(json)
// type = json.get("type")?.asString ?: "any"
// }
//
// override fun matches(player: ServerPlayerEntity, context: CountablePokemonTypeContext): Boolean {
// return super.matches(player, context) && (context.type == type || type == "any")
// }
//}
@@ -8,48 +8,69 @@
package com.cobblemon.mod.common.advancement.criterion
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.EntityPredicate
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
/**
* A type of advancement criterion condition that requires some number of completions. This can be extended to add
* more conditions than just the count.
*
* @author Hiroku
* @since November 4th, 2022
*/
abstract class CountableCriterionCondition<T : CountableContext>(id: Identifier, predicate: LootContextPredicate) : SimpleCriterionCondition<T>(id, predicate) {
var count = 0
override fun fromJson(json: JsonObject) {
count = json.get("count")?.asInt ?: 0
}
open class CountableContext(var times: Int)
override fun toJson(json: JsonObject) {
json.addProperty("count", count)
open class CountableCriterion<T : CountableContext>(
playerCtx: Optional<LootContextPredicate>,
val count: Int
): SimpleCriterionCondition<T>(playerCtx) {
companion object {
val CODEC: Codec<CountableCriterion<CountableContext>> = RecordCodecBuilder.create { it.group(
Codecs.createStrictOptionalFieldCodec(EntityPredicate.LOOT_CONTEXT_PREDICATE_CODEC, "player").forGetter(CountableCriterion<CountableContext>::playerCtx),
Codecs.createStrictOptionalFieldCodec(Codec.INT, "count", 0).forGetter(CountableCriterion<CountableContext>::count)
).apply(it, ::CountableCriterion) }
}
override fun matches(player: ServerPlayerEntity, context: T) = context.times >= count
}
/**
* A concrete subclass of [CountableCriterionCondition] so that you can have advancements that really just need the
* count and nothing else.
*
* This is just a quirk of using generics like this, don't worry about it. The criterion conditions that get used must
* not be generic typed.
*
* @author Hiroku
* @since November 4th, 2022
*/
class SimpleCountableCriterionCondition(id: Identifier, predicate: LootContextPredicate) : CountableCriterionCondition<CountableContext>(id, predicate)
fun SimpleCriterionTrigger<CountableContext, SimpleCountableCriterionCondition>.trigger(player: ServerPlayerEntity, times: Int) = trigger(player, CountableContext(times))
/**
* Some type of context that has a count associated with it, representing how many times the trigger has occurred.
*
* @author Hiroku
* @since November 4th, 2022
*/
open class CountableContext(var times: Int)
///**
// * A type of advancement criterion condition that requires some number of completions. This can be extended to add
// * more conditions than just the count.
// *
// * @author Hiroku
// * @since November 4th, 2022
// */
//abstract class CountableCriterionCondition<T : CountableContext>(id: Identifier, predicate: LootContextPredicate) : SimpleCriterionCondition<T>(id, predicate) {
// var count = 0
// override fun fromJson(json: JsonObject) {
// count = json.get("count")?.asInt ?: 0
// }
//
// override fun toJson(json: JsonObject) {
// json.addProperty("count", count)
// }
//
// override fun matches(player: ServerPlayerEntity, context: T) = context.times >= count
//}
//
///**
// * A concrete subclass of [CountableCriterionCondition] so that you can have advancements that really just need the
// * count and nothing else.
// *
// * This is just a quirk of using generics like this, don't worry about it. The criterion conditions that get used must
// * not be generic typed.
// *
// * @author Hiroku
// * @since November 4th, 2022
// */
//class SimpleCountableCriterionCondition(id: Identifier, predicate: LootContextPredicate) : CountableCriterionCondition<CountableContext>(id, predicate)
//fun SimpleCriterionTrigger<CountableContext, SimpleCountableCriterionCondition>.trigger(player: ServerPlayerEntity, times: Int) = trigger(player, CountableContext(times))
//
///**
// * Some type of context that has a count associated with it, representing how many times the trigger has occurred.
// *
// * @author Hiroku
// * @since November 4th, 2022
// */
//open class CountableContext(var times: Int)
@@ -0,0 +1,65 @@
/*
* 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.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
class EvolvePokemonContext(val species : Identifier, val evolution : Identifier, times: Int) : CountableContext(times)
class EvolvePokemonCriterion(
playerCtx: Optional<LootContextPredicate>,
val species: String,
val evolution: String,
count: Int
): CountableCriterion<EvolvePokemonContext>(playerCtx, count) {
companion object {
val CODEC: Codec<EvolvePokemonCriterion> = RecordCodecBuilder.create { it.group(
Codecs.createStrictOptionalFieldCodec(LootContextPredicate.CODEC, "player").forGetter(EvolvePokemonCriterion::playerCtx),
Codecs.createStrictOptionalFieldCodec(Codec.STRING, "species", "any").forGetter(EvolvePokemonCriterion::species),
Codecs.createStrictOptionalFieldCodec(Codec.STRING, "evolution", "any").forGetter(EvolvePokemonCriterion::evolution),
Codecs.createStrictOptionalFieldCodec(Codec.INT, "count", 0).forGetter(EvolvePokemonCriterion::count)
).apply(it, ::EvolvePokemonCriterion) }
}
override fun matches(player: ServerPlayerEntity, context: EvolvePokemonContext): Boolean {
return context.times >= count && (context.species == species.asIdentifierDefaultingNamespace() || species == "any") &&
(context.evolution == evolution.asIdentifierDefaultingNamespace() || evolution == "any")
}
}
//class EvolvePokemonCriterion(id: Identifier, entity: LootContextPredicate) : CountableCriterion<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,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)
@@ -9,12 +9,43 @@
package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.pokemon.Pokemon
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.EntityPredicate
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
class LevelUpCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<LevelUpContext>(id, entity) {
class LevelUpContext(val level: Int, val pokemon: Pokemon)
class LevelUpCriterion(
playerCtx: Optional<LootContextPredicate>,
val level: Int,
val evolved: Boolean
): SimpleCriterionCondition<LevelUpContext>(playerCtx) {
companion object {
val CODEC: Codec<LevelUpCriterion> = RecordCodecBuilder.create { it.group(
Codecs.createStrictOptionalFieldCodec(EntityPredicate.LOOT_CONTEXT_PREDICATE_CODEC, "player").forGetter(LevelUpCriterion::playerCtx),
Codecs.createStrictOptionalFieldCodec(Codec.INT, "level", 0).forGetter(LevelUpCriterion::level),
Codecs.createStrictOptionalFieldCodec(Codec.BOOL, "evolved", true).forGetter(LevelUpCriterion::evolved)
).apply(it, ::LevelUpCriterion) }
}
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
}
}
/*class LevelUpCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<LevelUpContext>(id, entity) {
var level = 0
var evolved = true
override fun toJson(json: JsonObject) {
@@ -36,6 +67,4 @@ class LevelUpCriterionCondition(id: Identifier, entity: LootContextPredicate) :
}
return level == context.level && evolutionCheck == evolved
}
}
open class LevelUpContext(var level : Int, var pokemon : Pokemon)
}*/
@@ -10,14 +10,62 @@ 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 com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.EntityPredicate
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
class PartyCheckCriterion(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<PartyCheckContext>(id, entity){
class PartyCheckCriterion(
playerCtx: Optional<LootContextPredicate>,
val party: List<Identifier>,
): SimpleCriterionCondition<PlayerPartyStore>(playerCtx) {
companion object {
val CODEC: Codec<PartyCheckCriterion> = RecordCodecBuilder.create { it.group(
Codecs.createStrictOptionalFieldCodec(EntityPredicate.LOOT_CONTEXT_PREDICATE_CODEC, "player").forGetter(PartyCheckCriterion::playerCtx),
Codecs.createStrictOptionalFieldCodec(Identifier.CODEC.listOf(), "id", listOf()).forGetter(PartyCheckCriterion::party)
).apply(it, ::PartyCheckCriterion) }
}
override fun matches(player: ServerPlayerEntity, context: PlayerPartyStore): Boolean {
val matches = mutableListOf<Identifier>()
party.forEach {
if (it == "any".asIdentifierDefaultingNamespace()) {
matches.add(it)
}
}
val partyCount = context.count()
if (matches.containsAll(party) && party.size == partyCount && matches.size == partyCount) return true
context.iterator().forEach {
if (party.contains(it.species.resourceIdentifier)) {
matches.add(it.species.resourceIdentifier)
}
}
return matches.containsAll(party) && matches.size == partyCount
}
}
/*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 {
@@ -51,4 +99,4 @@ class PartyCheckCriterion(id: Identifier, entity: LootContextPredicate) : Simple
}
}
open class PartyCheckContext(val party : PlayerPartyStore)
open class PartyCheckContext(val party : PlayerPartyStore)*/
@@ -10,26 +10,27 @@ package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.pokemon.Pokemon
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.EntityPredicate
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
/**
* A criteria that is triggered when a player picks a starter.
*
* @author Licious, Hiroku
* @since October 26th, 2022
*/
class PickStarterCriterionCondition(id: Identifier, predicate: LootContextPredicate) : SimpleCriterionCondition<Pokemon>(id, predicate) {
var properties = PokemonProperties()
override fun toJson(json: JsonObject) {
json.addProperty("properties", properties.originalString)
class PokemonCriterion(
playerCtx: Optional<LootContextPredicate>,
val properties: PokemonProperties
): SimpleCriterionCondition<Pokemon>(playerCtx) {
companion object {
val CODEC: Codec<PokemonCriterion> = RecordCodecBuilder.create { it.group(
Codecs.createStrictOptionalFieldCodec(EntityPredicate.LOOT_CONTEXT_PREDICATE_CODEC, "player").forGetter(PokemonCriterion::playerCtx),
Codecs.createStrictOptionalFieldCodec(PokemonProperties.CODEC, "properties", PokemonProperties()).forGetter(PokemonCriterion::properties)
).apply(it, ::PokemonCriterion) }
}
override fun fromJson(json: JsonObject) {
properties = PokemonProperties.parse(json.get("properties")?.asString ?: "")
override fun matches(player: ServerPlayerEntity, context: Pokemon): Boolean {
return properties.matches(context)
}
override fun matches(player: ServerPlayerEntity, context: Pokemon) = properties.matches(context)
}
@@ -9,44 +9,63 @@
package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.block.TumblestoneBlock
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.dynamic.Codecs
import net.minecraft.util.math.BlockPos
import java.util.Optional
/**
* A criterion condition for when a tumblestone block is planted
* @param id The identifier of the criterion
* @param predicate The predicate for the criterion
*
* @author Aethen
* @since 02/28/2024
*/
class PlantTumblestoneCriterionCondition(id: Identifier, predicate: LootContextPredicate) :
SimpleCriterionCondition<PlantTumblestoneContext>(id, predicate) {
override fun toJson(json: JsonObject) {
// Add properties to json if needed. None needed for this criterion
class PlantTumblestoneContext(val pos: BlockPos, val block: TumblestoneBlock)
class PlantTumblestoneCriterion(
playerCtx: Optional<LootContextPredicate>
): SimpleCriterionCondition<PlantTumblestoneContext>(playerCtx) {
companion object {
val CODEC: Codec<PlantTumblestoneCriterion> = RecordCodecBuilder.create { it.group(
Codecs.createStrictOptionalFieldCodec(LootContextPredicate.CODEC, "player").forGetter(PlantTumblestoneCriterion::playerCtx)
).apply(it, ::PlantTumblestoneCriterion) }
}
override fun fromJson(json: JsonObject) {
// Parse properties from json if needed. None needed for this criterion
}
/**
* Checks if the tumblestone block can grow at the given position
* @param player The player that planted the tumblestone block
* @param context The context of the criterion
* @return True if the tumblestone block can grow at the given position, false otherwise
*/
override fun matches(player: ServerPlayerEntity, context: PlantTumblestoneContext): Boolean {
return context.tumbleStoneBlock.canGrow(context.pos, player.world)
return context.block.canGrow(context.pos, player.world)
}
}
/**
* The context of the [PlantTumblestoneCriterionCondition]
* @param pos The position of the tumblestone block
* @param tumbleStoneBlock The tumblestone block
*/
open class PlantTumblestoneContext(var pos: BlockPos, var tumbleStoneBlock: TumblestoneBlock)
///**
// * A criterion condition for when a tumblestone block is planted
// * @param id The identifier of the criterion
// * @param predicate The predicate for the criterion
// *
// * @author Aethen
// * @since 02/28/2024
// */
//class PlantTumblestoneCriterionCondition(id: Identifier, predicate: LootContextPredicate) :
// SimpleCriterionCondition<PlantTumblestoneContext>(id, predicate) {
// override fun toJson(json: JsonObject) {
// // Add properties to json if needed. None needed for this criterion
// }
//
// override fun fromJson(json: JsonObject) {
// // Parse properties from json if needed. None needed for this criterion
// }
//
// /**
// * Checks if the tumblestone block can grow at the given position
// * @param player The player that planted the tumblestone block
// * @param context The context of the criterion
// * @return True if the tumblestone block can grow at the given position, false otherwise
// */
// override fun matches(player: ServerPlayerEntity, context: PlantTumblestoneContext): Boolean {
// return context.tumbleStoneBlock.canGrow(context.pos, player.world)
// }
//}
//
///**
// * The context of the [PlantTumblestoneCriterionCondition]
// * @param pos The position of the tumblestone block
// * @param tumbleStoneBlock The tumblestone block
// */
//open class PlantTumblestoneContext(var pos: BlockPos, var tumbleStoneBlock: TumblestoneBlock)
@@ -9,26 +9,33 @@
package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.EntityPredicate
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
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)
}
class PokemonInteractContext(val type: Identifier, val item: Identifier)
override fun fromJson(json: JsonObject) {
type = json.get("type")?.asString ?: "any"
item = json.get("item")?.asString ?: "any"
class PokemonInteractCriterion(
playerCtx: Optional<LootContextPredicate>,
val type: Optional<String>,
val item: Optional<String>
): SimpleCriterionCondition<PokemonInteractContext>(playerCtx) {
companion object {
val CODEC: Codec<PokemonInteractCriterion> = RecordCodecBuilder.create { it.group(
Codecs.createStrictOptionalFieldCodec(EntityPredicate.LOOT_CONTEXT_PREDICATE_CODEC, "player").forGetter(PokemonInteractCriterion::playerCtx),
Codecs.createStrictOptionalFieldCodec(Codec.STRING, "type").forGetter(PokemonInteractCriterion::type),
Codecs.createStrictOptionalFieldCodec(Codec.STRING, "item").forGetter(PokemonInteractCriterion::item)
).apply(it, ::PokemonInteractCriterion) }
}
override fun matches(player: ServerPlayerEntity, context: PokemonInteractContext): Boolean {
return (context.type == type.asIdentifierDefaultingNamespace() || type == "any") && (context.item == item.asIdentifierDefaultingNamespace() || item == "any")
val otherType = this.type.orElse("any")
val otherItem = this.item.orElse("any")
return (context.type == otherType.asIdentifierDefaultingNamespace() || otherType == "any") && (context.type == otherItem.asIdentifierDefaultingNamespace() || otherItem == "any")
}
}
@@ -8,61 +8,28 @@
package com.cobblemon.mod.common.advancement.criterion
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
import net.minecraft.advancement.criterion.AbstractCriterion
import net.minecraft.advancement.criterion.AbstractCriterionConditions
import net.minecraft.predicate.entity.AdvancementEntityPredicateDeserializer
import net.minecraft.predicate.entity.AdvancementEntityPredicateSerializer
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import java.util.Optional
/**
* A wrapping around the AbstractCriterion system of Minecraft. This allows the same base class to be used
* for all advancements by wrapping the advancement-specific contextual data in the generic T, the instance of
* the advancement conditions in the generic C, and allowing the condition to do all serialization, deserialization,
* and matching of context against conditions.
*
* @author Hiroku
* @since November 4th, 2022
*/
open class SimpleCriterionTrigger<T, C : SimpleCriterionCondition<T>>(
val _id: Identifier,
val criterionClass: Class<C>
class SimpleCriterionTrigger<T, C : SimpleCriterionCondition<T>>(
val codec: Codec<C>,
) : AbstractCriterion<C>() {
override fun getId() = _id
override fun conditionsFromJson(
obj: JsonObject,
playerPredicate: LootContextPredicate,
predicateDeserializer: AdvancementEntityPredicateDeserializer
): C {
val instance = criterionClass.getConstructor(Identifier::class.java, LootContextPredicate::class.java).newInstance(id, playerPredicate)
instance.fromJson(obj)
return instance
}
override fun getConditionsCodec() = codec
fun trigger(player: ServerPlayerEntity, context: T) = this.trigger(player) { it.matches(player, context) }
fun trigger(player: ServerPlayerEntity, context: T) {
return this.trigger(player) {
it.matches(player, context)
}
}
}
/**
* The base of the conditions for Cobblemon's wrapping around Minecraft advancements. This is parameterized by whatever
* contextual information this condition needs to be checked, and must handle serialization and deserialization from JSON.
*
* @author Hiroku
* @since November 4th, 2022
*/
abstract class SimpleCriterionCondition<T>(
id: Identifier,
playerPredicate: LootContextPredicate
): AbstractCriterionConditions(id, playerPredicate) {
override fun toJson(predicateSerializer: AdvancementEntityPredicateSerializer): JsonObject {
val json = super.toJson(predicateSerializer)
toJson(json)
return json
}
abstract fun toJson(json: JsonObject)
abstract fun fromJson(json: JsonObject)
val playerCtx: Optional<LootContextPredicate>
) : AbstractCriterion.Conditions {
override fun player() = playerCtx
abstract fun matches(player: ServerPlayerEntity, context: T): Boolean
}
@@ -10,12 +10,59 @@ 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 com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.EntityPredicate
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
class TradePokemonCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<TradePokemonContext>(id, entity) {
class TradePokemonContext(val traded: Pokemon, val received: Pokemon)
class TradePokemonCriterion(
playerCtx: Optional<LootContextPredicate>,
val traded: String,
val received: String,
val tradedHeldItem: String,
val receivedHeldItem: String
): SimpleCriterionCondition<TradePokemonContext>(playerCtx) {
companion object {
val CODEC: Codec<TradePokemonCriterion> = RecordCodecBuilder.create { it.group(
Codecs.createStrictOptionalFieldCodec(EntityPredicate.LOOT_CONTEXT_PREDICATE_CODEC, "player").forGetter(TradePokemonCriterion::playerCtx),
Codecs.createStrictOptionalFieldCodec(Codec.STRING, "traded", "any").forGetter(TradePokemonCriterion::traded),
Codecs.createStrictOptionalFieldCodec(Codec.STRING, "received", "any").forGetter(TradePokemonCriterion::received),
Codecs.createStrictOptionalFieldCodec(Codec.STRING, "traded_held_item", "minecraft:air").forGetter(TradePokemonCriterion::tradedHeldItem),
Codecs.createStrictOptionalFieldCodec(Codec.STRING, "received_held_item", "minecraft:air").forGetter(TradePokemonCriterion::receivedHeldItem)
).apply(it, ::TradePokemonCriterion) }
}
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
if (traded != "any" && context.traded.species.resourceIdentifier != traded.asIdentifierDefaultingNamespace()) {
return false
}
if (received != "any" && context.received.species.resourceIdentifier != received.asIdentifierDefaultingNamespace()) {
return false
}
if (heldItem1 != tradedHeldItem.asIdentifierDefaultingNamespace() && heldItem1 != "minecraft:air".asIdentifierDefaultingNamespace()) {
return false
}
if (heldItem2 != receivedHeldItem.asIdentifierDefaultingNamespace() && heldItem2 != "minecraft:air".asIdentifierDefaultingNamespace()) {
return false
}
return true
}
}
/*class TradePokemonCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<TradePokemonContext>(id, entity) {
var traded = "any"
var received = "any"
var tradedHeldItem = "any"
@@ -44,4 +91,4 @@ class TradePokemonCriterionCondition(id: Identifier, entity: LootContextPredicat
}
}
open class TradePokemonContext(val traded: Pokemon, val received: Pokemon)
open class TradePokemonContext(val traded: Pokemon, val received: Pokemon)*/
@@ -13,8 +13,13 @@ import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.block.ApricornBlock
import com.cobblemon.mod.common.block.ApricornSaplingBlock
import com.cobblemon.mod.common.item.ApricornItem
import com.cobblemon.mod.common.world.feature.CobblemonConfiguredFeatures
import com.mojang.serialization.Codec
import net.minecraft.block.MapColor
import net.minecraft.item.BlockItem
import net.minecraft.registry.RegistryKey
import net.minecraft.util.StringIdentifiable
import net.minecraft.world.gen.feature.ConfiguredFeature
/**
* Contains the different Apricorn variants and util methods to get their item, block, seedling and sapling form.
@@ -22,7 +27,7 @@ import net.minecraft.item.BlockItem
* @author Licious
* @since October 29th, 2022
*/
enum class Apricorn {
enum class Apricorn : StringIdentifiable {
BLACK,
BLUE,
@@ -82,4 +87,20 @@ enum class Apricorn {
YELLOW -> MapColor.YELLOW
}
fun configuredFeature(): RegistryKey<ConfiguredFeature<*, *>> = when(this) {
BLACK -> CobblemonConfiguredFeatures.BLACK_APRICORN_TREE_KEY
BLUE -> CobblemonConfiguredFeatures.BLUE_APRICORN_TREE_KEY
GREEN -> CobblemonConfiguredFeatures.GREEN_APRICORN_TREE_KEY
PINK -> CobblemonConfiguredFeatures.PINK_APRICORN_TREE_KEY
RED -> CobblemonConfiguredFeatures.RED_APRICORN_TREE_KEY
WHITE -> CobblemonConfiguredFeatures.WHITE_APRICORN_TREE_KEY
YELLOW -> CobblemonConfiguredFeatures.YELLOW_APRICORN_TREE_KEY
}
override fun asString() = this.name.lowercase()
companion object {
val CODEC: Codec<Apricorn> = StringIdentifiable.createBasicCodec(::values)
}
}
@@ -226,7 +226,7 @@ open class PokemonBattle(
val facedFainted = opponentPokemon.facedOpponents.contains(faintedPokemon)
val pokemon = opponentPokemon.effectedPokemon
if (facedFainted) {
pokemon.lockedEvolutions.forEach { evolution ->
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() }
@@ -497,4 +497,4 @@ open class PokemonBattle(
return queryStruct
}
}
}
@@ -48,7 +48,7 @@ object Berries : JsonDataRegistry<Berry> {
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(MulchVariant::class.java, MulchVariantAdapter)
.registerTypeAdapter(NumberRange.FloatRange::class.java, FloatNumberRangeAdapter)
.registerTypeAdapter(NumberRange.DoubleRange::class.java, FloatNumberRangeAdapter)
.registerTypeAdapter(Status::class.java, StatusAdapter)
.registerTypeAdapter(TypeToken.getParameterized(Collection::class.java, Box::class.java).type, BoxCollectionAdapter)
.registerTypeAdapter(Box::class.java, BoxAdapter)
@@ -103,4 +103,4 @@ object Berries : JsonDataRegistry<Berry> {
*/
fun getByName(name: String): Berry? = this.getByIdentifier(cobblemonResource(name))
}
}
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.fossil
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.pokemon.evolution.predicate.NbtItemPredicate
import net.minecraft.item.ItemStack
import net.minecraft.registry.Registries
import net.minecraft.text.MutableText
import net.minecraft.text.Text
import net.minecraft.util.Identifier
@@ -48,7 +47,7 @@ class Fossil(
}
return this.fossils.all { fossil ->
ingredients.any { fossil.item.fits(it.item, Registries.ITEM) && fossil.nbt.test(it) }
ingredients.any { fossil.test(it) }
}
}
@@ -64,7 +63,7 @@ class Fossil(
return ingredients.all { ingredient ->
ingredients.count { item -> ingredient.itemMatches(item.registryEntry) } <=
this.fossils.count { fossil -> fossil.item.fits(ingredient.item, Registries.ITEM) && fossil.nbt.test(ingredient) }
this.fossils.count { fossil -> fossil.test(ingredient) }
}
}
@@ -74,7 +73,7 @@ class Fossil(
* @return True if the [ItemStack] is an ingredient, false otherwise.
*/
fun isIngredient(itemStack: ItemStack): Boolean {
return this.fossils.any { it.item.fits(itemStack.item, Registries.ITEM) && it.nbt.test(itemStack) }
return this.fossils.any { it.test(itemStack) }
}
}
@@ -47,10 +47,10 @@ abstract class ParentWidget(
super.mouseMoved(pMouseX, pMouseY)
}
override fun mouseScrolled(pMouseX: Double, pMouseY: Double, pDelta: Double): Boolean {
override fun mouseScrolled(mouseX: Double, mouseY: Double, horizontalAmount: Double, verticalAmount: Double): Boolean {
return children.any {
it.mouseScrolled(pMouseX, pMouseY, pDelta)
} || super.mouseScrolled(pMouseX, pMouseY, pDelta)
it.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount)
} || super.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount)
}
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
@@ -19,8 +19,8 @@ import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunction
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
import com.cobblemon.mod.common.util.effectiveName
import com.cobblemon.mod.common.util.isInt
import com.cobblemon.mod.common.util.itemRegistry
import net.minecraft.block.Block
@@ -102,7 +102,7 @@ object MoLangFunctions {
fun ServerPlayerEntity.asMoLangValue(): ObjectValue<ServerPlayerEntity> {
val value = ObjectValue(
obj = this,
stringify = { it.entityName }
stringify = { it.effectiveName().string }
)
value.addFunctions(playerFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
@@ -62,7 +62,7 @@ object ActionEffects : JsonDataRegistry<ActionEffectTimeline> {
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(ActionEffectKeyframe::class.java, ActionEffectKeyframeAdapter)
.registerTypeAdapter(NumberRange.FloatRange::class.java, FloatNumberRangeAdapter)
.registerTypeAdapter(NumberRange.DoubleRange::class.java, FloatNumberRangeAdapter)
.registerTypeAdapter(TypeToken.getParameterized(Collection::class.java, Box::class.java).type, BoxCollectionAdapter)
.registerTypeAdapter(Box::class.java, BoxAdapter)
.registerTypeAdapter(Vec3d::class.java, VerboseVec3dAdapter)
@@ -36,7 +36,7 @@ class MoveToTargetActionEffectKeyframe : ActionEffectKeyframe {
var timedOut = false
val proximity = this.proximity.takeIf { it != -1F } ?: (sqrt(2 * user.boundingBox.xLength.pow(2)) + 1.5F + sqrt(2 * target.boundingBox.xLength.pow(2))).toFloat()
val proximity = this.proximity.takeIf { it != -1F } ?: (sqrt(2 * user.boundingBox.lengthX.pow(2)) + 1.5F + sqrt(2 * target.boundingBox.lengthX.pow(2))).toFloat()
if (target.distanceTo(user) !in proximity..20F) {
future.complete(Unit)
@@ -9,7 +9,9 @@
package com.cobblemon.mod.common.api.multiblock
import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
import net.minecraft.block.*
import net.minecraft.block.BlockRenderType
import net.minecraft.block.BlockState
import net.minecraft.block.BlockWithEntity
import net.minecraft.block.entity.BlockEntity
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.player.PlayerEntity
@@ -19,8 +21,8 @@ import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldView
/**
* A block that can be part of a [MultiblockStructure]
@@ -52,24 +54,22 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
if(hand == Hand.OFF_HAND) {
return ActionResult.SUCCESS
}
if (!world.isClient) {
val entity = world.getBlockEntity(pos) as MultiblockEntity?
if (entity?.multiblockStructure != null) {
return entity.multiblockStructure!!.onUse(state, world, pos, player, hand, hit)
}
val entity = world.getBlockEntity(pos) as MultiblockEntity?
if (entity?.multiblockStructure != null) {
return entity.multiblockStructure!!.onUse(state, world, pos, player, hand, hit)
}
return super.onUse(state, world, pos, player, hand, hit)
}
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?): BlockState {
val result = super.onBreak(world, pos, state, player)
if (!world.isClient) {
val entity = world.getBlockEntity(pos)
if (entity is MultiblockEntity && entity.multiblockStructure != null) {
entity.multiblockStructure!!.onBreak(world, pos, state, player)
}
entity?.markRemoved()
}
super.onBreak(world, pos, state, player)
return result
}
override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? {
@@ -81,12 +81,11 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
return BlockRenderType.MODEL
}
//This is done so a block picked with NBT doesnt absolutely DESTROY multiblocks
override fun getPickStack(world: BlockView, pos: BlockPos, state: BlockState): ItemStack {
override fun getPickStack(world: WorldView, pos: BlockPos, state: BlockState): ItemStack {
val blockEntity = world.getBlockEntity(pos) as? MultiblockEntity ?: return ItemStack.EMPTY
return if (blockEntity.multiblockStructure == null) super.getPickStack(world, pos, state) else ItemStack.EMPTY
}
abstract fun createMultiBlockEntity(pos: BlockPos, state: BlockState): FossilMultiblockEntity
}
}
@@ -43,6 +43,6 @@ interface MultiblockStructure {
return 0
}
fun markRemoved(world: World):Unit
fun markRemoved(world: World)
fun onTriggerEvent(state: BlockState?, world: ServerWorld?, pos: BlockPos?, random: Random?)
}
@@ -27,6 +27,7 @@ import com.cobblemon.mod.common.pokemon.status.PersistentStatus
import com.cobblemon.mod.common.util.*
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
import kotlin.random.Random
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtElement
@@ -54,6 +55,11 @@ import java.util.UUID
*/
open class PokemonProperties {
companion object {
val CODEC: Codec<PokemonProperties> = Codec.STRING.xmap(
{ parse(it) },
{ it.originalString }
)
@JvmOverloads
fun parse(string: String, delimiter: String = " ", assigner: String = "="): PokemonProperties {
val props = PokemonProperties()
@@ -496,7 +502,7 @@ open class PokemonProperties {
shiny?.let { nbt.putBoolean(DataKeys.POKEMON_SHINY, it) }
gender?.let { nbt.putString(DataKeys.POKEMON_GENDER, it.name) }
species?.let { nbt.putString(DataKeys.POKEMON_SPECIES_TEXT, it) }
nickname?.let { nbt.putString(DataKeys.POKEMON_NICKNAME, Text.Serializer.toJson(it)) }
nickname?.let { nbt.putString(DataKeys.POKEMON_NICKNAME, Text.Serialization.toJsonString(it)) }
form?.let { nbt.putString(DataKeys.POKEMON_FORM_ID, it) }
friendship?.let { nbt.putInt(DataKeys.POKEMON_FRIENDSHIP, it) }
pokeball?.let { nbt.putString(DataKeys.POKEMON_CAUGHT_BALL, it) }
@@ -524,7 +530,7 @@ open class PokemonProperties {
shiny = if (tag.contains(DataKeys.POKEMON_SHINY)) tag.getBoolean(DataKeys.POKEMON_SHINY) else null
gender = if (tag.contains(DataKeys.POKEMON_GENDER)) Gender.valueOf(tag.getString(DataKeys.POKEMON_GENDER)) else null
species = if (tag.contains(DataKeys.POKEMON_SPECIES_TEXT)) tag.getString(DataKeys.POKEMON_SPECIES_TEXT) else null
nickname = if (tag.contains(DataKeys.POKEMON_NICKNAME)) Text.Serializer.fromJson(tag.getString(DataKeys.POKEMON_NICKNAME)) else null
nickname = if (tag.contains(DataKeys.POKEMON_NICKNAME)) Text.Serialization.fromJson(tag.getString(DataKeys.POKEMON_NICKNAME)) else null
form = if (tag.contains(DataKeys.POKEMON_FORM_ID)) tag.getString(DataKeys.POKEMON_FORM_ID) else null
friendship = if (tag.contains(DataKeys.POKEMON_FRIENDSHIP)) tag.getInt(DataKeys.POKEMON_FRIENDSHIP) else null
pokeball = if (tag.contains(DataKeys.POKEMON_CAUGHT_BALL)) tag.getString(DataKeys.POKEMON_CAUGHT_BALL) else null
@@ -554,7 +560,7 @@ open class PokemonProperties {
shiny?.let { json.addProperty(DataKeys.POKEMON_SHINY, it) }
gender?.let { json.addProperty(DataKeys.POKEMON_GENDER, it.name) }
species?.let { json.addProperty(DataKeys.POKEMON_SPECIES_TEXT, it) }
nickname?.let { json.addProperty(DataKeys.POKEMON_NICKNAME, Text.Serializer.toJson(it)) }
nickname?.let { json.addProperty(DataKeys.POKEMON_NICKNAME, Text.Serialization.toJsonString(it)) }
form?.let { json.addProperty(DataKeys.POKEMON_FORM_ID, it) }
friendship?.let { json.addProperty(DataKeys.POKEMON_FRIENDSHIP, it) }
pokeball?.let { json.addProperty(DataKeys.POKEMON_CAUGHT_BALL, it) }
@@ -583,7 +589,7 @@ open class PokemonProperties {
shiny = json.get(DataKeys.POKEMON_SHINY)?.asBoolean
gender = json.get(DataKeys.POKEMON_GENDER)?.asString?.let { Gender.valueOf(it) }
species = json.get(DataKeys.POKEMON_SPECIES_TEXT)?.asString
nickname = json.get(DataKeys.POKEMON_NICKNAME)?.asString?.let { Text.Serializer.fromJson(it) }
nickname = json.get(DataKeys.POKEMON_NICKNAME)?.asString?.let { Text.Serialization.fromJson(it) }
form = json.get(DataKeys.POKEMON_FORM_ID)?.asString
friendship = json.get(DataKeys.POKEMON_FRIENDSHIP)?.asInt
pokeball = json.get(DataKeys.POKEMON_CAUGHT_BALL)?.asString
@@ -671,4 +677,4 @@ open class PokemonProperties {
return potentialAbility.template.create(false)
}
}
}
@@ -153,7 +153,7 @@ interface Evolution : EvolutionLike {
pokemon.getOwnerPlayer()?.sendMessage(lang("experience.learned_move", pokemon.getDisplayName(), move.displayName))
}
// we want to instantly tick for example you might only evolve your Bulbasaur at level 34 so Venusaur should be immediately available
pokemon.lockedEvolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
pokemon.evolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
pokemon.getOwnerPlayer()?.playSound(CobblemonSounds.EVOLVING, SoundCategory.NEUTRAL, 1F, 1F)
CobblemonEvents.EVOLUTION_COMPLETE.post(EvolutionCompleteEvent(pokemon, this))
}
@@ -35,13 +35,11 @@ class TimeRange : IntRanges {
"night" to TimeRange(12542..23459),
"noon" to TimeRange(5000..6999),
"midnight" to TimeRange(17000..18999),
"dawn" to TimeRange(22300..23999, 0..999),
"dawn" to TimeRange(22300..23999, 0..166),
"dusk" to TimeRange(11834..13701),
"twilight" to TimeRange(11834..13701, 22300..23999, 0..166),
"morning" to TimeRange(23000..23999, 0..4999),
"afternoon" to TimeRange(7000..12999),
"predawn" to TimeRange(19000..22999),
"evening" to TimeRange(13000..16999)
"morning" to TimeRange(0..4999),
"afternoon" to TimeRange(7000..12039)
)
}
@@ -78,7 +78,7 @@ abstract class SpawningCondition<T : SpawningContext> {
return false
} else if (ctx.position.z < minZ.orMin() || ctx.position.z > maxZ.orMax()) {
return false
} else if (dimensions != null && dimensions!!.isNotEmpty() && ctx.world.dimensionKey.value !in dimensions!!) {
} else if (dimensions != null && dimensions!!.isNotEmpty() && ctx.world.dimension.effects !in dimensions!!) {
return false
} else if (moonPhase != null && ctx.moonPhase !in moonPhase!!) {
return false
@@ -30,8 +30,8 @@ import net.minecraft.server.world.ServerWorld
*/
interface SpawningContextCalculator<I : SpawningContextInput, O : SpawningContext> {
companion object {
val isAirCondition: (BlockState) -> Boolean = { it.isAir || (!it.isSolid && !it.fluidState.isIn(FluidTags.WATER)) }
val isSolidCondition: (BlockState) -> Boolean = { it.isSolid }
val isAirCondition: (BlockState) -> Boolean = { it.isAir || (!it.isSolid && !it.isLiquid) }
val isSolidCondition: (BlockState) -> Boolean = { it.isSolid && !it.isIn(BlockTags.LEAVES) }
val isWaterCondition: (BlockState) -> Boolean = { it.fluidState.isIn(FluidTags.WATER) && it.fluidState.isStill }
val isLavaCondition: (BlockState) -> Boolean = { it.fluidState.isIn(FluidTags.LAVA) && it.fluidState.isStill }
@@ -34,19 +34,15 @@ abstract class SpawnAction<R>(
/**
* Runs the spawn action and processes any non-null result.
*/
open fun complete(): R? {
if (future.isDone) {
return null
}
open fun complete(): Boolean {
ctx.influences.forEach { it.affectAction(this) }
val result = run()
if (result != null) {
return if (result != null) {
future.complete(result)
true
} else {
future.completeExceptionally(Exception("Nothing was spawned."))
false
}
return result
}
}
@@ -48,7 +48,7 @@ abstract class SpawnDetailPreset {
var weightMultipliers: MutableList<WeightMultiplier>? = null
var weight: Float? = null
var percentage: Float? = null
var mergeMode = MergeMode.INSERT
var mergeMode = MergeMode.KEEP
open fun apply(spawnDetail: SpawnDetail) {
bucket?.let { spawnDetail.bucket = it }
@@ -46,7 +46,7 @@ class ReforgedConversion(val base: Path) : CobblemonConverter<NbtCompound> {
return null
}
val nbt = NbtIo.read(target.toFile())
val nbt = NbtIo.read(target)
if(nbt != null) {
return (if (extension == "pk") party(uuid, nbt) else pc(uuid, nbt)) as T
}
@@ -14,6 +14,7 @@ import java.io.File
import java.util.UUID
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtIo
import net.minecraft.nbt.NbtSizeTracker
/**
* A [OneToOneFileStoreAdapter] that can arbitrarily save a single [PokemonStore] into an [NbtCompound] with the
@@ -29,7 +30,7 @@ open class NBTStoreAdapter(
folderPerClass: Boolean,
) : OneToOneFileStoreAdapter<NbtCompound>(rootFolder, useNestedFolders, folderPerClass, "dat") {
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 save(file: File, serialized: NbtCompound) = NbtIo.writeCompressed(serialized, file.toPath())
override fun <E, T : PokemonStore<E>> load(file: File, storeClass: Class<out T>, uuid: UUID): T? {
val store = try {
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
@@ -37,7 +38,7 @@ open class NBTStoreAdapter(
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
}
return try {
val nbt = NbtIo.readCompressed(file)
val nbt = NbtIo.readCompressed(file.toPath(), NbtSizeTracker.ofUnlimitedBytes())
store.loadFromNBT(nbt)
store
} catch (e: Exception) {
@@ -16,6 +16,7 @@ import java.nio.file.Path
import java.util.UUID
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtIo
import net.minecraft.nbt.NbtSizeTracker
import net.minecraft.util.WorldSavePath
object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
@@ -61,13 +62,13 @@ object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
cache[uuid]!!
else {
val file = this.file(uuid)
if (!file.exists()) {
if (!file.toFile().exists()) {
val data = VariableStruct()
cache[uuid] = data
return data
}
val nbt = NbtIo.readCompressed(file(uuid))
val nbt = NbtIo.readCompressed(file(uuid), NbtSizeTracker.ofUnlimitedBytes())
// If it's not a VariableStruct then someone's fucked around and will subsequently find out
val data = readMoValueFromNBT(nbt) as VariableStruct
@@ -80,10 +81,10 @@ object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
val file = file(uuid)
val data = cache[uuid] ?: return
val nbt = writeMoValueToNBT(data)!! as NbtCompound
file.parentFile.mkdirs()
file.toFile().parentFile.mkdirs()
NbtIo.writeCompressed(nbt, file)
dirty -= uuid
}
private fun file(uuid: UUID) = savePath.resolve("playermolangdata/${uuid.toString().substring(0, 2)}/$uuid.dat").toFile()
private fun file(uuid: UUID) = savePath.resolve("playermolangdata/${uuid.toString().substring(0, 2)}/$uuid.dat")
}
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.storage.party
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.PartyCheckContext
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.evolution.PassiveEvolution
import com.cobblemon.mod.common.api.storage.pc.PCStore
@@ -63,7 +62,7 @@ open class PlayerPartyStore(
pokemon.refreshOriginalTrainer()
return if (super.add(pokemon)) {
pokemon.getOwnerPlayer()?.let { CobblemonCriteria.PARTY_CHECK.trigger(it, PartyCheckContext(this)) }
pokemon.getOwnerPlayer()?.let { CobblemonCriteria.PARTY_CHECK.trigger(it, this) }
true
} else {
val player = playerUUID.getPlayer()
@@ -124,7 +123,7 @@ open class PlayerPartyStore(
}
// Passive evolutions
pokemon.lockedEvolutions.filterIsInstance<PassiveEvolution>().forEach { it.attemptEvolution(pokemon) }
pokemon.evolutions.filterIsInstance<PassiveEvolution>().forEach { it.attemptEvolution(pokemon) }
val removeList = mutableListOf<Evolution>()
pokemon.evolutionProxy.server().forEach {
if (!it.test(pokemon) && it is LevelUpEvolution && !it.permanent)
@@ -179,21 +178,21 @@ open class PlayerPartyStore(
if (pokemon1 != null && pokemon2 != null) {
val player = pokemon1.getOwnerPlayer()
if (player != null) {
CobblemonCriteria.PARTY_CHECK.trigger(player, PartyCheckContext(this))
CobblemonCriteria.PARTY_CHECK.trigger(player, this)
}
} else if (pokemon1 != null || pokemon2 != null) {
var player = pokemon1?.getOwnerPlayer()
if (player != null) {
CobblemonCriteria.PARTY_CHECK.trigger(player, PartyCheckContext(this))
CobblemonCriteria.PARTY_CHECK.trigger(player, this)
} else {
player = pokemon2!!.getOwnerPlayer()
CobblemonCriteria.PARTY_CHECK.trigger(player!!, PartyCheckContext(this))
CobblemonCriteria.PARTY_CHECK.trigger(player!!, this)
}
}
}
override fun set(position: PartyPosition, pokemon: Pokemon) {
super.set(position, pokemon)
pokemon.getOwnerPlayer()?.let { CobblemonCriteria.PARTY_CHECK.trigger(it, PartyCheckContext(this)) }
pokemon.getOwnerPlayer()?.let { CobblemonCriteria.PARTY_CHECK.trigger(it, this) }
}
}
@@ -99,7 +99,7 @@ open class PCStore(
this.lockedSize = lockNewSize
if (boxes.size > newSize) {
// reduce
val slicedBoxes = boxes.slice(newSize until boxes.size)
val slicedBoxes = boxes.slice(boxes.size until newSize)
boxes.removeAll(slicedBoxes)
slicedBoxes.flatMap { it.asIterable() }.forEach(overflowHandler)
} else {
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.api.storage.player
import com.cobblemon.mod.common.advancement.criterion.AspectCriterionCondition
import com.cobblemon.mod.common.advancement.criterion.AspectCriterion
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.network.ServerPlayerEntity
@@ -103,9 +103,9 @@ class PlayerAdvancementData {
fun updateAspectsCollected(player: ServerPlayerEntity, pokemon: Pokemon) {
val aspectConditions = player.advancementTracker.progress.keys
.flatMap { it.criteria.values }
.flatMap { it.value.criteria.values }
.mapNotNull { it.conditions }
.filterIsInstance<AspectCriterionCondition>()
.filterIsInstance<AspectCriterion>()
val trackedAspects = aspectConditions
.filter { it.pokemon == pokemon.species.resourceIdentifier }
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.battles
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonSounds
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.storage.party.PartyStore
@@ -18,15 +17,15 @@ import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.effectiveName
import com.cobblemon.mod.common.util.getBattleTheme
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.party
import java.util.Optional
import java.util.UUID
import net.minecraft.entity.Entity
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.MutableText
import net.minecraft.text.Text
import java.util.UUID
object BattleBuilder {
@JvmOverloads
@@ -71,6 +70,7 @@ object BattleBuilder {
player1Actor.battleTheme = player2.getBattleTheme()
player2Actor.battleTheme = player1.getBattleTheme()
}
errors
} else {
errors
}
@@ -139,6 +139,7 @@ object BattleBuilder {
}
playerActor.battleTheme = pokemonEntity.getBattleTheme()
}
errors
} else {
errors
}
@@ -168,8 +169,8 @@ interface BattleStartError {
fun getMessageFor(entity: Entity): MutableText
companion object {
fun alreadyInBattle(player: ServerPlayerEntity) = AlreadyInBattleError(player.uuid, player.displayName)
fun alreadyInBattle(pokemonEntity: PokemonEntity) = AlreadyInBattleError(pokemonEntity.uuid, pokemonEntity.displayName)
fun alreadyInBattle(player: ServerPlayerEntity) = AlreadyInBattleError(player.uuid, player.effectiveName())
fun alreadyInBattle(pokemonEntity: PokemonEntity) = AlreadyInBattleError(pokemonEntity.uuid, pokemonEntity.effectiveName())
fun alreadyInBattle(actor: BattleActor) = AlreadyInBattleError(actor.uuid, actor.getName())
fun targetIsBusy(targetName: MutableText) = BusyError(targetName)
@@ -209,7 +210,7 @@ class InsufficientPokemonError(
} else {
battleLang(
"error.insufficient_pokemon",
player.displayName,
player.effectiveName(),
requiredCount,
hadCount
)
@@ -35,7 +35,7 @@ class BoostInstruction(val instructionSet: InstructionSet, val message: BattleMe
val severity = Stats.getSeverity(stages)
val rootKey = if (isBoost) "boost" else "unboost"
battle.dispatchWaiting {
battle.dispatchWaiting(1.5F) {
val lang = when {
message.hasOptionalArgument("zeffect") -> battleLang("$rootKey.$severity.zeffect", pokemon.getName(), stat)
else -> battleLang("$rootKey.$severity", pokemon.getName(), stat)
@@ -54,8 +54,8 @@ class DamageInstruction(
if (recoiling) {
battlePokemon.effectedPokemon.let { pokemon ->
if (RecoilEvolutionProgress.supports(pokemon)) {
val healthStr = privateMessage.argumentAt(1) ?: throw UnsupportedOperationException("Cant get recoil string")
val newHealth = "([0-9]+).*".toRegex().find(healthStr)?.groups?.get(1)?.value?.toIntOrNull() ?: throw UnsupportedOperationException("Cant get recoil string")
val newPercentage = privateMessage.argumentAt(1)?.split("/")?.getOrNull(0)?.toIntOrNull() ?: 0
val newHealth = (pokemon.hp * (newPercentage / 100.0)).roundToInt()
val difference = pokemon.currentHealth - newHealth
if (difference > 0) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is RecoilEvolutionProgress }) { RecoilEvolutionProgress() }
@@ -26,7 +26,7 @@ import net.minecraft.world.biome.Biome
* @since December 2nd, 2022
*/
class BiomeDownfallGrowthFactor(
val range: NumberRange.FloatRange,
val range: NumberRange.DoubleRange,
val bonusYield: IntRange
) : GrowthFactor {
@@ -26,7 +26,7 @@ import net.minecraft.world.biome.Biome
* @since December 2nd, 2022
*/
class BiomeTemperatureGrowthFactor(
val range: NumberRange.FloatRange,
val range: NumberRange.DoubleRange,
val bonusYield: IntRange
) : GrowthFactor {
@@ -15,6 +15,8 @@ import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.util.playSoundServer
import com.cobblemon.mod.common.util.toVec3d
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.block.*
import net.minecraft.entity.ItemEntity
import net.minecraft.entity.ai.pathing.NavigationType
@@ -111,7 +113,7 @@ class ApricornBlock(settings: Settings, val apricorn: Apricorn) : HorizontalFaci
else super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
}
override fun isFertilizable(world: WorldView, pos: BlockPos, state: BlockState, isClient: Boolean) = state.get(AGE) < MAX_AGE
override fun isFertilizable(world: WorldView, pos: BlockPos, state: BlockState) = state.get(AGE) < MAX_AGE
override fun canGrow(world: World, random: Random, pos: BlockPos, state: BlockState) = true
@@ -143,7 +145,7 @@ class ApricornBlock(settings: Settings, val apricorn: Apricorn) : HorizontalFaci
}
// We need to point back to the actual apricorn item, see SweetBerryBushBlock for example
override fun getPickStack(world: BlockView, pos: BlockPos, state: BlockState) = ItemStack(this.apricorn.item())
override fun getPickStack(world: WorldView, pos: BlockPos, state: BlockState) = ItemStack(this.apricorn.item())
private fun doHarvest(world: World, state: BlockState, pos: BlockPos, player: PlayerEntity) {
val resetState = this.harvest(world, state, pos)
@@ -188,7 +190,15 @@ class ApricornBlock(settings: Settings, val apricorn: Apricorn) : HorizontalFaci
return true
}
override fun getCodec(): MapCodec<out HorizontalFacingBlock> {
return CODEC
}
companion object {
val CODEC: MapCodec<ApricornBlock> = RecordCodecBuilder.mapCodec { it.group(
createSettingsCodec(),
Apricorn.CODEC.fieldOf("apricorn").forGetter(ApricornBlock::apricorn)
).apply(it, ::ApricornBlock) }
val AGE: IntProperty = Properties.AGE_3
const val MAX_AGE = Properties.AGE_3_MAX
@@ -10,5 +10,20 @@ package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.api.apricorn.Apricorn
import com.cobblemon.mod.common.block.grower.ApricornTreeGrower
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.block.SaplingBlock
class ApricornSaplingBlock(properties : Settings, apricorn: Apricorn) : SaplingBlock(ApricornTreeGrower(apricorn), properties)
class ApricornSaplingBlock(properties : Settings, val apricorn: Apricorn) : SaplingBlock(ApricornTreeGrower(apricorn), properties) {
override fun getCodec(): MapCodec<out SaplingBlock> {
return CODEC
}
companion object {
val CODEC: MapCodec<ApricornSaplingBlock> = RecordCodecBuilder.mapCodec { it.group(
createSettingsCodec(),
Apricorn.CODEC.fieldOf("apricorn").forGetter(ApricornSaplingBlock::apricorn)
).apply(it, ::ApricornSaplingBlock) }
}
}
@@ -19,6 +19,8 @@ import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.api.mulch.Mulchable
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.block.Block
import net.minecraft.block.BlockRenderType
import net.minecraft.block.BlockState
@@ -68,21 +70,21 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
*/
fun berry(): Berry? = Berries.getByIdentifier(this.berryIdentifier)
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) {
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity): BlockState {
if (!player.isCreative && state.get(AGE) == FRUIT_AGE) {
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
treeEntity.harvest(world, state, pos, player).forEach { drop -> Block.dropStack(world, pos, drop) }
}
super.onBreak(world, pos, state, player)
return super.onBreak(world, pos, state, player)
}
override fun createBlockEntity(pos: BlockPos, state: BlockState) = BerryBlockEntity(pos, state, berryIdentifier)
override fun isFertilizable(world: WorldView, pos: BlockPos, state: BlockState, isClient: Boolean) = !this.isMaxAge(state)
override fun isFertilizable(world: WorldView, pos: BlockPos, state: BlockState) = !this.isMaxAge(state)
override fun canGrow(world: World, random: Random, pos: BlockPos, state: BlockState) = !this.isMaxAge(state)
override fun <T : BlockEntity> getTicker(world: World, blockState: BlockState, blockWithEntityType: BlockEntityType<T>): BlockEntityTicker<T>? = checkType(blockWithEntityType, CobblemonBlockEntities.BERRY, BerryBlockEntity.TICKER)
override fun <T : BlockEntity> getTicker(world: World, blockState: BlockState, blockWithEntityType: BlockEntityType<T>): BlockEntityTicker<T>? = validateTicker(blockWithEntityType, CobblemonBlockEntities.BERRY, BerryBlockEntity.TICKER)
init {
defaultState = this.stateManager.defaultState
@@ -192,6 +194,10 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|| below.block is FarmlandBlock
}
override fun getCodec(): MapCodec<out BlockWithEntity> {
return CODEC
}
@Deprecated("Deprecated in Java")
override fun getStateForNeighborUpdate(state: BlockState, direction: Direction, neighborState: BlockState, world: WorldAccess, pos: BlockPos, neighborPos: BlockPos): BlockState {
return if (state.canPlaceAt(world, pos)) super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos) else Blocks.AIR.defaultState
@@ -210,7 +216,7 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
builder.add(MULCH)
}
override fun getPickStack(world: BlockView?, pos: BlockPos?, state: BlockState?): ItemStack {
override fun getPickStack(world: WorldView?, pos: BlockPos?, state: BlockState?): ItemStack {
val berryItem = this.berry()?.item() ?: return ItemStack.EMPTY
return ItemStack(berryItem)
}
@@ -232,6 +238,11 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
override fun getRenderType(blockState: BlockState) = BlockRenderType.MODEL
companion object {
val CODEC: MapCodec<BerryBlock> = RecordCodecBuilder.mapCodec { it.group(
Identifier.CODEC.fieldOf("berry").forGetter(BerryBlock::berryIdentifier),
createSettingsCodec()
).apply(it, ::BerryBlock) }
const val MATURE_AGE = 3
const val FLOWER_AGE = 4
const val FRUIT_AGE = 5
@@ -312,4 +323,4 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
world.setBlockState(pos, state.with(MULCH, mulch))
}
}
}
}
@@ -8,6 +8,8 @@
package com.cobblemon.mod.common.block
import com.mojang.serialization.MapCodec
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.Blocks
import net.minecraft.block.ShapeContext
@@ -39,10 +41,14 @@ class BigRootBlock(settings: Settings) : RootBlock(settings) {
override fun shearedDrop(): ItemStack = Items.STRING.defaultStack
override fun getCodec(): MapCodec<out Block> {
return CODEC
}
companion object {
val CODEC = createCodec(::BigRootBlock)
private val AABB = VoxelShapes.cuboid(0.2, 0.3, 0.2, 0.8, 1.0, 0.8)
}
}
@@ -8,13 +8,19 @@
package com.cobblemon.mod.common.block
import net.minecraft.block.*
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.Blocks
import net.minecraft.block.HorizontalFacingBlock
import net.minecraft.block.ShapeContext
import net.minecraft.item.ItemPlacementContext
import net.minecraft.state.StateManager
import net.minecraft.state.property.BooleanProperty
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
import net.minecraft.world.WorldAccess
@@ -51,6 +57,10 @@ class CoinPouchBlock(settings: Settings, val small: Boolean) : HorizontalFacingB
return null
}
override fun getCodec(): MapCodec<out HorizontalFacingBlock> {
return CODEC
}
@Deprecated("Deprecated in Java")
override fun getStateForNeighborUpdate(
state: BlockState,
@@ -70,5 +80,10 @@ class CoinPouchBlock(settings: Settings, val small: Boolean) : HorizontalFacingB
companion object {
val NATURAL: BooleanProperty = BooleanProperty.of("natural")
val CODEC: MapCodec<CoinPouchBlock> = RecordCodecBuilder.mapCodec { it.group(
createSettingsCodec(),
PrimitiveCodec.BOOL.fieldOf("small").forGetter(CoinPouchBlock::small),
).apply(it, ::CoinPouchBlock) }
}
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
import com.cobblemon.mod.common.item.PokeBallItem
import com.mojang.serialization.MapCodec
import net.minecraft.block.*
import net.minecraft.block.HorizontalFacingBlock.*
import net.minecraft.entity.ai.pathing.NavigationType
@@ -36,10 +37,6 @@ class DisplayCaseBlock(settings: Settings) : BlockWithEntity(settings) {
.with(ITEM_DIRECTION, Direction.NORTH)
}
companion object {
val ITEM_DIRECTION = DirectionProperty.of("item_facing")
}
override fun getPlacementState(ctx: ItemPlacementContext): BlockState? {
var blockState = defaultState
val worldView = ctx.world
@@ -95,12 +92,12 @@ class DisplayCaseBlock(settings: Settings) : BlockWithEntity(settings) {
return result
}
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) {
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity): BlockState {
val entity = world.getBlockEntity(pos) as DisplayCaseBlockEntity
if (!entity.getStack().isEmpty && !player.isCreative) {
ItemScatterer.spawn(world, pos, entity.inv)
}
super.onBreak(world, pos, state, player)
return super.onBreak(world, pos, state, player)
}
override fun getRenderType(state: BlockState?) = BlockRenderType.MODEL
@@ -115,7 +112,15 @@ class DisplayCaseBlock(settings: Settings) : BlockWithEntity(settings) {
}
override fun hasComparatorOutput(state: BlockState?) = true
override fun getCodec(): MapCodec<out BlockWithEntity> {
return CODEC
}
override fun canPathfindThrough(state: BlockState, world: BlockView, pos: BlockPos, type: NavigationType) = false
companion object {
val CODEC = createCodec(::DisplayCaseBlock)
val ITEM_DIRECTION = DirectionProperty.of("item_facing")
}
}
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.CobblemonItems
import com.mojang.serialization.MapCodec
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.ShapeContext
import net.minecraft.item.ItemStack
@@ -27,10 +29,14 @@ class EnergyRootBlock(settings: Settings) : RootBlock(settings) {
override fun shearedDrop(): ItemStack = CobblemonItems.ENERGY_ROOT.defaultStack
override fun getCodec(): MapCodec<out Block> {
return CODEC
}
companion object {
private val CODEC = createCodec(::EnergyRootBlock)
private val AABB = VoxelShapes.cuboid(0.2, 0.1, 0.2, 0.8, 1.0, 0.8)
}
}
@@ -14,9 +14,11 @@ import com.cobblemon.mod.common.block.entity.FossilAnalyzerBlockEntity
import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockStructure
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
import com.mojang.serialization.MapCodec
import net.minecraft.block.Block
import net.minecraft.inventory.SidedInventory
import net.minecraft.block.BlockState
import net.minecraft.block.BlockWithEntity
import net.minecraft.block.HorizontalFacingBlock
import net.minecraft.block.InventoryProvider
import net.minecraft.block.entity.BlockEntity
@@ -41,11 +43,15 @@ class FossilAnalyzerBlock(properties: Settings) : MultiblockBlock(properties), I
return FossilAnalyzerBlockEntity(pos, state, FossilMultiblockBuilder(pos))
}
override fun getCodec(): MapCodec<out BlockWithEntity> {
return CODEC
}
override fun <T : BlockEntity?> getTicker(
world: World?,
state: BlockState?,
type: BlockEntityType<T>?
): BlockEntityTicker<T>? = checkType(type, CobblemonBlockEntities.FOSSIL_ANALYZER, FossilMultiblockStructure.TICKER::tick)
): BlockEntityTicker<T>? = validateTicker(type, CobblemonBlockEntities.FOSSIL_ANALYZER, FossilMultiblockStructure.TICKER::tick)
override fun getPlacementState(blockPlaceContext: ItemPlacementContext): BlockState? {
return defaultState.with(HorizontalFacingBlock.FACING, blockPlaceContext.horizontalPlayerFacing)
@@ -68,5 +74,7 @@ class FossilAnalyzerBlock(properties: Settings) : MultiblockBlock(properties), I
companion object {
val ON = BooleanProperty.of("on")
val CODEC = createCodec(::FossilAnalyzerBlock)
}
}
}
@@ -34,7 +34,7 @@ abstract class GrowableStoneBlock(
settings: Settings,
val stage: Int,
val height: Int,
xzOffset: Int,
val xzOffset: Int,
val nextStage: Block?
) : FacingBlock(settings) {
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
import com.cobblemon.mod.common.util.*
import com.mojang.serialization.MapCodec
import net.minecraft.block.*
import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityTicker
@@ -47,6 +48,8 @@ import net.minecraft.world.World
@Suppress("DEPRECATED", "OVERRIDE_DEPRECATION")
class HealingMachineBlock(properties: Settings) : BlockWithEntity(properties) {
companion object {
val CODEC: MapCodec<HealingMachineBlock> = createCodec(::HealingMachineBlock)
private val NORTH_SOUTH_AABB = VoxelShapes.union(
VoxelShapes.cuboid(0.0, 0.0, 0.0, 1.0, 0.625, 1.0),
VoxelShapes.cuboid(0.0625, 0.625, 0.0, 0.9375, 0.875, 0.125),
@@ -93,6 +96,10 @@ class HealingMachineBlock(properties: Settings) : BlockWithEntity(properties) {
return this.defaultState.with(HorizontalFacingBlock.FACING, blockPlaceContext.horizontalPlayerFacing)
}
override fun getCodec(): MapCodec<out BlockWithEntity> {
return CODEC
}
override fun canPathfindThrough(blockState: BlockState, blockGetter: BlockView, blockPos: BlockPos, pathComputationType: NavigationType): Boolean {
return false
}
@@ -190,7 +197,7 @@ class HealingMachineBlock(properties: Settings) : BlockWithEntity(properties) {
override fun getComparatorOutput(state: BlockState, world: World, pos: BlockPos): Int = (world.getBlockEntity(pos) as? HealingMachineBlockEntity)?.currentSignal ?: 0
override fun <T : BlockEntity> getTicker(world: World, blockState: BlockState, blockWithEntityType: BlockEntityType<T>): BlockEntityTicker<T>? = checkType(blockWithEntityType, CobblemonBlockEntities.HEALING_MACHINE, HealingMachineBlockEntity.TICKER::tick)
override fun <T : BlockEntity> getTicker(world: World, blockState: BlockState, blockWithEntityType: BlockEntityType<T>): BlockEntityTicker<T>? = validateTicker(blockWithEntityType, CobblemonBlockEntities.HEALING_MACHINE, HealingMachineBlockEntity.TICKER::tick)
override fun getRenderType(blockState: BlockState): BlockRenderType {
return BlockRenderType.MODEL
@@ -201,4 +208,4 @@ class HealingMachineBlock(properties: Settings) : BlockWithEntity(properties) {
tooltip.add("block.${Cobblemon.MODID}.healing_machine.tooltip2".asTranslated().gray())
}
}
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.mojang.serialization.MapCodec
import net.minecraft.block.*
import net.minecraft.item.ItemConvertible
import net.minecraft.registry.tag.FluidTags
@@ -50,7 +51,7 @@ class MedicinalLeekBlock(settings: Settings) : CropBlock(settings) {
// These 3 are still around for the sake of compatibility, vanilla won't trigger it but some mods might
// We implement applyGrowth & getGrowthAmount for them
override fun isFertilizable(world: WorldView?, pos: BlockPos?, state: BlockState?, isClient: Boolean): Boolean = !this.isMature(state)
override fun isFertilizable(world: WorldView?, pos: BlockPos?, state: BlockState?): Boolean = !this.isMature(state)
override fun applyGrowth(world: World, pos: BlockPos, state: BlockState) {
world.setBlockState(pos, state.with(this.ageProperty, (this.getAge(state) + 1).coerceAtMost(this.maxAge)), NOTIFY_LISTENERS)
@@ -70,7 +71,12 @@ class MedicinalLeekBlock(settings: Settings) : CropBlock(settings) {
return floor.isIn(CobblemonBlockTags.MEDICINAL_LEEK_PLANTABLE) && fluidState.level == 8 && fluidState.isIn(FluidTags.WATER)
}
override fun getCodec(): MapCodec<out CropBlock> {
return CODEC
}
companion object {
val CODEC = createCodec(::MedicinalLeekBlock)
const val MATURE_AGE = 3
val AGE: IntProperty = Properties.AGE_3
@@ -11,7 +11,13 @@ package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.item.MintLeafItem
import net.minecraft.block.*
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.CropBlock
import net.minecraft.block.Fertilizable
import net.minecraft.block.ShapeContext
import net.minecraft.item.Item
import net.minecraft.item.ItemConvertible
import net.minecraft.registry.tag.BlockTags
@@ -19,6 +25,7 @@ import net.minecraft.server.world.ServerWorld
import net.minecraft.state.StateManager
import net.minecraft.state.property.BooleanProperty
import net.minecraft.state.property.IntProperty
import net.minecraft.util.StringIdentifiable
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.random.Random
import net.minecraft.util.shape.VoxelShape
@@ -74,8 +81,12 @@ class MintBlock(private val mintType: MintType, settings: Settings) : CropBlock(
world.setBlockState(pos, state.with(AGE, newAge), NOTIFY_LISTENERS)
}
override fun getCodec(): MapCodec<out CropBlock> {
return CODEC
}
@Suppress("unused")
enum class MintType {
enum class MintType : StringIdentifiable {
RED,
BLUE,
@@ -111,9 +122,18 @@ class MintBlock(private val mintType: MintType, settings: Settings) : CropBlock(
WHITE -> CobblemonBlocks.WHITE_MINT
}
override fun asString(): String = this.name.lowercase()
companion object {
val CODEC = StringIdentifiable.createBasicCodec(::values)
}
}
companion object {
val CODEC: MapCodec<MintBlock> = RecordCodecBuilder.mapCodec { it.group(
MintType.CODEC.fieldOf("mintType").forGetter(MintBlock::mintType),
createSettingsCodec()
).apply(it, ::MintBlock) }
val AGE: IntProperty = CropBlock.AGE
const val MATURE_AGE = MAX_AGE
@@ -139,4 +159,4 @@ class MintBlock(private val mintType: MintType, settings: Settings) : CropBlock(
)
}
}
}
@@ -12,8 +12,10 @@ import com.cobblemon.mod.common.api.multiblock.MultiblockBlock
import com.cobblemon.mod.common.api.multiblock.MultiblockEntity
import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
import com.mojang.serialization.MapCodec
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.BlockWithEntity
import net.minecraft.block.HorizontalFacingBlock
import net.minecraft.block.ShapeContext
import net.minecraft.entity.ai.pathing.NavigationType
@@ -42,6 +44,10 @@ class MonitorBlock(properties: Settings) : MultiblockBlock(properties) {
)
}
override fun getCodec(): MapCodec<out BlockWithEntity> {
return CODEC
}
override fun getPlacementState(blockPlaceContext: ItemPlacementContext): BlockState? {
return defaultState.with(HorizontalFacingBlock.FACING, blockPlaceContext.horizontalPlayerFacing)
}
@@ -84,6 +90,8 @@ class MonitorBlock(properties: Settings) : MultiblockBlock(properties) {
}
companion object {
val CODEC = createCodec(::MonitorBlock)
//0 is off
val SCREEN = EnumProperty.of("screen", MonitorScreen::class.java)
val HITBOX = VoxelShapes.union(
@@ -59,6 +59,7 @@ import net.minecraft.world.WorldView
class PCBlock(properties: Settings): BlockWithEntity(properties), Waterloggable {
companion object {
val CODEC = createCodec(::PCBlock)
val PART = EnumProperty.of("part", PCPart::class.java)
val ON = BooleanProperty.of("on")
val WATERLOGGED = BooleanProperty.of("waterlogged")
@@ -208,7 +209,7 @@ class PCBlock(properties: Settings): BlockWithEntity(properties), Waterloggable
return if (state.get(PART) == PCPart.BOTTOM) blockState.isSideSolidFullSquare(world, blockPos, Direction.UP) else blockState.isOf(this)
}
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?): BlockState {
if (!world.isClient && player?.isCreative == true) {
var blockPos: BlockPos = BlockPos.ORIGIN
var blockState: BlockState = state
@@ -219,9 +220,11 @@ class PCBlock(properties: Settings): BlockWithEntity(properties), Waterloggable
world.syncWorldEvent(player, WorldEvents.BLOCK_BROKEN, blockPos, getRawIdFromState(blockState))
}
}
super.onBreak(world, pos, state, player)
return super.onBreak(world, pos, state, player)
}
override fun getCodec() = CODEC
@Deprecated("Deprecated in Java")
override fun canPathfindThrough(blockState: BlockState, blockGetter: BlockView, blockPos: BlockPos, pathComputationType: NavigationType) = false
@@ -283,7 +286,7 @@ class PCBlock(properties: Settings): BlockWithEntity(properties), Waterloggable
return ActionResult.SUCCESS
}
override fun <T : BlockEntity> getTicker(world: World, blockState: BlockState, BlockWithEntityType: BlockEntityType<T>) = checkType(BlockWithEntityType, CobblemonBlockEntities.PC, PCBlockEntity.TICKER::tick)
override fun <T : BlockEntity> getTicker(world: World, blockState: BlockState, BlockWithEntityType: BlockEntityType<T>) = validateTicker(BlockWithEntityType, CobblemonBlockEntities.PC, PCBlockEntity.TICKER::tick)
@Deprecated("Deprecated in Java")
override fun getRenderType(blockState: BlockState): BlockRenderType {
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.util.isInBattle
import com.cobblemon.mod.common.util.playSoundServer
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.voxelShape
import com.mojang.serialization.MapCodec
import java.util.UUID
import net.minecraft.block.Block
import net.minecraft.block.BlockRenderType
@@ -64,6 +65,8 @@ import net.minecraft.world.WorldView
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterloggable, PreEmptsExplosion {
companion object {
val CODEC = createCodec(::PastureBlock)
val PART: EnumProperty<PasturePart> = EnumProperty.of("part", PasturePart::class.java)
val ON: BooleanProperty = BooleanProperty.of("on")
val WATERLOGGED: BooleanProperty = BooleanProperty.of("waterlogged")
@@ -174,6 +177,10 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
private fun isBase(state: BlockState): Boolean = state.contains(PART) && state.get(PART) == PasturePart.BOTTOM
override fun getCodec(): MapCodec<out BlockWithEntity> {
return CODEC
}
override fun canPathfindThrough(blockState: BlockState, blockGetter: BlockView, blockPos: BlockPos, pathComputationType: NavigationType) = false
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
@@ -193,7 +200,7 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
}
}
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?): BlockState {
checkBreakEntity(world, state, pos)
if (!world.isClient && player?.isCreative == true) {
var blockPos: BlockPos = BlockPos.ORIGIN
@@ -206,7 +213,7 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
world.syncWorldEvent(player, WorldEvents.BLOCK_BROKEN, blockPos, getRawIdFromState(blockState))
}
}
super.onBreak(world, pos, state, player)
return super.onBreak(world, pos, state, player)
}
override fun whenExploded(world: World, state: BlockState, pos: BlockPos) {
@@ -215,7 +222,7 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
}
override fun <T : BlockEntity?> getTicker(world: World, state: BlockState, type: BlockEntityType<T>): BlockEntityTicker<T>? {
return checkType(type, CobblemonBlockEntities.PASTURE, PokemonPastureBlockEntity.TICKER::tick)
return validateTicker(type, CobblemonBlockEntities.PASTURE, PokemonPastureBlockEntity.TICKER::tick)
}
override fun onPlaced(world: World, pos: BlockPos, state: BlockState, placer: LivingEntity?, itemStack: ItemStack?) {
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.multiblock.MultiblockEntity
import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
import com.cobblemon.mod.common.block.entity.RestorationTankBlockEntity
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
import com.mojang.serialization.MapCodec
import net.minecraft.block.*
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.ai.pathing.NavigationType
@@ -38,7 +39,6 @@ import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.minecraft.world.WorldEvents
import net.minecraft.world.WorldView
@@ -52,7 +52,6 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
.with(ON, false)
}
fun getPositionOfOtherPart(state: BlockState, pos: BlockPos): BlockPos {
return if (state.get(PART) == TankPart.BOTTOM) {
pos.up()
@@ -71,15 +70,16 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
private fun isBase(state: BlockState): Boolean = state.get(PART) == TankPart.BOTTOM
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
super.onBreak(world, pos, state, player)
if (!world.isClient) {
val otherPart = world.getBlockState(getPositionOfOtherPart(state, pos))
if (otherPart.block is RestorationTankBlock) {
world.setBlockState(getPositionOfOtherPart(state, pos), Blocks.AIR.defaultState, Block.NOTIFY_ALL)
world.syncWorldEvent(player, WorldEvents.BLOCK_BROKEN, getPositionOfOtherPart(state, pos), getRawIdFromState(otherPart))
}
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?): BlockState {
val result = super.onBreak(world, pos, state, player)
val otherPart = world.getBlockState(getPositionOfOtherPart(state, pos))
if (otherPart.block is RestorationTankBlock) {
world.setBlockState(getPositionOfOtherPart(state, pos), Blocks.AIR.defaultState, 35)
world.syncWorldEvent(player, 2001, getPositionOfOtherPart(state, pos), getRawIdFromState(otherPart))
}
return result
}
override fun onPlaced(
@@ -90,9 +90,9 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
itemStack: ItemStack?
) {
//Place the full block before we call to super to validate the multiblock
world.setBlockState(pos.up(), state.with(PART, TankPart.TOP) as BlockState, Block.NOTIFY_ALL)
world.setBlockState(pos.up(), state.with(PART, TankPart.TOP) as BlockState, 3)
world.updateNeighbors(pos, Blocks.AIR)
state.updateNeighbors(world, pos, Block.NOTIFY_ALL)
state.updateNeighbors(world, pos, 3)
super.onPlaced(world, pos, state, placer, itemStack)
}
@@ -103,7 +103,7 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
val tankBottomPos = pos.down()
val tankBottomState = world.getBlockState(tankBottomPos)
if(tankBottomState.block.equals(CobblemonBlocks.RESTORATION_TANK.asBlock()) && tankBottomState.get(PART) == TankPart.BOTTOM) {
return onUse(tankBottomState, world, tankBottomPos, player, hand, hit)
return super.onUse(tankBottomState, world, tankBottomPos, player, hand, hit)
}
}
return super.onUse(state, world, pos, player, hand, hit)
@@ -225,9 +225,15 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
return false
}
override fun getCodec(): MapCodec<out BlockWithEntity> {
return CODEC
}
companion object {
val CODEC = createCodec(::RestorationTankBlock)
val PART = EnumProperty.of("part", TankPart::class.java)
val TRIGGERED = Properties.TRIGGERED
val ON = BooleanProperty.of("on")
}
}
}
@@ -124,6 +124,7 @@ class RevivalHerbBlock(settings: Settings) : CropBlock(settings), Mulchable {
}
companion object {
val CODEC = createCodec(::RevivalHerbBlock)
const val MIN_AGE = 0
const val MAX_AGE = 8
@@ -70,7 +70,7 @@ abstract class RootBlock(settings: Settings) : Block(settings), Fertilizable, Sh
return if (direction == Direction.UP && !this.canPlaceAt(state, world, pos)) Blocks.AIR.defaultState else super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
}
override fun isFertilizable(world: WorldView, pos: BlockPos, state: BlockState, isClient: Boolean) = this.canSpread(world, pos, state)
override fun isFertilizable(world: WorldView, pos: BlockPos, state: BlockState) = this.canSpread(world, pos, state)
override fun canGrow(world: World, random: Random, pos: BlockPos, state: BlockState) = this.canSpread(world, pos, state)
@@ -187,4 +187,4 @@ abstract class RootBlock(settings: Settings) : Block(settings), Fertilizable, Sh
companion object {
const val MAX_PROPAGATING_LIGHT_LEVEL = 11
}
}
}
@@ -9,6 +9,11 @@
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.block.Block
import net.minecraft.block.FacingBlock
import com.cobblemon.mod.common.block.chest.GildedChestBlock
import net.minecraft.block.*
import net.minecraft.entity.player.PlayerEntity
@@ -20,10 +25,9 @@ import net.minecraft.server.world.ServerWorld
import net.minecraft.state.StateManager
import net.minecraft.state.property.BooleanProperty
import net.minecraft.state.property.Properties
import net.minecraft.state.property.Properties.WATERLOGGED
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.math.random.Random
import net.minecraft.util.shape.VoxelShape
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldAccess
@@ -38,16 +42,24 @@ class TumblestoneBlock(
nextStage: Block?
) : GrowableStoneBlock(settings, stage, height, xzOffset, nextStage), Waterloggable {
// TODO(Deltric): Look into Block.CODEC for being optional more
companion object {
val CODEC: MapCodec<TumblestoneBlock> = RecordCodecBuilder.mapCodec { it.group(
createSettingsCodec(),
PrimitiveCodec.INT.fieldOf("stage").forGetter { it.stage },
PrimitiveCodec.INT.fieldOf("height").forGetter { it.height },
PrimitiveCodec.INT.fieldOf("xzOffset").forGetter { it.xzOffset },
Block.CODEC.fieldOf("nextStage").forGetter { it.nextStage }
).apply(it, ::TumblestoneBlock) }
val WATERLOGGED = BooleanProperty.of("waterlogged")
}
init {
this.defaultState = this.stateManager.defaultState
.with(FACING, Direction.DOWN)
.with(WATERLOGGED, false)
}
companion object {
val WATERLOGGED = BooleanProperty.of("waterlogged")
}
override fun canGrow(pos: BlockPos, world: BlockView): Boolean {
if (stage == MAX_STAGE) return false
val iterator: Iterator<BlockPos> =
@@ -62,6 +74,10 @@ class TumblestoneBlock(
return true
}
override fun getCodec(): MapCodec<out FacingBlock> {
return CODEC
}
override fun getFluidState(state: BlockState): FluidState {
return if (state.get(GildedChestBlock.WATERLOGGED)) {
Fluids.WATER.getStill(false)
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.CobblemonItems
import com.mojang.serialization.MapCodec
import net.minecraft.block.BlockState
import net.minecraft.block.CropBlock
import net.minecraft.block.ShapeContext
@@ -29,7 +30,12 @@ class VivichokeBlock(settings: Settings) : CropBlock(settings) {
override fun getSeedsItem(): ItemConvertible = CobblemonItems.VIVICHOKE_SEEDS
override fun getCodec(): MapCodec<out CropBlock> {
return CODEC
}
companion object {
val CODEC = createCodec(::VivichokeBlock)
private val STAGE_0_SHAPE = createCuboidShape(6.0, -1.0, 6.0, 10.0, 2.0, 10.0)
private val STAGE_1_SHAPE = createCuboidShape(6.0, -1.0, 6.0, 10.0, 5.0, 10.0)
@@ -16,6 +16,8 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.toVec3d
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.block.*
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.ai.pathing.NavigationType
@@ -27,18 +29,22 @@ import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.screen.ScreenHandler
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.sound.SoundCategory
import net.minecraft.state.StateManager
import net.minecraft.state.property.BooleanProperty
import net.minecraft.state.property.Properties
import net.minecraft.text.MutableText
import net.minecraft.text.Text
import net.minecraft.util.*
import net.minecraft.util.ActionResult
import net.minecraft.util.BlockMirror
import net.minecraft.util.BlockRotation
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.ItemScatterer
import net.minecraft.util.StringIdentifiable
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.math.random.Random
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
@@ -55,6 +61,11 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
}
companion object {
val CODEC: MapCodec<GildedChestBlock> = RecordCodecBuilder.mapCodec { it.group(
createSettingsCodec(),
Type.CODEC.fieldOf("chestType").forGetter(GildedChestBlock::type)
).apply(it, ::GildedChestBlock) }
val POKEMON_ARGS = "gimmighoul"
val LEVEL_RANGE = 5..30
val WATERLOGGED = BooleanProperty.of("waterlogged")
@@ -126,15 +137,16 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
fun isFake() = (type == Type.FAKE)
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) {
if (!world.isClient) {
if (isFake() && (player is ServerPlayerEntity)) {
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity): BlockState {
if (isFake()) {
if (player is ServerPlayerEntity) {
spawnPokemon(world, pos, state, player)
}
world.setBlockState(pos, if (state.fluidState.isOf(Fluids.WATER)) Blocks.WATER.defaultState else Blocks.AIR.defaultState)
val bEntity = world.getBlockEntity(pos) as? GildedChestBlockEntity
bEntity?.markRemoved()
} else super.onBreak(world, pos, state, player)
val resultState = if (state.fluidState.isOf(Fluids.WATER)) Blocks.WATER.defaultState else Blocks.AIR.defaultState
world.setBlockState(pos, resultState)
return resultState
}
return super.onBreak(world, pos, state, player)
}
private fun spawnPokemon(world: World, pos: BlockPos, state: BlockState, player: ServerPlayerEntity) : ActionResult {
@@ -201,11 +213,9 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
newState: BlockState,
moved: Boolean
) {
if (!state.isOf(newState.block) && !world.isClient) {
val chest = world.getBlockEntity(pos) as? GildedChestBlockEntity
chest?.let {
ItemScatterer.spawn(world, pos, chest.inventoryContents)
}
if (!state.isOf(newState.block)) {
val chest = world.getBlockEntity(pos) as GildedChestBlockEntity
ItemScatterer.spawn(world, pos, chest.inventoryContents)
}
}
@@ -238,7 +248,7 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
return state.rotate(mirror.getRotation(state.get(Properties.HORIZONTAL_FACING) as Direction))
}
enum class Type(val poserId: Identifier) {
enum class Type(val poserId: Identifier): StringIdentifiable {
RED(cobblemonResource("gilded_chest")),
BLUE(cobblemonResource("blue_gilded_chest")),
GREEN(cobblemonResource("green_gilded_chest")),
@@ -246,7 +256,15 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
WHITE(cobblemonResource("white_gilded_chest")),
BLACK(cobblemonResource("black_gilded_chest")),
YELLOW(cobblemonResource("yellow_gilded_chest")),
FAKE(cobblemonResource("gilded_chest"))
FAKE(cobblemonResource("gilded_chest"));
override fun asString(): String {
return name.lowercase()
}
companion object {
val CODEC = StringIdentifiable.createBasicCodec(::values)
}
}
@Deprecated("Deprecated in Java")
@@ -254,6 +272,10 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
return false
}
override fun getCodec(): MapCodec<out BlockWithEntity> {
return CODEC
}
override fun onPlaced(
world: World,
pos: BlockPos,
@@ -267,14 +289,4 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
}
}
override fun scheduledTick(
state: BlockState?,
world: ServerWorld?,
pos: BlockPos?,
random: Random?
) {
val blockEntity = world?.getBlockEntity(pos) as? GildedChestBlockEntity ?: return
blockEntity.onScheduledTick()
}
}
@@ -29,17 +29,11 @@ class FossilAnalyzerBlockEntity(
class FossilAnalyzerInventory(val analyzerEntity: FossilAnalyzerBlockEntity) : SidedInventory {
override fun clear() {
if(analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
val fossilMultiblockStructure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
fossilMultiblockStructure.fossilInventory.clear()
}
TODO("Not yet implemented")
}
override fun size(): Int {
if (analyzerEntity.multiblockStructure != null) {
return 3
}
return 0
TODO("Not yet implemented")
}
override fun isEmpty(): Boolean {
@@ -47,34 +41,19 @@ class FossilAnalyzerBlockEntity(
}
override fun getStack(slot: Int): ItemStack {
if (analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
val fossilMultiblockStructure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
if (fossilMultiblockStructure.fossilInventory.size > slot) {
return fossilMultiblockStructure.fossilInventory[slot]
}
}
return ItemStack.EMPTY
}
//Unimplemented since no extract
override fun removeStack(slot: Int, amount: Int): ItemStack {
return removeStack(slot)
return ItemStack.EMPTY
}
override fun removeStack(slot: Int): ItemStack {
// if(analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
// val fossilMultiblockStructure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
// if (fossilMultiblockStructure.fossilInventory.size > slot) {
// val itemStack = fossilMultiblockStructure.fossilInventory.removeAt(slot)
// markDirty()
// return itemStack
// }
// }
return ItemStack.EMPTY
}
override fun setStack(slot: Int, stack: ItemStack) {
if (analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
if (analyzerEntity.multiblockStructure != null) {
val struct = analyzerEntity.multiblockStructure as FossilMultiblockStructure
analyzerEntity.world?.let {
struct.insertFossil(stack, it)
@@ -93,10 +72,7 @@ class FossilAnalyzerBlockEntity(
}
override fun getAvailableSlots(side: Direction?): IntArray {
if(analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
return intArrayOf(0,1,2)
}
return IntArray(0)
return IntArray(1)
}
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
@@ -111,8 +87,8 @@ class FossilAnalyzerBlockEntity(
}
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
// return analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure && dir == Direction.DOWN
return false
}
}
}
@@ -20,7 +20,6 @@ import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.inventory.DoubleInventory
import net.minecraft.inventory.Inventories
import net.minecraft.inventory.Inventory
import net.minecraft.inventory.SidedInventory
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NbtCompound
@@ -59,7 +58,9 @@ class GildedChestBlockEntity(pos: BlockPos, state: BlockState, val type: Type =
override fun isPlayerViewing(player: PlayerEntity): Boolean {
if (player.currentScreenHandler is GenericContainerScreenHandler) {
val inventory = (player.currentScreenHandler as GenericContainerScreenHandler).inventory
return inventory === this@GildedChestBlockEntity
return inventory === this@GildedChestBlockEntity || inventory is DoubleInventory && inventory.isPart(
this@GildedChestBlockEntity
)
}
return false
}
@@ -72,6 +73,8 @@ class GildedChestBlockEntity(pos: BlockPos, state: BlockState, val type: Type =
override fun createScreenHandler(syncId: Int, playerInventory: PlayerInventory?) =
GenericContainerScreenHandler.createGeneric9x3(syncId, playerInventory, this)
override fun method_11282() = inventoryContents
override fun onOpen(player: PlayerEntity) {
if (!this.removed && !player.isSpectator && type != Type.FAKE) {
stateManager.openContainer(player, this.getWorld(), this.getPos(), this.cachedState)
@@ -85,7 +88,7 @@ class GildedChestBlockEntity(pos: BlockPos, state: BlockState, val type: Type =
}
override fun getAvailableSlots(side: Direction): IntArray {
return if (type == Type.FAKE) IntArray(0) else IntArray(NUM_SLOTS) { it }
return if (type == Type.FAKE) IntArray(0) else IntArray(NUM_SLOTS)
}
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
@@ -98,7 +101,7 @@ class GildedChestBlockEntity(pos: BlockPos, state: BlockState, val type: Type =
return dir == Direction.DOWN
}
override fun getInvStackList(): DefaultedList<ItemStack> = inventoryContents
override fun canPlayerUse(player: PlayerEntity) = !player.isSpectator
override fun setInvStackList(list: DefaultedList<ItemStack>) {
inventoryContents = list
@@ -147,7 +150,7 @@ class GildedChestBlockEntity(pos: BlockPos, state: BlockState, val type: Type =
}
override fun writeNbt(nbt: NbtCompound?) {
super.writeNbt(nbt)
if (!serializeLootTable(nbt)) {
if (!writeLootTable(nbt)) {
Inventories.writeNbt(nbt, inventoryContents)
}
}
@@ -157,14 +160,8 @@ class GildedChestBlockEntity(pos: BlockPos, state: BlockState, val type: Type =
inventoryContents= DefaultedList.ofSize(
size(), ItemStack.EMPTY
)
if (!deserializeLootTable(nbt)) {
if (!readLootTable(nbt)) {
Inventories.readNbt(nbt, inventoryContents)
}
}
fun onScheduledTick() {
if (!this.removed) {
stateManager.updateViewerCount(this.getWorld(), this.getPos(), this.cachedState)
}
}
}
@@ -36,8 +36,8 @@ import net.minecraft.util.math.BlockPos
@Suppress("MemberVisibilityCanBePrivate", "unused")
class HealingMachineBlockEntity(
blockPos: BlockPos,
blockState: BlockState
val blockPos: BlockPos,
val blockState: BlockState
) : BlockEntity(CobblemonBlockEntities.HEALING_MACHINE, blockPos, blockState) {
var currentUser: UUID? = null
private set
@@ -114,7 +114,7 @@ class HealingMachineBlockEntity(
this.setUser(player.uuid)
alreadyHealing.add(player.uuid)
updateBlockChargeLevel(HealingMachineBlock.MAX_CHARGE_LEVEL + 1)
if (world != null && !world!!.isClient) world!!.playSoundServer(position = pos.toVec3d(), sound = CobblemonSounds.HEALING_MACHINE_ACTIVE, volume = 1F, pitch = 1F)
if (world != null && !world!!.isClient) world!!.playSoundServer(position = blockPos.toVec3d(), sound = CobblemonSounds.HEALING_MACHINE_ACTIVE, volume = 1F, pitch = 1F)
}
fun completeHealing() {
@@ -217,10 +217,10 @@ class HealingMachineBlockEntity(
else floor((healingCharge / maxCharge) * HealingMachineBlock.MAX_CHARGE_LEVEL).toInt()
).coerceIn(0..HealingMachineBlock.MAX_CHARGE_LEVEL + 1)
val state = world!!.getBlockState(pos)
val state = world!!.getBlockState(blockPos)
if (state != null && state.block is HealingMachineBlock) {
val currentCharge = state.get(HealingMachineBlock.CHARGE_LEVEL).toInt()
if (chargeLevel != currentCharge) world!!.setBlockState(pos, state.with(HealingMachineBlock.CHARGE_LEVEL, chargeLevel))
if (chargeLevel != currentCharge) world!!.setBlockState(blockPos, state.with(HealingMachineBlock.CHARGE_LEVEL, chargeLevel))
}
}
}
@@ -26,24 +26,24 @@ import net.minecraft.util.math.Box
import net.minecraft.world.World
class PCBlockEntity(
blockPos: BlockPos,
blockState: BlockState
val blockPos: BlockPos,
val blockState: BlockState
) : BlockEntity(CobblemonBlockEntities.PC, blockPos, blockState) {
companion object {
internal val TICKER = BlockEntityTicker<PCBlockEntity> { world, _, _, blockEntity ->
if (world.isClient) return@BlockEntityTicker
blockEntity.togglePCOn(blockEntity.getInRangeViewerCount(world, blockEntity.pos) > 0)
blockEntity.togglePCOn(blockEntity.getInRangeViewerCount(world, blockEntity.blockPos) > 0)
}
}
private fun togglePCOn(on: Boolean) {
val pcBlock = cachedState.block as PCBlock
val pcBlock = blockState.block as PCBlock
if (world != null && !world!!.isClient) {
val world = world!!
val posBottom = pcBlock.getBasePosition(cachedState, pos)
val posBottom = pcBlock.getBasePosition(blockState, blockPos)
val stateBottom = world.getBlockState(posBottom)
val posTop = pcBlock.getPositionOfOtherPart(stateBottom, posBottom)
@@ -71,7 +71,7 @@ class PCBlockEntity(
val pcLink = PCLinkManager.getLink(player.uuid)
return pcLink != null
&& pcLink is ProximityPCLink
&& pcLink.pos == pos
&& pcLink.pos == blockPos
&& pcLink.world!!.dimension == player.world.dimension
}
@@ -48,7 +48,7 @@ import net.minecraft.world.World
import java.util.*
import kotlin.math.ceil
class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.PASTURE, pos, state) {
class PokemonPastureBlockEntity(pos: BlockPos, val state: BlockState) : BlockEntity(CobblemonBlockEntities.PASTURE, pos, state) {
open class Tethering(
val minRoamPos: BlockPos,
val maxRoamPos: BlockPos,
@@ -60,7 +60,7 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(
val entityId: Int
) {
fun getPokemon() = Cobblemon.storage.getPC(pcId)[pokemonId]
val box = Box(minRoamPos, maxRoamPos)
val box = Box(minRoamPos.toVec3d(), maxRoamPos.toVec3d())
open fun canRoamTo(pos: BlockPos) = box.contains(pos.toCenterPos())
fun toDTO(player: ServerPlayerEntity): OpenPasturePacket.PasturePokemonDataDTO? {
@@ -129,7 +129,7 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(
val world = world ?: return false
val entity = PokemonEntity(world, pokemon = pokemon)
entity.calculateDimensions()
val width = entity.boundingBox.xLength
val width = entity.boundingBox.lengthX
val idealPlace = pos.add(directionToBehind.vector.multiply(ceil(width).toInt() + 1))
var box = entity.getDimensions(EntityPose.STANDING).getBoxAt(idealPlace.toCenterPos().subtract(0.0, 0.5, 0.0))
@@ -173,11 +173,11 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(
}
private fun togglePastureOn(on: Boolean) {
val pastureBlock = cachedState.block as PastureBlock
val pastureBlock = state.block as PastureBlock
if (world != null && !world!!.isClient) {
val world = world!!
val posBottom = pastureBlock.getBasePosition(cachedState, pos)
val posBottom = pastureBlock.getBasePosition(state, pos)
val stateBottom = world.getBlockState(posBottom)
val posTop = pastureBlock.getPositionOfOtherPart(stateBottom, posBottom)
@@ -12,16 +12,13 @@ import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.api.fossil.NaturalMaterials
import com.cobblemon.mod.common.api.multiblock.builder.MultiblockStructureBuilder
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockStructure
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.inventory.Inventories
import net.minecraft.inventory.SidedInventory
import net.minecraft.inventory.SimpleInventory
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtList
import net.minecraft.registry.Registries
import net.minecraft.util.collection.DefaultedList
import net.minecraft.util.math.BlockPos
@@ -30,71 +27,88 @@ import net.minecraft.util.math.Direction
class RestorationTankBlockEntity(
pos: BlockPos, state: BlockState,
multiblockBuilder: MultiblockStructureBuilder
) : FossilMultiblockEntity(pos, state, multiblockBuilder, CobblemonBlockEntities.RESTORATION_TANK) {
) : FossilMultiblockEntity(pos, state, multiblockBuilder, CobblemonBlockEntities.RESTORATION_TANK) {
val inv = RestorationTankInventory(this)
override fun writeNbt(nbt: NbtCompound) {
super.writeNbt(nbt)
nbt.put("inventory", inv.toNbtList())
Inventories.writeNbt(nbt, inv.items)
}
override fun readNbt(nbt: NbtCompound) {
super.readNbt(nbt)
if (nbt.contains("Items")) {
// Old format
val items = DefaultedList.ofSize(inv.size(), ItemStack.EMPTY)
Inventories.readNbt(nbt, items)
RestorationTankInventory(this, *items.toTypedArray())
} else if (nbt.contains("inventory")) {
// New format
inv.readNbtList(nbt.get("inventory") as NbtList)
}
Inventories.readNbt(nbt, inv.items)
}
class RestorationTankInventory(val tankEntity: RestorationTankBlockEntity, vararg items: ItemStack) : SimpleInventory(*items), SidedInventory {
class RestorationTankInventory(val tankEntity: RestorationTankBlockEntity) : SidedInventory {
constructor(tankEntity: RestorationTankBlockEntity) : this(tankEntity, *Array(8) { ItemStack.EMPTY })
val items: DefaultedList<ItemStack> = DefaultedList.ofSize(8, ItemStack.EMPTY)
override fun clear() {
for (i in items.indices) {
items[i] = ItemStack.EMPTY
}
}
override fun markDirty() {
super.markDirty()
for(i in 0 until size()) { // iterate over inventory, consume items, generate and place return items
val itemStack : ItemStack = this.getStack(i)
if(!itemStack.isEmpty) {
val struct = tankEntity.multiblockStructure as? FossilMultiblockStructure
val returnIdentifier = NaturalMaterials.getReturnItem(itemStack)
override fun size(): Int {
return 8
}
if(tankEntity.world != null) {
if(struct?.insertOrganicMaterial(itemStack, tankEntity.world!!) == true) {
removeStack(i)
if (returnIdentifier != null) {
val returnItem = Registries.ITEM.get(returnIdentifier)
val returnStack = ItemStack(returnItem, itemStack.count)
var done = false
if (returnStack.maxCount > 1) { // Only search for a merge if merging could be possible
for(j in 0 until size()) {
// Look for a inventory slot to merge with
val existingStack : ItemStack = getStack(j)
if (Registries.ITEM.getId(existingStack.item) == returnIdentifier && existingStack.count + returnStack.count < existingStack.maxCount) {
setStack(j, ItemStack(returnItem, existingStack.count + returnStack.count))
done = true
}
}
}
if (!done) {
setStack(i, ItemStack(returnItem, itemStack.count))
}
}
}
}
override fun isEmpty(): Boolean {
return items.all { it == ItemStack.EMPTY }
}
override fun getStack(slot: Int): ItemStack {
if(slot == -1 || slot >= size()) {
return ItemStack.EMPTY
}
return items[slot]
}
override fun removeStack(slot: Int, amount: Int): ItemStack {
if(slot < 0 || slot >= size()) {
return ItemStack.EMPTY
}
val result = items[slot].split(amount)
if(items[slot].count == 0) {
items[slot] = ItemStack.EMPTY
}
return result
}
override fun removeStack(slot: Int): ItemStack {
return removeStack(slot, 1)
}
override fun setStack(slot: Int, stack: ItemStack) {
val struct = tankEntity.multiblockStructure as? FossilMultiblockStructure
tankEntity.world?.let {
struct?.insertOrganicMaterial(stack, it)
val returnIdentifier = NaturalMaterials.getReturnItem((stack))
if(returnIdentifier != null ) {
// Store the return item
val returnItem = Registries.ITEM.get(returnIdentifier)
storeReturnItem(returnItem, stack.count)
}
}
}
private fun storeReturnItem(returnItem: Item, count: Int) {
val destStack = items.withIndex().firstOrNull {
it.value == ItemStack.EMPTY || (it.value.count < it.value.maxCount && it.value.item == returnItem)
}
if (destStack != null) {
if(destStack.value == ItemStack.EMPTY) {
items[destStack.index] = ItemStack(returnItem, 1)
} else {
destStack.value.increment(count)
}
}
}
override fun markDirty() {
tankEntity.world?.let {
// tankEntity.world!!.updateListeners(tankEntity.pos, tankEntity.cachedState, tankEntity.cachedState, Block.NOTIFY_ALL)
// tankEntity.world!!.updateComparators(tankEntity.pos, tankEntity.world!!.getBlockState(tankEntity.pos).block)
tankEntity.markDirty()
tankEntity.multiblockStructure?.markDirty(it)
}
}
@@ -104,7 +118,7 @@ class RestorationTankBlockEntity(
}
override fun getAvailableSlots(side: Direction?): IntArray {
return intArrayOf(0,1,2,3,4,5,6,7)
return if(side == Direction.DOWN) intArrayOf(0,1,2,3,4,5,6,7) else intArrayOf(-1)
}
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
@@ -115,26 +129,12 @@ class RestorationTankBlockEntity(
&& structure.organicMaterialInside < FossilMultiblockStructure.MATERIAL_TO_START
&& structure.createdPokemon == null
val returnItem = NaturalMaterials.getReturnItem(stack!!) ?: return canUtilize
val returnStack = ItemStack(Registries.ITEM.get(returnItem), stack.count)
if(canUtilize && super.canInsert(returnStack)) {
// See if there's room for the return
if(returnStack == ItemStack.EMPTY) {
return true
}
var emptyCount = 0
for(i in 0 until size()) {
val existingStack : ItemStack = getStack(i)
if(existingStack == ItemStack.EMPTY) {
emptyCount += 1
if(emptyCount > 1) {
// 2 empty slots, safe to insert
return true
}
} else if(returnStack.maxCount > 1) {
if(Registries.ITEM.getId(existingStack.item) == returnItem && existingStack.count + returnStack.count < existingStack.maxCount) {
// Return item can merge with an item already in the inventory
return true
}
if(canUtilize) {
// See if there's room
for (i in items.indices ) {
if(items[i] == ItemStack.EMPTY || (items[i].count < items[i].maxCount
&& items[i].item == Registries.ITEM.get(returnItem))) {
return true
}
}
return false
@@ -9,18 +9,12 @@
package com.cobblemon.mod.common.block.grower
import com.cobblemon.mod.common.api.apricorn.Apricorn
import com.cobblemon.mod.common.world.feature.CobblemonConfiguredFeatures
import net.minecraft.block.sapling.SaplingGenerator
import net.minecraft.util.math.random.Random
import net.minecraft.block.SaplingGenerator
import java.util.Optional
class ApricornTreeGrower(private val apricorn: Apricorn) : SaplingGenerator() {
override fun getTreeFeature(random: Random, bl: Boolean) = when (this.apricorn) {
Apricorn.BLACK -> CobblemonConfiguredFeatures.BLACK_APRICORN_TREE_KEY
Apricorn.BLUE -> CobblemonConfiguredFeatures.BLUE_APRICORN_TREE_KEY
Apricorn.GREEN -> CobblemonConfiguredFeatures.GREEN_APRICORN_TREE_KEY
Apricorn.PINK -> CobblemonConfiguredFeatures.PINK_APRICORN_TREE_KEY
Apricorn.RED -> CobblemonConfiguredFeatures.RED_APRICORN_TREE_KEY
Apricorn.WHITE -> CobblemonConfiguredFeatures.WHITE_APRICORN_TREE_KEY
Apricorn.YELLOW -> CobblemonConfiguredFeatures.YELLOW_APRICORN_TREE_KEY
}
}
class ApricornTreeGrower(apricorn: Apricorn) : SaplingGenerator(
apricorn.asString(),
Optional.empty(),
Optional.of(apricorn.configuredFeature()),
Optional.empty()
)
@@ -161,9 +161,9 @@ class FossilMultiblockBuilder(val centerPos: BlockPos) : MultiblockStructureBuil
val RESTORATION_TANK_PRED = BlockPredicate.Builder.create()
.nbt(NBT_TO_CHECK)
.blocks(CobblemonBlocks.RESTORATION_TANK)
.state(StatePredicate.Builder.create().exactMatch(RestorationTankBlock.PART, RestorationTankBlock.TankPart.BOTTOM).build())
.state(StatePredicate.Builder.create().exactMatch(RestorationTankBlock.PART, RestorationTankBlock.TankPart.BOTTOM))
.build()
//lol thanks mojang for not allowing nbt puts to be chained
}
}
}
@@ -188,7 +188,7 @@ class FossilMultiblockStructure (
// Check if the player is holding a natural material and if so, feed it to the machine.
if (NaturalMaterials.isNaturalMaterial(stack)) {
if (!this.isRunning() && this.createdPokemon == null && this.organicMaterialInside < MATERIAL_TO_START && insertOrganicMaterial(ItemStack(stack.item, 1), world)) {
if (!this.isRunning() && this.createdPokemon == null && this.organicMaterialInside < MATERIAL_TO_START && insertOrganicMaterial(stack, world)) {
this.lastInteraction = world.time
if (!player.isCreative) {
val returnItem = NaturalMaterials.getReturnItem(stack)
@@ -205,10 +205,10 @@ class FossilMultiblockStructure (
return ActionResult.PASS
}
public fun spawn(world: World, pos: BlockPos, directionToBehind: Direction, pokemon: Pokemon) : Boolean {
fun spawn(world: World, pos: BlockPos, directionToBehind: Direction, pokemon: Pokemon) : Boolean {
val entity = PokemonEntity(world, pokemon = pokemon)
entity.calculateDimensions()
val width = entity.boundingBox.xLength
val width = entity.boundingBox.lengthX
val idealPlace = pos.add(directionToBehind.vector.multiply(ceil(width / 2.0).toInt() + 1))
var box = entity.getDimensions(EntityPose.STANDING).getBoxAt(idealPlace.toCenterPos().subtract(0.0, 0.5, 0.0))
@@ -331,9 +331,11 @@ class FossilMultiblockStructure (
}
}
if(tankBaseEntity is RestorationTankBlockEntity) {
tankBaseEntity.inv.clearToList().forEach {
ItemScatterer.spawn(world, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), it)
tankBaseEntity.inv.items.forEach {
val stack = ItemStack(it.item, 1)
ItemScatterer.spawn(world, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), stack)
}
tankBaseEntity.inv.clear()
}
@@ -480,19 +482,15 @@ class FossilMultiblockStructure (
}
fun updateProgress(world: World) {
val screenID = if (protectionTime > 0F) {
val screenID = if(protectionTime > 0F ) {
MonitorBlock.MonitorScreen.GREEN_PROGRESS_9
} else if (timeRemaining <= 0) {
MonitorBlock.MonitorScreen.OFF
} else {
getProgressScreen((TIME_TO_TAKE - timeRemaining) / TIME_PER_STAGE)
}
val monitorState = world.getBlockState(monitorPos)
if (monitorState.contains(MonitorBlock.SCREEN)) {
world.setBlockState(monitorPos, monitorState.with(MonitorBlock.SCREEN, screenID))
}
world.setBlockState(monitorPos, monitorState.with(MonitorBlock.SCREEN, screenID))
}
fun getProgressScreen(progress:Int) : MonitorBlock.MonitorScreen {
@@ -535,13 +533,12 @@ class FossilMultiblockStructure (
//Returns false if material wasnt inserted
fun insertOrganicMaterial(stack: ItemStack, world: World): Boolean {
var natureValue = NaturalMaterials.getContent(stack)
val natureValue = NaturalMaterials.getContent(stack)
if (timeRemaining > 0 || this.organicMaterialInside >= MATERIAL_TO_START || natureValue == null) {
return false
}
natureValue *= stack.count
if (natureValue <= 0 && organicMaterialInside == 0) return false
if (natureValue < 0 && organicMaterialInside == 0) return false
val oldFillStage = organicMaterialInside * 8 / MATERIAL_TO_START
// to prevent over/under filling the tank causing a crash
@@ -15,7 +15,7 @@ import net.minecraft.block.WoodType
import net.minecraft.block.entity.BlockEntity
import net.minecraft.util.math.BlockPos
class CobblemonHangingSignBlock(settings: Settings, woodType: WoodType) : HangingSignBlock(settings, woodType) {
class CobblemonHangingSignBlock(settings: Settings, woodType: WoodType) : HangingSignBlock(woodType, settings) {
override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = CobblemonHangingSignBlockEntity(pos, state)

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