Compare commits

..
32 Commits
Author SHA1 Message Date
Tyzillion a077e74bf2 When you wish upon a star
Makes no difference who you are
Anything your heart desires
Will come to you
If your heart is in your dream
No request is too extreme
When you wish upon a star
As dreamers do
Fate is kind
She brings to those who love
The sweet fulfillment of
Their secret longing
Like a bolt out of the blue
Fate steps in and sees you through
When you wish upon a star
Your dreams come true
2024-02-18 14:25:47 -05:00
Lily 763f1de793 forgot to push this mb 2024-02-16 22:34:31 -05:00
Lily 62a95b93c5 begin implementing falling stars 2024-02-16 14:48:20 -05:00
Lily 7991f70c09 Merge remote-tracking branch 'origin/dynamax' into dynamax 2024-02-16 14:48:07 -05:00
Lily 70d393a899 begin implementing falling stars 2024-02-16 14:47:58 -05:00
Lily 5bace46aa6 begin implementing falling stars 2024-02-13 21:51:28 -05:00
Lily c5fb411636 Merge branch 'sega_bass_pro_fishing_2' into dynamax
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonEntities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	fabric/src/main/kotlin/com/cobblemon/mod/fabric/client/CobblemonFabricClient.kt
#	forge/src/main/kotlin/com/cobblemon/mod/forge/client/CobblemonForgeClient.kt
2024-02-13 20:59:18 -05:00
Lily 9a8159ee30 start on dynamax 2024-02-13 20:07:29 -05:00
Plastered_Crab acc6f119e1 - Magikarps getting reeled in now land in front of the player instead of behind the player (for battle reasons) 2024-02-13 16:45:22 -08:00
Plastered_Crab 932a2c375c - Begin different reel-in trajectories for fishing up Magikarps 2024-02-13 14:46:42 -08:00
Nick 142eebe9e6 Apply model predicates, starting with Pokerod 2024-02-09 22:07:43 -08:00
jeffw773 36f19c9180 - Xp is rewarded to player for fishing up Pokemon 2024-02-09 21:15:10 -08:00
jeffw773 beb8fed6bd - Pokeball Bobber now renders correctly when cast out with Pokerod
- Luck of the Sea and Lure get read correctly from the rod and factor into the spawn bucket chosen
2024-02-09 14:17:13 -08:00
jeffw773 142284a17c - Pokemon rarity is altered by Luck of the Sea 2024-02-08 21:18:41 -08:00
jeffw773 f1ea86362a - Fixed fishing spawns being able to check if sky is actually visible.
- added isOcean to biomes to Magikarp
2024-02-07 18:28:42 -08:00
Paul ec3361ad91 Added basic fishing spawn detail for Magikarp and use the world pool for fishing because ehhh 2024-02-03 12:16:53 +10:00
Paul ca7c36ce15 Added trigger spawning with a fishing implementation 2024-02-03 12:13:38 +10:00
jeffw773 f7c73728d7 - Added some newer spawning stuff and added battle initiation after fished up mon 2024-02-02 16:38:15 -08:00
jeffw773 1992a23edc Merge branch 'main' into sega_bass_pro_fishing_2 2024-02-02 13:39:02 -08:00
jeffw773 dad28135cc - Brief changes to particle count based on rarity reeled in 2024-01-18 14:32:43 -08:00
jeffw773 e41d13dd53 - Pokerod now uses config values for rarity buckets and access biome spawn pools 2024-01-14 23:02:33 -08:00
jeffw773 64cae53d82 - Pokerod foundation laid out for having chance to spawn Pokemon instead of getting item as well as rarity chance started 2024-01-14 00:11:12 -08:00
dhi_holo c9bf0bd8d7 Merge branch 'sega_bass_pro_fishing_2' of https://gitlab.com/cable-mc/cobblemon into sega_bass_pro_fishing_2 2024-01-14 13:36:53 +10:00
dhi_holo 97d2047aee It's a Tool, It's a Rod
makes pokerod be held correctly, and adds second model file for the cast variation, when that is implemented.
2024-01-14 13:36:04 +10:00
jeffw773 811f8abed7 - Added override for model of pokerod when cast 2024-01-13 19:27:22 -08:00
jeffw773 ab02a54f28 - Finally the Pokerod is starting to work! (it is backwards tho) 2024-01-13 19:08:36 -08:00
jeffw773 6e0c1aefcc - More testing fishing code for pokerod 2024-01-13 13:55:02 -08:00
jeffw773 e887b6c43c - Some code for fishing testing again 2024-01-13 12:16:38 -08:00
jeffw773 25172c2bc9 - Some code for fishing testing 2024-01-13 11:23:38 -08:00
jeffw773 929b90173a - Some code for fishing testing 2024-01-12 11:58:02 -08:00
jeffw773 70a65f6ffe - Testing code 2024-01-12 07:39:21 -08:00
jeffw773 db7d3f01d8 - Starting implementation on fishing 2024-01-12 07:39:20 -08:00
5256 changed files with 37903 additions and 383045 deletions
+1 -4
View File
@@ -33,7 +33,4 @@ common/src/main/resources/assets/cobblemon/showdown
# Generated files
*/generated/resources/*
/fabric/src/generated/resources/cobblemon-common.accesswidener
/.ignored/
# MacOS
.DS_Store
/.ignored/
+4 -270
View File
@@ -1,9 +1,8 @@
# Changelog
## [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 22 fossil structures, where you can brush Suspicious Sand/Gravel for Fossils.
- Added over (number here) ruin structures, where you can find Pokémon-themed Armor Trims and Pottery Sherds, Tumblestones, and more.
- Added (number here) 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.
@@ -15,240 +14,15 @@
- Added the hidden "True Vivillionaire" advancement.
- 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.
- 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 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.
- 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
### Pokémon Added
#### Gen 2
- Sentret
- Furret
- Qwilfish
- Heracross
- Skarmory
- Larvitar
- Pupitar
- Tyranitar
#### Gen 3
- Lileep
- Cradily
- Anorith
- Armaldo
- Tropius
- Roselia
- Aron
- Lairon
- Aggron
- Solrock
- Lunatone
- Makuhita
- Hariyama
- Trapinch
- Vibrava
- Flygon
#### Gen 4
- Shieldon
- Bastiodon
- Cranidos
- Rampardos
- Budew
- Roserade
- Hippopotas
- Hippowdon
#### Gen 5
- Tirtouga
- Carracosta
- Archen
- Archeops
- Zorua
- Zoroark
- Petilil
- Lilligant
- Darumaka
- Darmanitan
- Woobat
- Swoobat
- Sandile
- Krokorok
- Krookodile
- Frillish
- Jellicent
- Cubchoo
- Beartic
- Deino
- Zweilous
- Hydreigon
- Larvesta
- Volcarona
- Alomomola
- Ferroseed
- Ferrothorn
#### Gen 6
- Tyrunt
- Tyrantrum
- Amaura
- Aurorus
- Goomy
- Sliggoo
- Goodra
- Carbink
- Flabébé
- Floette
- Florges
- Klefki
#### Gen 7
- Turtonator
- Fomantis
- Lurantis
- Salandit
- Salazzle
- Jangmo-o
- Hakamo-o
- Kommo-o
- Alolan Diglett
- Alolan Dugtrio
#### Gen 8
- Gossifleur
- Eldegoss
- Arctozolt
- Arctovish
- Dracozolt
- Dracovish
- Basculegion
- Hisuian Decidueye
- Hisuian Typhlosion
- Hisuian Samurott
- Hisuian Lilligant
- Hisuian Sliggoo
- Hisuian Goodra
- Hisuian Zorua
- Hisuian Zoroark
- Hisuian Voltorb
- Hisuian Electrode
- Hisuian Qwilfish
- Overqwil
- Hisuian Sneasel
- Sneasler
- Stonjourner
- Cufant
- Copperajah
- Dreepy
- Drakloak
- Dragapult
- Impidimp
- Morgrem
- Grimmsnarl
#### 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. Who knows what might happen if it fills up?
- Gholdengo
#### Cobblemon Exclusives
- Hisui Bias Rowlet
- Hisui Bias Dartrix
- Hisui Bias Cyndaquil
- Hisui Bias Quilava
- Hisui Bias Oshawott
- Hisui Bias Dewott
- Hisui Bias Goomy
- Hisui Bias Petilil
### Added cries to the following Pokémon
- Alolan Raticate
- Vulpix, Ninetales
- Zubat, Golbat, Crobat
- Meowth, Persian
- Psyduck, Golduck
- Growlithe, Arcanine
- Geodude, Graveler, Golem
- Doduo, Dodrio
- Seel, Dewgong
- Muk, Grimer
- Krabby, Kingler
- Voltorb, Electrode, Hisuian Voltorb, Hisuian Electrode
- Tangela, Tangrowth
- Snorlax
- Cubone, Marowak
- Koffing, Weezing
- Aerodactyl
- Elekid, Electabuzz, Electivire
- Omanyte, Omastar
- Kabuto, Kabutops
- Lapras
- Scyther
- Hisui Bias Cyndaquil, Hisui Bias Quilava
- Igglybuff
- Yanma, Yanmega
- Gligar, Gliscor
- Qwilfish, Hisuian Qwilfish, Overqwil
- Sneasel, Hisuian Sneasel, Weavile, Sneasler
- Larvitar, Pupitar, Tyranitar
- Swinub, Piloswine, Mamoswine
- Aron, Lairon, Aggron
- Trapinch, Vibrava, Flygon
- Cacnea, Cacturne
- Barboach, Whiscash
- Lileep, Cradily
- Anorith, Armaldo
- Tropius
- Chimecho
- Bidoof, Bibarel
- Buizel, Floatzel
- Gible, Gabite, Garchomp
- Hisui Bias Oshawott, Hisui Bias Dewott
- Petilil, Lilligant, Hisui Bias Petilil, Hisuian Lilligant
- Basculin, Basculegion
- Sandile, Krokolok, Krookodile
- Darumaka, Darmanitan
- Archen, Archeops
- Zorua, Zoroark, Hisuian Zorua, Hisuian Zoroark
- Tyrunt, Tyrantrum
- Amaura Aurorus
- Carbink
- Goomy, Sliggoo, Goodra, Hisui Bias Goomy, Hisuian Sliggoo, Hisuian Goodra
- Klefki
- Hisui Bias Rowlet, Hisui Bias Dartrix
- Komala
- Impidimp, Morgrem, Grimmsnarl
- Cufant, Copperajah
- Dreepy, Drakloak, Dragapult
- Dracozolt, Arctozolt, Dracovish, Arctovish
- Lechonk, Oinkologne
- Maushold family of three and four
### Changes
- 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, 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.
- 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.
@@ -258,27 +32,11 @@
- 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 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 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.
- 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.
### Fixes
- 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 Exeggcute not being able to evolve.
- Fixed battle lang for Poison Touch Ability
- Fixed Cubone not being able to evolve into Alolan Marowak.
- Fixed Squawkabilly forms not being recognized.
@@ -288,19 +46,6 @@
- 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 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 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 properties in PokemonEntity work. This could break some plugins (not in a big way, but changes will be needed).
@@ -312,15 +57,6 @@
- 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.
- ``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 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.
### Datapack & Resourcepack Creators
- The maximum amount of fossils that can fit in the Fossil Analyzer can be adjusted in the config.
@@ -355,8 +91,6 @@
}
```
- Following up on this change ``light_source`` was removed as a possible shoulder effect due to becoming unnecessary and never having had a default implementation.
- Added the item tag ``cobblemon:ability_changers``, contains the ``cobblemon:ability_capsule`` & ``cobblemon:ability_patch`` by default.
- Added the item tag ``cobblemon:held/is_friendship_booster``, allows items to give the Soothe Bell effect of a boost of 1.5x Friendship gained, contains ``cobblemon:soothe_bell`` by default.
## [1.4.1 (December 23rd, 2023)](#1-4-1)
@@ -1292,4 +1026,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.
-3
View File
@@ -15,7 +15,4 @@ dependencies {
implementation(libs.shadow)
implementation(libs.loom)
implementation(libs.architecturyPlugin)
implementation(libs.blossom)
implementation(libs.ideaExt)
}
@@ -1,6 +1,3 @@
import extensions.VersionType
import extensions.writeVersion
plugins {
java
`java-library`
@@ -34,7 +31,7 @@ publishing {
groupId = "com.cobblemon"
artifactId = project.findProperty("maven.artifactId")?.toString() ?: project.name
version = project.writeVersion(VersionType.PUBLISHING)
version = rootProject.version.toString()
}
}
}
@@ -1,24 +0,0 @@
package extensions
import org.gradle.api.Project
fun Project.version(): String {
return rootProject.property("mod_version").toString()
}
fun Project.isSnapshot(): Boolean {
return rootProject.property("snapshot") == "true"
}
fun Project.writeVersion(type: VersionType = VersionType.FULL): String {
val version = "${rootProject.property("mod_version")}+${rootProject.property("mc_version")}"
return when (type) {
VersionType.PUBLISHING -> if(this.isSnapshot()) "$version-SNAPSHOT" else version
VersionType.FULL -> if(this.isSnapshot()) rootProject.version.toString() else version
}
}
enum class VersionType {
PUBLISHING,
FULL
}
+2 -2
View File
@@ -9,5 +9,5 @@ version = "${project.property("mod_version")}+${project.property("mc_version")}"
val isSnapshot = project.property("snapshot")?.equals("true") ?: false
if (isSnapshot) {
version = "$version-${versioning.info.branch}-${versioning.info.build}"
}
version = "$version-SNAPSHOT-${versioning.info.branch}-${versioning.info.build}"
}
+5 -43
View File
@@ -1,9 +1,3 @@
import extensions.isSnapshot
import extensions.version
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
/*
*
* * Copyright (C) 2023 Cobblemon Contributors
@@ -17,10 +11,6 @@ import java.time.format.DateTimeFormatter
plugins {
id("cobblemon.base-conventions")
id("cobblemon.publish-conventions")
id("net.kyori.blossom")
id("org.jetbrains.gradle.plugin.idea-ext")
id ("net.nemerosa.versioning") version "2.8.2"
}
architectury {
@@ -39,8 +29,10 @@ dependencies {
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)
// modCompileOnly(libs.flywheelFabric)
modApi(libs.molang)
compileOnlyApi(libs.jeiApi)
modCompileOnly(libs.adornFabric)
modCompileOnly(libs.lambDynamicLights) { isTransitive = false }
// For Showdown
@@ -52,7 +44,8 @@ dependencies {
modCompileOnly(libs.mongoDriverCore)
modCompileOnly(libs.mongoDriverSync)
testImplementation(libs.fabricJunit)
testRuntimeOnly(libs.junitEngine)
testImplementation(libs.junitApi)
testImplementation(libs.junitParams)
testImplementation(libs.mockito)
testImplementation(libs.mockk)
@@ -66,34 +59,3 @@ tasks.withType<Test> {
setExceptionFormat("full")
}
}
sourceSets {
main {
blossom {
kotlinSources {
fun generateLicenseHeader() : String {
val builder = StringBuilder()
builder.append("/*\n")
rootProject.file("HEADER").forEachLine {
if(it.isEmpty()) {
builder.append(" *").append("\n")
} else {
builder.append(" * ").append(it).append("\n")
}
}
return builder.append(" */").append("\n").toString()
}
property("license", generateLicenseHeader())
property("modid", "cobblemon")
property("version", project.version())
property("isSnapshot", if(rootProject.isSnapshot()) "true" else "false")
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")
}
}
}
}
@@ -1,35 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.CobblemonBuildDetails;
import net.minecraft.util.crash.CrashReport;
import net.minecraft.util.crash.CrashReportSection;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(CrashReport.class)
public final class CrashReportMixin {
@Inject(method = "addStackTrace", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/SystemDetails;writeTo(Ljava/lang/StringBuilder;)V"))
public void cobblemon$printCobblemonDetails(StringBuilder builder, CallbackInfo callback) {
CrashReportSection cobblemon = new CrashReportSection("Cobblemon");
cobblemon.add("Version", CobblemonBuildDetails.VERSION);
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,7 +11,6 @@ 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;
@@ -25,7 +24,7 @@ import java.util.Map;
@Mixin(EntityRenderDispatcher.class)
public class EntityRenderDispatcherMixin {
@Shadow private Map<SkinTextures.Model, EntityRenderer<? extends PlayerEntity>> modelRenderers;
@Shadow private Map<String, EntityRenderer<? extends PlayerEntity>> modelRenderers;
@Inject(
method = "reload",
@@ -34,4 +33,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 eventDeltaVerticalWheel;
private double eventDeltaWheel;
@Inject(
method = "onMouseScroll",
at = @At(
value = "FIELD",
target="Lnet/minecraft/client/Mouse;eventDeltaVerticalWheel:D",
target="Lnet/minecraft/client/Mouse;eventDeltaWheel: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)eventDeltaVerticalWheel;
int i = (int)eventDeltaWheel;
if (i > 0) {
while (i-- > 0) CobblemonClient.INSTANCE.getStorage().shiftSelected(false);
ci.cancel();
eventDeltaVerticalWheel = 0;
eventDeltaWheel = 0;
PartySendBinding.INSTANCE.actioned();
} else if (i < 0) {
while (i++ < 0) CobblemonClient.INSTANCE.getStorage().shiftSelected(true);
ci.cancel();
eventDeltaVerticalWheel = 0;
eventDeltaWheel = 0;
PartySendBinding.INSTANCE.actioned();
}
}
}
}
}
@@ -1,33 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.CobblemonItems;
import net.minecraft.entity.mob.PiglinBrain;
import net.minecraft.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(PiglinBrain.class)
public class PiglinBarterMixin {
@Inject(method = "acceptsForBarter", at = @At(value = "RETURN"), cancellable = true)
private static void cobblemon$acceptsForBarter(ItemStack stack, CallbackInfoReturnable<Boolean> ci) {
if(!ci.getReturnValue()) {
//TODO: Make a tag?
if(stack.isOf(CobblemonItems.RELIC_COIN_POUCH))
ci.setReturnValue(true);
}
}
}
@@ -1,31 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.CobblemonItems;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.mob.PiglinEntity;
import net.minecraft.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(PiglinEntity.class)
public abstract class PiglinEntityMixin {
@Inject(method = "equipToOffHand", at = @At(value = "HEAD"), cancellable = true)
public void cobblemon$isValidBarteringItem(ItemStack stack, CallbackInfo ci) {
PiglinEntity entity = (PiglinEntity)(Object) this;
if (stack.isOf(CobblemonItems.RELIC_COIN_POUCH)) {
entity.equipStack(EquipmentSlot.OFFHAND, stack);
entity.updateDropChances(EquipmentSlot.OFFHAND);
ci.cancel();
}
}
}
@@ -15,8 +15,15 @@ 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;
@@ -26,6 +33,9 @@ import java.util.*;
@Mixin(StructurePoolBasedGenerator.StructurePoolGenerator.class)
public abstract class StructurePoolGeneratorMixin {
@Final
@Mutable
@Shadow Deque<StructurePoolBasedGenerator.ShapedPoolStructurePiece> structurePieces;
Map<String, Integer> generatedStructureGroupCounts;
@@ -93,7 +103,7 @@ public abstract class StructurePoolGeneratorMixin {
generatedStructureGroupCounts = new HashMap<>();
}
@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)
@ModifyVariable(method = "generatePiece", at = @At("STORE"), ordinal = 1)
private Iterator<StructurePoolElement> reduceStructurePoolElementIterator(Iterator<StructurePoolElement> iterator) {
List<StructurePoolElement> reducedList = new ArrayList<>();
@@ -113,7 +123,7 @@ public abstract class StructurePoolGeneratorMixin {
return reducedList.iterator();
}
@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)
@ModifyVariable(method = "generatePiece", at = @At("STORE"), ordinal = 1)
private PoolStructurePiece injected(PoolStructurePiece poolStructurePiece) {
Identifier structureLocationKey = getCobblemonOnlyLocation(poolStructurePiece.getPoolElement());
if (structureLocationKey != null) {
@@ -123,42 +133,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);
@@ -204,4 +214,4 @@ public abstract class StructurePoolGeneratorMixin {
//
// return reducedList;
// }
}
}
@@ -9,6 +9,7 @@
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;
@@ -18,23 +19,23 @@ import org.spongepowered.asm.mixin.gen.Invoker;
public interface BlocksInvoker {
@Invoker("createLogBlock")
static Block createLogBlock(MapColor topMapColor, MapColor sideMapColor) {
static PillarBlock createLogBlock(MapColor topMapColor, MapColor sideMapColor) {
throw new UnsupportedOperationException();
}
@Invoker("createLeavesBlock")
static Block createLeavesBlock(BlockSoundGroup soundGroup) {
static LeavesBlock createLeavesBlock(BlockSoundGroup soundGroup) {
throw new UnsupportedOperationException();
}
@Invoker("createWoodenButtonBlock")
static Block createWoodenButtonBlock(BlockSetType blockSetType) {
static ButtonBlock createWoodenButtonBlock(BlockSetType blockSetType, FeatureFlag... requiredFeatures) {
throw new UnsupportedOperationException();
}
@Invoker("createFlowerPotBlock")
static Block createFlowerPotBlock(Block flower) {
static FlowerPotBlock createFlowerPotBlock(Block flower, FeatureFlag... requiredFeatures) {
throw new UnsupportedOperationException();
}
}
}
@@ -11,13 +11,14 @@ 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(BlockSetType blockSetType, AbstractBlock.Settings settings) {
static DoorBlock cobblemon$create(AbstractBlock.Settings settings, BlockSetType blockSetType) {
throw new UnsupportedOperationException();
}
}
}
@@ -11,13 +11,14 @@ 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(BlockSetType type, AbstractBlock.Settings settings) {
static PressurePlateBlock cobblemon$create(PressurePlateBlock.ActivationRule type, AbstractBlock.Settings settings, BlockSetType blockSetType) {
throw new UnsupportedOperationException();
}
}
}
@@ -11,13 +11,14 @@ 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(BlockSetType blockSetType, AbstractBlock.Settings settings) {
static TrapdoorBlock cobblemon$create(AbstractBlock.Settings settings, BlockSetType blockSetType) {
throw new UnsupportedOperationException();
}
}
}
@@ -1,18 +0,0 @@
{{ license }}
package com.cobblemon.mod.common
object CobblemonBuildDetails {
const val MOD_ID = "{{ modid }}"
const val VERSION = "{{ version }}"
const val SNAPSHOT = {{ isSnapshot }}
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)
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.CobblemonBuildDetails.smallCommitHash
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.EvolvePokemonContext
import com.cobblemon.mod.common.api.Priority
@@ -17,7 +16,6 @@ import com.cobblemon.mod.common.api.data.DataProvider
import com.cobblemon.mod.common.api.drop.CommandDropEntry
import com.cobblemon.mod.common.api.drop.DropEntry
import com.cobblemon.mod.common.api.drop.ItemDropEntry
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.CobblemonEvents.BATTLE_VICTORY
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
import com.cobblemon.mod.common.api.events.CobblemonEvents.EVOLUTION_COMPLETE
@@ -38,7 +36,6 @@ import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroups
import com.cobblemon.mod.common.api.pokemon.experience.StandardExperienceCalculator
import com.cobblemon.mod.common.api.pokemon.feature.ChoiceSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemProvider
import com.cobblemon.mod.common.api.pokemon.stats.EvCalculator
@@ -49,8 +46,13 @@ import com.cobblemon.mod.common.api.scheduling.ServerRealTimeTaskTracker
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.CobblemonSpawningProspector
import com.cobblemon.mod.common.api.spawning.WorldSlice
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
import com.cobblemon.mod.common.api.spawning.context.AreaSpawningContext
import com.cobblemon.mod.common.api.spawning.context.calculators.AreaSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.AreaSpawningInput
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.api.starter.StarterHandler
import com.cobblemon.mod.common.api.storage.PokemonStoreManager
import com.cobblemon.mod.common.api.storage.adapter.conversions.ReforgedConversion
@@ -66,7 +68,6 @@ import com.cobblemon.mod.common.api.storage.player.PlayerDataStoreManager
import com.cobblemon.mod.common.api.storage.player.factory.JsonPlayerDataStoreFactory
import com.cobblemon.mod.common.api.storage.player.factory.MongoPlayerDataStoreFactory
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.battles.BattleRegistry
@@ -105,13 +106,7 @@ 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
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifDedicatedServer
import com.cobblemon.mod.common.util.isLaterVersion
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.removeAmountIf
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
@@ -119,6 +114,15 @@ import com.mongodb.ConnectionString
import com.mongodb.MongoClientSettings
import com.mongodb.client.MongoClient
import com.mongodb.client.MongoClients
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.UUID
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
import net.minecraft.client.MinecraftClient
import net.minecraft.command.argument.serialize.ConstantArgumentSerializer
import net.minecraft.entity.data.TrackedDataHandlerRegistry
@@ -126,25 +130,15 @@ import net.minecraft.item.Items
import net.minecraft.item.NameTagItem
import net.minecraft.registry.RegistryKey
import net.minecraft.util.WorldSavePath
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.*
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
import kotlin.math.roundToInt
object Cobblemon {
const val MODID = CobblemonBuildDetails.MOD_ID
const val VERSION = CobblemonBuildDetails.VERSION
const val MODID = "cobblemon"
const val VERSION = "1.4.0"
const val CONFIG_PATH = "config/$MODID/main.json"
val LOGGER: Logger = LogManager.getLogger()
val LOGGER = LogManager.getLogger()
lateinit var implementation: CobblemonImplementation
@Deprecated("This field is now a config value", ReplaceWith("Cobblemon.config.captureCalculator"))
@@ -174,14 +168,6 @@ object Cobblemon {
fun preInitialize(implementation: CobblemonImplementation) {
this.implementation = implementation
this.LOGGER.info("Launching Cobblemon ${CobblemonBuildDetails.VERSION}${if(CobblemonBuildDetails.SNAPSHOT) "-SNAPSHOT" else ""} ")
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()
implementation.registerSoundEvents()
implementation.registerBlocks()
@@ -244,9 +230,6 @@ object Cobblemon {
}
}
// Register the grow_tumblestone advancement
PlatformEvents.RIGHT_CLICK_BLOCK.subscribe { AdvancementHandler.onTumbleStonePlaced(it) }
PlatformEvents.CHANGE_DIMENSION.subscribe {
it.player.party().forEach { pokemon -> pokemon.entity?.recallWithAnimation() }
}
@@ -256,16 +239,6 @@ object Cobblemon {
TrackedDataHandlerRegistry.register(PoseTypeDataSerializer)
TrackedDataHandlerRegistry.register(IdentifierDataSerializer)
// Lowest priority because this applies after luxury ball bonus as of gen 4
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(Priority.LOWEST) { event ->
var increment = (event.newFriendship - event.pokemon.friendship).toFloat()
// Our Luxury ball spec is diff from official, but we will still assume these stack
if (event.pokemon.heldItemNoCopy().isIn(CobblemonItemTags.IS_FRIENDSHIP_BOOSTER)) {
increment += increment * 0.5F
}
event.newFriendship = event.pokemon.friendship + increment.roundToInt()
}
HeldItemProvider.register(CobblemonHeldItemManager, Priority.LOWEST)
}
@@ -280,7 +253,6 @@ object Cobblemon {
SpeciesFeatures.types["choice"] = ChoiceSpeciesFeatureProvider::class.java
SpeciesFeatures.types["flag"] = FlagSpeciesFeatureProvider::class.java
SpeciesFeatures.types["integer"] = IntSpeciesFeatureProvider::class.java
SpeciesFeatures.register(
DataKeys.CAN_BE_MILKED,
@@ -17,7 +17,6 @@ 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.cobblemon.mod.common.block.entity.*
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
import com.cobblemon.mod.common.platform.PlatformRegistry
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.registry.Registries
@@ -46,18 +45,6 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
@JvmField
val HANGING_SIGN: BlockEntityType<CobblemonHangingSignBlockEntity> = this.create("hanging_sign", BlockEntityType.Builder.create(::CobblemonHangingSignBlockEntity, CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN).build(null))
@JvmField
val GILDED_CHEST: BlockEntityType<GildedChestBlockEntity> = this.create("chest", BlockEntityType.Builder.create(::GildedChestBlockEntity,
CobblemonBlocks.GILDED_CHEST,
CobblemonBlocks.BLUE_GILDED_CHEST,
CobblemonBlocks.YELLOW_GILDED_CHEST,
CobblemonBlocks.PINK_GILDED_CHEST,
CobblemonBlocks.BLACK_GILDED_CHEST,
CobblemonBlocks.WHITE_GILDED_CHEST,
CobblemonBlocks.GREEN_GILDED_CHEST,
CobblemonBlocks.GIMMIGHOUL_CHEST
).build(null))
@JvmField
val FOSSIL_MULTIBLOCK: BlockEntityType<FossilMultiblockEntity> = this.create("fossil_multiblock",
BlockEntityType.Builder.create({ pos, state -> FossilMultiblockEntity(pos, state, FossilMultiblockBuilder(pos)) },
@@ -80,7 +67,8 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
)
@JvmField
val DISPLAY_CASE: BlockEntityType<DisplayCaseBlockEntity> = this.create("display_case",
BlockEntityType.Builder.create(::DisplayCaseBlockEntity, CobblemonBlocks.DISPLAY_CASE).build(null)
val DYNITE_ORE: BlockEntityType<DyniteOreBlockEntity> = this.create("dynite_ore",
BlockEntityType.Builder.create(::DyniteOreBlockEntity, CobblemonBlocks.DYNITE_ORE)
.build(null)
)
}
@@ -22,7 +22,6 @@ import com.cobblemon.mod.common.mixin.invoker.*
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.chest.GildedChestBlock
import com.cobblemon.mod.common.block.sign.CobblemonHangingSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonWallHangingSignBlock
@@ -72,8 +71,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
@JvmField
val SUN_STONE_ORE = evolutionStoneOre("sun_stone_ore")
@JvmField
val TERRACOTTA_SUN_STONE_ORE = evolutionStoneOre("terracotta_sun_stone_ore")
@JvmField
val THUNDER_STONE_ORE = evolutionStoneOre("thunder_stone_ore")
@JvmField
val WATER_STONE_ORE = evolutionStoneOre("water_stone_ore")
@@ -99,6 +96,15 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
@JvmField
val DEEPSLATE_WATER_STONE_ORE = this.deepslateEvolutionStoneOre("deepslate_water_stone_ore")
@JvmField
val DYNITE_ORE = this.create("dynite_ore", DyniteOreBlock(
AbstractBlock.Settings.create()
.mapColor(MapColor.BRIGHT_RED)
.strength(4.5f, 3f)
.requiresTool()
.sounds(BlockSoundGroup.METAL)
))
// Apricorns
@JvmField
val APRICORN_LOG = log("apricorn_log", arg2 = MapColor.BROWN)
@@ -115,11 +121,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(APRICORN_WOOD_TYPE, AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD)))
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))
@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`(APRICORN_BLOCK_SET_TYPE, AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).noCollision().strength(0.5f).sounds(BlockSoundGroup.WOOD)))
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))
@JvmField
val APRICORN_SIGN = this.create("apricorn_sign", CobblemonSignBlock(AbstractBlock.Settings.copy(Blocks.OAK_SIGN), APRICORN_WOOD_TYPE))
@JvmField
@@ -133,9 +139,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`(APRICORN_BLOCK_SET_TYPE, AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).instrument(Instrument.BASS).strength(3.0F).sounds(BlockSoundGroup.WOOD).nonOpaque()))
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))
@JvmField
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 }))
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))
private val PLANT_PROPERTIES = AbstractBlock.Settings.create().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.GRASS).pistonBehavior(PistonBehavior.DESTROY)
@JvmField
@@ -154,13 +160,13 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
val YELLOW_APRICORN_SAPLING = this.create("yellow_apricorn_sapling", ApricornSaplingBlock(PLANT_PROPERTIES, Apricorn.YELLOW))
@JvmField
val MEDICINAL_LEEK = this.create("medicinal_leek", MedicinalLeekBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DULL_RED).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MEDICINAL_LEEK_SOUNDS)))
val MEDICINAL_LEEK = this.create("medicinal_leek", MedicinalLeekBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DULL_RED).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
@JvmField
val ENERGY_ROOT = this.create("energy_root", EnergyRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DIRT_BROWN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.ENERGY_ROOT_SOUNDS)))
val ENERGY_ROOT = this.create("energy_root", EnergyRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DIRT_BROWN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.ROOTS)))
@JvmField
val BIG_ROOT = this.create("big_root", BigRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.BIG_ROOT_SOUNDS)))
val BIG_ROOT = this.create("big_root", BigRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.ROOTS)))
@JvmField
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).mapColor(MapColor.DARK_GREEN).burnable().noCollision().breakInstantly().sounds(CobblemonSounds.REVIVAL_HERB_SOUNDS)))
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).mapColor(MapColor.DARK_GREEN).burnable().noCollision().breakInstantly().sounds(BlockSoundGroup.CROP)))
@JvmField
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
@@ -194,7 +200,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
@@ -202,7 +207,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
@@ -210,7 +214,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@@ -229,99 +232,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
@JvmField
val YELLOW_APRICORN = apricornBlock("yellow_apricorn", Apricorn.YELLOW)
@JvmField
val RELIC_COIN_POUCH = create(
"relic_coin_pouch",
CoinPouchBlock(
AbstractBlock.Settings.create()
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
.pistonBehavior(PistonBehavior.DESTROY)
.strength(0.4f)
.nonOpaque(), true
)
)
@JvmField
val RELIC_COIN_SACK = create(
"relic_coin_sack",
CoinPouchBlock(
AbstractBlock.Settings.create()
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
.pistonBehavior(PistonBehavior.DESTROY)
.strength(0.4f), false
))
@JvmField
val GILDED_CHEST = create(
"gilded_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.RED
)
)
@JvmField
val BLUE_GILDED_CHEST = create(
"blue_gilded_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.BLUE
)
)
@JvmField
val BLACK_GILDED_CHEST = create(
"black_gilded_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.BLACK
)
)
@JvmField
val YELLOW_GILDED_CHEST = create(
"yellow_gilded_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.YELLOW
)
)
@JvmField
val WHITE_GILDED_CHEST = create(
"white_gilded_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.WHITE
)
)
@JvmField
val GREEN_GILDED_CHEST = create(
"green_gilded_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.GREEN
)
)
@JvmField
val PINK_GILDED_CHEST = create(
"pink_gilded_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.PINK
)
)
@JvmField
val GIMMIGHOUL_CHEST = create(
"gimmighoul_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.FAKE
)
)
@JvmField
val MONITOR = create(
"monitor",
@@ -354,7 +264,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
RestorationTankBlock(
AbstractBlock.Settings.create()
.mapColor(MapColor.IRON_GRAY)
.sounds(BlockSoundGroup.GLASS)
.sounds(BlockSoundGroup.METAL)
.pistonBehavior(PistonBehavior.BLOCK)
.requiresTool()
.strength(5.0F, 6.0F)
@@ -389,29 +299,17 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
)
@JvmField
val DISPLAY_CASE = create(
"display_case",
DisplayCaseBlock(
AbstractBlock.Settings.create()
.sounds(CobblemonSounds.DISPLAY_CASE_SOUNDS)
.nonOpaque()
.pistonBehavior(PistonBehavior.BLOCK)
.mapColor(MapColor.STONE_GRAY)
.strength(0.3f)
)
)
val RED_MINT = create("red_mint", MintBlock(MintType.RED, AbstractBlock.Settings.create().mapColor(MapColor.RED).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
val RED_MINT = create("red_mint", MintBlock(MintType.RED, AbstractBlock.Settings.create().mapColor(MapColor.RED).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
@JvmField
val BLUE_MINT = create("blue_mint", MintBlock(MintType.BLUE, AbstractBlock.Settings.create().mapColor(MapColor.BLUE).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
val BLUE_MINT = create("blue_mint", MintBlock(MintType.BLUE, AbstractBlock.Settings.create().mapColor(MapColor.BLUE).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
@JvmField
val CYAN_MINT = create("cyan_mint", MintBlock(MintType.CYAN, AbstractBlock.Settings.create().mapColor(MapColor.CYAN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
val CYAN_MINT = create("cyan_mint", MintBlock(MintType.CYAN, AbstractBlock.Settings.create().mapColor(MapColor.CYAN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
@JvmField
val PINK_MINT = create("pink_mint", MintBlock(MintType.PINK, AbstractBlock.Settings.create().mapColor(MapColor.PINK).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
val PINK_MINT = create("pink_mint", MintBlock(MintType.PINK, AbstractBlock.Settings.create().mapColor(MapColor.PINK).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
@JvmField
val GREEN_MINT = create("green_mint", MintBlock(MintType.GREEN, AbstractBlock.Settings.create().mapColor(MapColor.GREEN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
val GREEN_MINT = create("green_mint", MintBlock(MintType.GREEN, AbstractBlock.Settings.create().mapColor(MapColor.GREEN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
@JvmField
val WHITE_MINT = create("white_mint", MintBlock(MintType.WHITE, AbstractBlock.Settings.create().mapColor(MapColor.WHITE).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
val WHITE_MINT = create("white_mint", MintBlock(MintType.WHITE, AbstractBlock.Settings.create().mapColor(MapColor.WHITE).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
@JvmField
val PASTURE = create(
@@ -428,7 +326,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
)
@JvmField
val VIVICHOKE_SEEDS = this.create("vivichoke_seeds", VivichokeBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.VIVICHOKE_SOUNDS)))
val VIVICHOKE_SEEDS = this.create("vivichoke_seeds", VivichokeBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
@JvmField
val PEP_UP_FLOWER = this.create("pep_up_flower", FlowerBlock(StatusEffects.LEVITATION, 10, AbstractBlock.Settings.create().mapColor(MapColor.DARK_GREEN).noCollision().breakInstantly().sounds(BlockSoundGroup.GRASS).offset(AbstractBlock.OffsetType.XZ).pistonBehavior(PistonBehavior.DESTROY)))
@JvmField
@@ -552,7 +450,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
private fun berryBlock(name: String): BerryBlock {
val identifier = cobblemonResource("${name}_berry")
val block = this.create(identifier.path, BerryBlock(identifier, AbstractBlock.Settings.copy(Blocks.WHEAT).dynamicBounds().sounds(CobblemonSounds.BERRY_BUSH_SOUNDS).strength(0.2F)))
val block = this.create(identifier.path, BerryBlock(identifier, AbstractBlock.Settings.copy(Blocks.WHEAT).dynamicBounds().sounds(BlockSoundGroup.CROP).strength(0.2F)))
this.berries[identifier] = block
return block
}
@@ -560,7 +458,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): Block {
private fun log(name: String, arg: MapColor = MapColor.DIRT_BROWN, arg2: MapColor = MapColor.DIRT_BROWN): PillarBlock {
val block = BlocksInvoker.createLogBlock(arg, arg2)
return this.create(name, block)
}
@@ -580,16 +478,16 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
return block
}
private fun evolutionStoneOre(name: String) = this.create(name, ExperienceDroppingBlock(UniformIntProvider.create(1, 2), AbstractBlock.Settings.copy(Blocks.IRON_ORE)))
private fun evolutionStoneOre(name: String) = this.create(name, ExperienceDroppingBlock(AbstractBlock.Settings.copy(Blocks.IRON_ORE), UniformIntProvider.create(1, 2)))
private fun deepslateEvolutionStoneOre(name: String) = this.create(name, ExperienceDroppingBlock(UniformIntProvider.create(1, 2), AbstractBlock.Settings.copy(Blocks.DEEPSLATE_IRON_ORE)))
private fun deepslateEvolutionStoneOre(name: String) = this.create(name, ExperienceDroppingBlock(AbstractBlock.Settings.copy(Blocks.DEEPSLATE_IRON_ORE), UniformIntProvider.create(1, 2)))
/**
* Helper method for creating leaves
* copied over from Vanilla
*/
private fun leaves(name: String): Block {
private fun leaves(name: String): LeavesBlock {
val block = BlocksInvoker.createLeavesBlock(BlockSoundGroup.GRASS)
return this.create(name, block)
}
}
}
@@ -48,9 +48,5 @@ object CobblemonCommands {
PokemonRestartCommand.register(dispatcher)
BedrockParticleCommand.register(dispatcher)
OpenDialogueCommand.register(dispatcher)
// Possibly lock down registration if and only if under dev environment or running in an environment
// with a certain system environment variable set
CobblemonInfoCommand.register(dispatcher)
}
}
@@ -11,7 +11,9 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.entity.boat.CobblemonBoatEntity
import com.cobblemon.mod.common.entity.boat.CobblemonChestBoatEntity
import com.cobblemon.mod.common.entity.fallingstar.FallingStarEntity
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.platform.PlatformRegistry
@@ -75,6 +77,24 @@ object CobblemonEntities : PlatformRegistry<Registry<EntityType<*>>, RegistryKey
.build(GENERIC_BEDROCK_ENTITY_KEY.toString())
)
@JvmField
val FALLING_STAR_ENTITY_KEY = cobblemonResource("falling_star")
@JvmField
val FALLING_STAR_ENTITY: EntityType<FallingStarEntity> = this.create(
FALLING_STAR_ENTITY_KEY.path,
EntityType.Builder.create({ _, world -> FallingStarEntity(world) }, SpawnGroup.MISC)
.build(FALLING_STAR_ENTITY_KEY.toString())
)
@JvmField
val POKE_BOBBER_KEY = cobblemonResource("poke_bobber")
@JvmField
val POKE_BOBBER: EntityType<PokeRodFishingBobberEntity> = this.create(
POKE_BOBBER_KEY.path,
EntityType.Builder.create(::PokeRodFishingBobberEntity, SpawnGroup.MISC).setDimensions(0.5F, 0.5F).maxTrackingRange(10)
.build(POKE_BOBBER_KEY.toString())
)
fun registerAttributes(consumer: (EntityType<out LivingEntity>, DefaultAttributeContainer.Builder) -> Unit) {
consumer(POKEMON, PokemonEntity.createAttributes())
}
@@ -146,7 +146,7 @@ interface CobblemonImplementation {
* @param criteria
* @return
*/
fun <T : Criterion<*>> registerCriteria(id: String, criteria: T): T
fun <T : Criterion<*>> registerCriteria(criteria: T): T
/**
* TODO
@@ -240,4 +240,4 @@ enum class Environment {
CLIENT,
SERVER
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.item.ability.AbilityChanger
import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.Natures
@@ -31,7 +30,6 @@ import com.cobblemon.mod.common.item.berry.StatusCuringBerryItem
import com.cobblemon.mod.common.item.berry.VolatileCuringBerryItem
import com.cobblemon.mod.common.item.interactive.*
import com.cobblemon.mod.common.item.interactive.PotionItem
import com.cobblemon.mod.common.item.interactive.ability.AbilityChangeItem
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
@@ -41,6 +39,14 @@ import net.minecraft.entity.LivingEntity
import net.minecraft.entity.effect.StatusEffectInstance
import net.minecraft.entity.effect.StatusEffects
import net.minecraft.item.*
import net.minecraft.item.BlockItem
import net.minecraft.item.FoodComponent
import net.minecraft.item.HangingSignItem
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.item.SignItem
import net.minecraft.item.SmithingTemplateItem
import net.minecraft.item.StewItem
import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.registry.RegistryKey
@@ -225,22 +231,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val APRICORN_SIGN = this.create("apricorn_sign", SignItem(Item.Settings().maxCount(16), CobblemonBlocks.APRICORN_SIGN, CobblemonBlocks.APRICORN_WALL_SIGN))
@JvmField
val APRICORN_HANGING_SIGN = this.create("apricorn_hanging_sign", HangingSignItem(CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN, Item.Settings().maxCount(16)))
@JvmField
val GILDED_CHEST = this.create("gilded_chest", BlockItem(CobblemonBlocks.GILDED_CHEST, Item.Settings()))
@JvmField
val BLUE_GILDED_CHEST = this.create("blue_gilded_chest", BlockItem(CobblemonBlocks.BLUE_GILDED_CHEST, Item.Settings()))
@JvmField
val YELLOW_GILDED_CHEST = this.create("yellow_gilded_chest", BlockItem(CobblemonBlocks.YELLOW_GILDED_CHEST, Item.Settings()))
@JvmField
val PINK_GILDED_CHEST = this.create("pink_gilded_chest", BlockItem(CobblemonBlocks.PINK_GILDED_CHEST, Item.Settings()))
@JvmField
val BLACK_GILDED_CHEST = this.create("black_gilded_chest", BlockItem(CobblemonBlocks.BLACK_GILDED_CHEST, Item.Settings()))
@JvmField
val WHITE_GILDED_CHEST = this.create("white_gilded_chest", BlockItem(CobblemonBlocks.WHITE_GILDED_CHEST, Item.Settings()))
@JvmField
val GREEN_GILDED_CHEST = this.create("green_gilded_chest", BlockItem(CobblemonBlocks.GREEN_GILDED_CHEST, Item.Settings()))
@JvmField
val GIMMIGHOUL_CHEST = this.create("gimmighoul_chest", BlockItem(CobblemonBlocks.GIMMIGHOUL_CHEST, Item.Settings()))
@JvmField
val RESTORATION_TANK = blockItem("restoration_tank", CobblemonBlocks.RESTORATION_TANK)
@@ -254,8 +244,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val PC = blockItem("pc", CobblemonBlocks.PC)
@JvmField
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
@JvmField
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
// Evolution items
@JvmField val LINK_CABLE = create("link_cable", LinkCableItem())
@@ -283,8 +271,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField val RIBBON_SWEET = noSettingsItem("ribbon_sweet")
@JvmField val CHIPPED_POT = noSettingsItem("chipped_pot")
@JvmField val CRACKED_POT = noSettingsItem("cracked_pot")
@JvmField val MASTERPIECE_TEACUP = noSettingsItem("masterpiece_teacup")
@JvmField val UNREMARKABLE_TEACUP = noSettingsItem("unremarkable_teacup")
@JvmField val SWEET_APPLE = noSettingsItem("sweet_apple")
@JvmField val TART_APPLE = noSettingsItem("tart_apple")
@JvmField val GALARICA_CUFF = noSettingsItem("galarica_cuff")
@@ -396,19 +382,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val ZINC = create("zinc", VitaminItem(Stats.SPECIAL_DEFENCE))
@JvmField
val GENIUS_FEATHER = create("genius_feather", FeatherItem(Stats.SPECIAL_ATTACK))
@JvmField
val SWIFT_FEATHER = create("swift_feather", FeatherItem(Stats.SPEED))
@JvmField
val HEALTH_FEATHER = create("health_feather", FeatherItem(Stats.HP))
@JvmField
val RESIST_FEATHER = create("resist_feather", FeatherItem(Stats.DEFENCE))
@JvmField
val MUSCLE_FEATHER = create("muscle_feather", FeatherItem(Stats.ATTACK))
@JvmField
val CLEVER_FEATHER = create("clever_feather", FeatherItem(Stats.SPECIAL_DEFENCE))
@JvmField
val MEDICINAL_LEEK = heldItem("medicinal_leek", MedicinalLeekItem(CobblemonBlocks.MEDICINAL_LEEK, Item.Settings().food(FoodComponent.Builder().snack().hunger(1).saturationModifier(0.2f).build())), "leek")
val MEDICINAL_LEEK = create("medicinal_leek", MedicinalLeekItem(CobblemonBlocks.MEDICINAL_LEEK, Item.Settings().food(FoodComponent.Builder().snack().hunger(1).saturationModifier(0.2f).build())))
@JvmField
val ROASTED_LEEK = create("roasted_leek", Item(Item.Settings().food(FoodComponent.Builder().snack().hunger(3).saturationModifier(0.3f).build())))
@JvmField
@@ -558,11 +532,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField val ELIXIR = create("elixir", ElixirItem(max = false))
@JvmField val MAX_ELIXIR = create("max_elixir", ElixirItem(max = true))
@JvmField
val ABILITY_CAPSULE = this.create("ability_capsule", AbilityChangeItem(AbilityChanger.COMMON_ABILITY))
@JvmField
val ABILITY_PATCH = this.create("ability_patch", AbilityChangeItem(AbilityChanger.HIDDEN_ABILITY))
/**
* Evolution Ores and Stones
*/
@@ -583,8 +552,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val SUN_STONE_ORE = blockItem("sun_stone_ore", CobblemonBlocks.SUN_STONE_ORE)
@JvmField
val TERRACOTTA_SUN_STONE_ORE = blockItem("terracotta_sun_stone_ore", CobblemonBlocks.TERRACOTTA_SUN_STONE_ORE)
@JvmField
val THUNDER_STONE_ORE = blockItem("thunder_stone_ore", CobblemonBlocks.THUNDER_STONE_ORE)
@JvmField
val WATER_STONE_ORE = blockItem("water_stone_ore", CobblemonBlocks.WATER_STONE_ORE)
@@ -613,6 +580,9 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val NETHER_FIRE_STONE_ORE = blockItem("nether_fire_stone_ore", CobblemonBlocks.NETHER_FIRE_STONE_ORE)
@JvmField
val DYNITE_ORE = blockItem("dynite_ore", CobblemonBlocks.DYNITE_ORE)
@JvmField
val DAWN_STONE = noSettingsItem("dawn_stone")
@JvmField
@@ -636,28 +606,16 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
// Held Items
@JvmField
val ABILITY_SHIELD = heldItem("ability_shield")
@JvmField
val ABSORB_BULB = heldItem("absorb_bulb")
@JvmField
val AIR_BALLOON = heldItem("air_balloon")
@JvmField
val ASSAULT_VEST = heldItem("assault_vest")
@JvmField
val BIG_ROOT = blockItem("big_root", CobblemonBlocks.BIG_ROOT)
@JvmField
val BINDING_BAND = heldItem("binding_band")
@JvmField
val BLACK_BELT = heldItem("black_belt")
@JvmField
val BLACK_GLASSES = heldItem("black_glasses")
@JvmField
val BLACK_SLUDGE = heldItem("black_sludge")
@JvmField
val BLUNDER_POLICY = heldItem("blunder_policy")
@JvmField
val CELL_BATTERY = heldItem("cell_battery")
@JvmField
val CHARCOAL = heldItem("charcoal_stick", remappedName = "charcoal")
@JvmField
val CHOICE_BAND = heldItem("choice_band")
@@ -668,64 +626,26 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val CLEANSE_TAG = heldItem("cleanse_tag")
@JvmField
val CLEAR_AMULET = heldItem("clear_amulet")
@JvmField
val COVERT_CLOAK = heldItem("covert_cloak")
@JvmField
val DESTINY_KNOT = heldItem("destiny_knot")
@JvmField
val DRAGON_FANG = heldItem("dragon_fang")
@JvmField
val EJECT_BUTTON = heldItem("eject_button")
@JvmField
val EJECT_PACK = heldItem("eject_pack")
@JvmField
val EVERSTONE = heldItem("everstone")
@JvmField
val EVIOLITE = heldItem("eviolite")
@JvmField
val EXP_SHARE = heldItem("exp_share")
@JvmField
val EXPERT_BELT = heldItem("expert_belt")
@JvmField
val FAIRY_FEATHER = heldItem("fairy_feather")
@JvmField
val FLAME_ORB = heldItem("flame_orb")
@JvmField
val FLOAT_STONE = heldItem("float_stone")
@JvmField
val FOCUS_BAND = heldItem("focus_band")
@JvmField
val FOCUS_SASH = heldItem("focus_sash")
@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 LEFTOVERS = heldItem("leftovers")
@JvmField
val LIFE_ORB = heldItem("life_orb")
@JvmField
val LIGHT_BALL = heldItem("light_ball")
@JvmField
val LIGHT_CLAY = heldItem("light_clay")
@JvmField
val LOADED_DICE = heldItem("loaded_dice")
@JvmField
val LUCKY_EGG = heldItem("lucky_egg")
@JvmField
val LUMINOUS_MOSS = heldItem("luminous_moss")
@JvmField
val MAGNET = heldItem("magnet")
@JvmField
val METRONOME = heldItem("metronome")
@JvmField
val MIRACLE_SEED = heldItem("miracle_seed")
@JvmField
val MUSCLE_BAND = heldItem("muscle_band")
@@ -736,42 +656,14 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val POISON_BARB = heldItem("poison_barb")
@JvmField
val POWER_ANKLET = heldItem("power_anklet")
@JvmField
val POWER_BAND = heldItem("power_band")
@JvmField
val POWER_BELT = heldItem("power_belt")
@JvmField
val POWER_BRACER = heldItem("power_bracer")
@JvmField
val POWER_LENS = heldItem("power_lens")
@JvmField
val POWER_WEIGHT = heldItem("power_weight")
@JvmField
val PROTECTIVE_PADS = heldItem("protective_pads")
@JvmField
val PUNCHING_GLOVE = heldItem("punching_glove")
@JvmField
val QUICK_CLAW = heldItem("quick_claw")
@JvmField
val RED_CARD = heldItem("red_card")
@JvmField
val RING_TARGET = heldItem("ring_target")
@JvmField
val ROCKY_HELMET = heldItem("rocky_helmet")
@JvmField
val ROOM_SERVICE = heldItem("room_service")
@JvmField
val SAFETY_GOGGLES = heldItem("safety_goggles")
@JvmField
val SCOPE_LENS = heldItem("scope_lens")
@JvmField
val SHARP_BEAK = heldItem("sharp_beak")
@JvmField
val SHED_SHELL = heldItem("shed_shell")
@JvmField
val SHELL_BELL = heldItem("shell_bell")
@JvmField
val SILK_SCARF = heldItem("silk_scarf")
@JvmField
val SILVER_POWDER = heldItem("silver_powder")
@@ -780,30 +672,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val SPELL_TAG = heldItem("spell_tag")
@JvmField
val SMOKE_BALL = heldItem("smoke_ball")
@JvmField
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 TOXIC_ORB = heldItem("toxic_orb")
@JvmField
val TWISTED_SPOON = heldItem("twisted_spoon")
@JvmField
val UTILITY_UMBRELLA = heldItem("utility_umbrella")
@JvmField
val WEAKNESS_POLICY = heldItem("weakness_policy")
@JvmField
val WIDE_LENS = heldItem("wide_lens")
@JvmField
val WISE_GLASSES = heldItem("wise_glasses")
@JvmField
val ZOOM_LENS = heldItem("zoom_lens")
@JvmField
val MENTAL_HERB = compostableHeldItem("mental_herb", null, 1F)
@JvmField
val MIRROR_HERB = compostableHeldItem("mirror_herb", null, 1F)
@@ -818,13 +690,29 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val QUICK_POWDER = heldItem("quick_powder")
@JvmField
val DAMP_ROCK = heldItem("damp_rock")
val DESTINY_KNOT = heldItem("destiny_knot")
@JvmField
val HEAT_ROCK = heldItem("heat_rock")
val EVERSTONE = heldItem("everstone")
@JvmField
val SMOOTH_ROCK = heldItem("smooth_rock")
val POWER_ANKLET = heldItem("power_anklet")
@JvmField
val ICY_ROCK = heldItem("icy_rock")
val POWER_BAND = heldItem("power_band")
@JvmField
val POWER_BELT = heldItem("power_belt")
@JvmField
val POWER_BRACER = heldItem("power_bracer")
@JvmField
val POWER_LENS = heldItem("power_lens")
@JvmField
val POWER_WEIGHT = heldItem("power_weight")
@JvmField
val FLAME_ORB = heldItem("flame_orb")
@JvmField
val LIFE_ORB = heldItem("life_orb")
@JvmField
val TOXIC_ORB = heldItem("toxic_orb")
@JvmField
val SMOKE_BALL = heldItem("smoke_ball")
// Mulch
@JvmField
@@ -850,19 +738,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val ARMOR_FOSSIL = noSettingsItem("armor_fossil")
@JvmField
val FOSSILIZED_BIRD = noSettingsItem("fossilized_bird")
val BIRD_FOSSIL = noSettingsItem("bird_fossil")
@JvmField
val CLAW_FOSSIL = noSettingsItem("claw_fossil")
@JvmField
val COVER_FOSSIL = noSettingsItem("cover_fossil")
@JvmField
val FOSSILIZED_DINO = noSettingsItem("fossilized_dino")
val DINO_FOSSIL = noSettingsItem("dino_fossil")
@JvmField
val DOME_FOSSIL = noSettingsItem("dome_fossil")
@JvmField
val FOSSILIZED_DRAKE = noSettingsItem("fossilized_drake")
val DRAKE_FOSSIL = noSettingsItem("drake_fossil")
@JvmField
val FOSSILIZED_FISH = noSettingsItem("fossilized_fish")
val FISH_FOSSIL = noSettingsItem("fish_fossil")
@JvmField
val HELIX_FOSSIL = noSettingsItem("helix_fossil")
@JvmField
@@ -938,53 +826,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
SmithingTemplateItem.of(CobblemonArmorTrims.AUTOMATON)
)
@JvmField
val POKEROD = pokerodItem("pokerod")
// Misc
@JvmField
val POKEMON_MODEL = this.create("pokemon_model", PokemonItem())
@JvmField
val RELIC_COIN = noSettingsItem("relic_coin")
@JvmField
val RELIC_COIN_POUCH = blockItem("relic_coin_pouch", CobblemonBlocks.RELIC_COIN_POUCH)
@JvmField
val RELIC_COIN_SACK = blockItem("relic_coin_sack", CobblemonBlocks.RELIC_COIN_SACK)
// Type Gems
@JvmField
val NORMAL_GEM = noSettingsItem("normal_gem")
@JvmField
val FIRE_GEM = noSettingsItem("fire_gem")
@JvmField
val WATER_GEM = noSettingsItem("water_gem")
@JvmField
val GRASS_GEM = noSettingsItem("grass_gem")
@JvmField
val ELECTRIC_GEM = noSettingsItem("electric_gem")
@JvmField
val ICE_GEM = noSettingsItem("ice_gem")
@JvmField
val FIGHTING_GEM = noSettingsItem("fighting_gem")
@JvmField
val POISON_GEM = noSettingsItem("poison_gem")
@JvmField
val GROUND_GEM = noSettingsItem("ground_gem")
@JvmField
val FLYING_GEM = noSettingsItem("flying_gem")
@JvmField
val PSYCHIC_GEM = noSettingsItem("psychic_gem")
@JvmField
val BUG_GEM = noSettingsItem("bug_gem")
@JvmField
val ROCK_GEM = noSettingsItem("rock_gem")
@JvmField
val GHOST_GEM = noSettingsItem("ghost_gem")
@JvmField
val DRAGON_GEM = noSettingsItem("dragon_gem")
@JvmField
val DARK_GEM = noSettingsItem("dark_gem")
@JvmField
val STEEL_GEM = noSettingsItem("steel_gem")
@JvmField
val FAIRY_GEM = noSettingsItem("fairy_gem")
private fun blockItem(name: String, block: Block): BlockItem = this.create(name, BlockItem(block, Item.Settings()))
@@ -1003,6 +850,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
private fun candyItem(name: String, calculator: CandyItem.Calculator): CandyItem = this.create(name, CandyItem(calculator))
private fun pokerodItem(name: String): PokerodItem {
var settings = Item.Settings()
val item = create(name, PokerodItem(settings))
return item
}
private fun heldItem(name: String, remappedName: String? = null): CobblemonItem = create(
name,
CobblemonItem(Item.Settings()).also {
@@ -1011,16 +864,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
}
}
)
private fun heldItem(name: String, item: Item, remappedName: String? = null) = create(
name = name,
entry = item.also {
if (remappedName != null) {
CobblemonHeldItemManager.registerRemap(it, remappedName)
CobblemonHeldItemManager.registerRemap(Items.BONE, "thickclub")
CobblemonHeldItemManager.registerRemap(Items.SNOWBALL, "snowball")
}
}
)
private fun compostable(item: Item, increaseLevelChance: Float) = Cobblemon.implementation.registerCompostable(item, increaseLevelChance)
@@ -14,7 +14,25 @@ import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.client.net.PlayerInteractOptionsHandler
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
import com.cobblemon.mod.common.client.net.animation.PlayPoseableAnimationHandler
import com.cobblemon.mod.common.client.net.battle.*
import com.cobblemon.mod.common.client.net.battle.BattleApplyPassResponseHandler
import com.cobblemon.mod.common.client.net.battle.BattleCaptureEndHandler
import com.cobblemon.mod.common.client.net.battle.BattleCaptureShakeHandler
import com.cobblemon.mod.common.client.net.battle.BattleCaptureStartHandler
import com.cobblemon.mod.common.client.net.battle.BattleChallengeExpiredHandler
import com.cobblemon.mod.common.client.net.battle.BattleChallengeNotificationHandler
import com.cobblemon.mod.common.client.net.battle.BattleEndHandler
import com.cobblemon.mod.common.client.net.battle.BattleFaintHandler
import com.cobblemon.mod.common.client.net.battle.BattleHealthChangeHandler
import com.cobblemon.mod.common.client.net.battle.BattleInitializeHandler
import com.cobblemon.mod.common.client.net.battle.BattleMadeInvalidChoiceHandler
import com.cobblemon.mod.common.client.net.battle.BattleMakeChoiceHandler
import com.cobblemon.mod.common.client.net.battle.BattleMessageHandler
import com.cobblemon.mod.common.client.net.battle.BattleMusicHandler
import com.cobblemon.mod.common.client.net.battle.BattlePersistentStatusHandler
import com.cobblemon.mod.common.client.net.battle.BattleQueueRequestHandler
import com.cobblemon.mod.common.client.net.battle.BattleSetTeamPokemonHandler
import com.cobblemon.mod.common.client.net.battle.BattleSwitchPokemonHandler
import com.cobblemon.mod.common.client.net.battle.BattleUpdateTeamPokemonHandler
import com.cobblemon.mod.common.client.net.callback.move.OpenMoveCallbackHandler
import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandler
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
@@ -64,12 +82,9 @@ import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPa
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.GlobalSpeciesFeatureSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.PropertiesCompletionRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesFeatureAssignmentSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.StandardSpeciesFeatureSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
@@ -233,7 +248,6 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(PokemonStateUpdatePacket.ID, PokemonStateUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(TetheringUpdatePacket.ID, TetheringUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(TradeableUpdatePacket.ID, TradeableUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(SpeciesFeatureUpdatePacket.ID, SpeciesFeatureUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(OriginalTrainerUpdatePacket.ID, OriginalTrainerUpdatePacket::decode, PokemonUpdatePacketHandler())
// Evolution start
@@ -287,8 +301,6 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(BattleMadeInvalidChoicePacket.ID, BattleMadeInvalidChoicePacket::decode, BattleMadeInvalidChoiceHandler)
this.createClientBound(BattleMusicPacket.ID, BattleMusicPacket::decode, BattleMusicHandler)
this.createClientBound(BattleChallengeExpiredPacket.ID, BattleChallengeExpiredPacket::decode, BattleChallengeExpiredHandler)
this.createClientBound(BattleReplacePokemonPacket.ID, BattleReplacePokemonPacket::decode, BattleReplacePokemonHandler)
this.createClientBound(BattleTransformPokemonPacket.ID, BattleTransformPokemonPacket::decode, BattleTransformPokemonHandler)
@@ -302,9 +314,6 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler)
this.createClientBound(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(GlobalSpeciesFeatureSyncPacket.ID, GlobalSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(SpeciesFeatureAssignmentSyncPacket.ID, SpeciesFeatureAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(NaturalMaterialRegistrySyncPacket.ID, NaturalMaterialRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(FossilRegistrySyncPacket.ID, FossilRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
@@ -16,7 +16,6 @@ import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.sound.SoundEvent
import net.minecraft.sound.SoundEvents
object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Registry<SoundEvent>>, SoundEvent>() {
@@ -85,8 +84,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
val MEDICINE_PILLS_USE = this.create("medicine_pills.use")
@JvmField
val MEDICINE_SPRAY_USE = this.create("medicine_spray.use")
@JvmField
val MEDICINE_FEATHER_USE = this.create("medicine_feather.use")
@JvmField
val BERRY_HARVEST = this.create("berry.harvest")
@@ -99,36 +96,28 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
val MULCH_REMOVE = this.create("mulch.remove")
@JvmField
val FOSSIL_MACHINE_ACTIVATE = this.create("fossil_machine.activate")
val FOSSIL_MACHINE_ACTIVATE = this.create("fossilmachine.activate")
@JvmField
val FOSSIL_MACHINE_ACTIVE_LOOP = this.create("fossil_machine.active_loop")
val FOSSIL_MACHINE_ACTIVE_LOOP = this.create("fossilmachine.active_loop")
@JvmField
val FOSSIL_MACHINE_ASSEMBLE = this.create("fossil_machine.assemble")
val FOSSIL_MACHINE_ASSEMBLE = this.create("fossilmachine.assemble")
@JvmField
val FOSSIL_MACHINE_DNA_FULL = this.create("fossil_machine.dna_full")
val FOSSIL_MACHINE_DNA_FULL = this.create("fossilmachine.dna_full")
@JvmField
val FOSSIL_MACHINE_FINISHED = this.create("fossil_machine.finished")
val FOSSIL_MACHINE_FINISHED = this.create("fossilmachine.finished")
@JvmField
val FOSSIL_MACHINE_INSERT_DNA = this.create("fossil_machine.insert_dna")
val FOSSIL_MACHINE_INSERT_DNA = this.create("fossilmachine.insert_dna")
@JvmField
val FOSSIL_MACHINE_INSERT_DNA_SMALL = this.create("fossil_machine.insert_dna_small")
val FOSSIL_MACHINE_INSERT_DNA_SMALL = this.create("fossilmachine.insert_dna_small")
@JvmField
val FOSSIL_MACHINE_INSERT_FOSSIL = this.create("fossil_machine.insert_fossil")
val FOSSIL_MACHINE_INSERT_FOSSIL = this.create("fossilmachine.insert_fossil")
@JvmField
val FOSSIL_MACHINE_RETRIEVE_FOSSIL = this.create("fossil_machine.retrieve_fossil")
val FOSSIL_MACHINE_RETRIEVE_FOSSIL = this.create("fossilmachine.retrieve_fossil")
@JvmField
val FOSSIL_MACHINE_RETRIEVE_POKEMON = this.create("fossil_machine.retrieve_pokemon")
val FOSSIL_MACHINE_RETRIEVE_POKEMON = this.create("fossilmachine.retrieve_pokemon")
@JvmField
val FOSSIL_MACHINE_UNPROTECTED = this.create("fossil_machine.unprotected")
val FOSSIL_MACHINE_UNPROTECTED = this.create("fossilmachine.unprotected")
@JvmField
val COIN_POUCH_BREAK = this.create("coin_pouch.break")
@JvmField
val COIN_POUCH_HIT = this.create("coin_pouch.hit")
@JvmField
val COIN_POUCH_STEP = this.create("coin_pouch.step")
@JvmField
val COIN_POUCH_PLACE = this.create("coin_pouch.place")
@JvmField
val TUMBLESTONE_BREAK = this.create("tumblestone.break")
@JvmField
@@ -140,62 +129,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
@JvmField
val TUMBLESTONE_STEP = this.create("tumblestone.step")
@JvmField
val GIMMIGHOUL_GIVE_ITEM_SMALL = this.create("gimmighoul.give_item_small")
@JvmField
val GIMMIGHOUL_REVEAL = this.create("gimmighoul.reveal")
@JvmField
val BERRY_BUSH_BREAK = this.create("berry_bush.break")
@JvmField
val BERRY_BUSH_PLACE = this.create("berry_bush.place")
@JvmField
val BIG_ROOT_BREAK = this.create("big_root.break")
@JvmField
val ENERGY_ROOT_PLACE = this.create("energy_root.place")
@JvmField
val VIVICHOKE_BREAK = this.create("vivichoke.break")
@JvmField
val VIVICHOKE_PLACE = this.create("vivichoke.place")
@JvmField
val MINT_BREAK = this.create("mint.break")
@JvmField
val MINT_PLACE = this.create("mint.place")
@JvmField
val REVIVAL_HERB_BREAK = this.create("revival_herb.break")
@JvmField
val REVIVAL_HERB_PLACE = this.create("revival_herb.place")
@JvmField
val MEDICINAL_LEEK_BREAK = this.create("medicinal_leek.break")
@JvmField
val MEDICINAL_LEEK_PLACE = this.create("medicinal_leek.plant")
@JvmField
val GILDED_CHEST_OPEN = this.create("gilded_chest.open")
@JvmField
val GILDED_CHEST_CLOSE = this.create("gilded_chest.close")
@JvmField
val GILDED_CHEST_STEP = this.create("gilded_chest.step")
@JvmField
val GILDED_CHEST_HIT = this.create("gilded_chest.hit")
@JvmField
val GILDED_CHEST_BREAK = this.create("gilded_chest.break")
@JvmField
val GILDED_CHEST_PLACE = this.create("gilded_chest.place")
@JvmField
val COIN_POUCH_SOUNDS = BlockSoundGroup(1f, 1f,
COIN_POUCH_BREAK,
COIN_POUCH_STEP,
COIN_POUCH_PLACE,
COIN_POUCH_HIT,
COIN_POUCH_STEP
)
@JvmField
val IMPACT_NORMAL = this.create("impact.normal")
@JvmField
@@ -251,99 +184,5 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
TUMBLESTONE_STEP
)
@JvmField
val BERRY_BUSH_SOUNDS = BlockSoundGroup(1f, 1f,
BERRY_BUSH_BREAK,
SoundEvents.BLOCK_GRASS_STEP,
BERRY_BUSH_PLACE,
SoundEvents.BLOCK_GRASS_HIT,
SoundEvents.BLOCK_GRASS_STEP
)
@JvmField
val BIG_ROOT_SOUNDS = BlockSoundGroup(1f, 1f,
BIG_ROOT_BREAK,
SoundEvents.BLOCK_ROOTS_STEP,
SoundEvents.BLOCK_ROOTS_PLACE,
SoundEvents.BLOCK_ROOTS_HIT,
SoundEvents.BLOCK_ROOTS_FALL
)
@JvmField
val ENERGY_ROOT_SOUNDS = BlockSoundGroup(1f, 1f,
SoundEvents.BLOCK_ROOTS_BREAK,
SoundEvents.BLOCK_ROOTS_STEP,
ENERGY_ROOT_PLACE,
SoundEvents.BLOCK_ROOTS_HIT,
SoundEvents.BLOCK_ROOTS_FALL
)
@JvmField
val MEDICINAL_LEEK_SOUNDS = BlockSoundGroup(1f, 1f,
MEDICINAL_LEEK_BREAK,
SoundEvents.BLOCK_GRASS_STEP,
MEDICINAL_LEEK_PLACE,
SoundEvents.BLOCK_GRASS_HIT,
SoundEvents.BLOCK_GRASS_FALL
)
@JvmField
val VIVICHOKE_SOUNDS = BlockSoundGroup(1f, 1f,
VIVICHOKE_BREAK,
SoundEvents.BLOCK_GRASS_STEP,
VIVICHOKE_PLACE,
SoundEvents.BLOCK_GRASS_HIT,
SoundEvents.BLOCK_GRASS_FALL
)
@JvmField
val MINT_SOUNDS = BlockSoundGroup(1f, 1f,
MINT_BREAK,
SoundEvents.BLOCK_GRASS_STEP,
MINT_PLACE,
SoundEvents.BLOCK_GRASS_HIT,
SoundEvents.BLOCK_GRASS_FALL
)
@JvmField
val REVIVAL_HERB_SOUNDS = BlockSoundGroup(1f, 1f,
REVIVAL_HERB_BREAK,
SoundEvents.BLOCK_GRASS_STEP,
REVIVAL_HERB_PLACE,
SoundEvents.BLOCK_GRASS_HIT,
SoundEvents.BLOCK_GRASS_FALL
)
@JvmField
val GILDED_CHEST_SOUNDS = BlockSoundGroup(1f, 1f,
GILDED_CHEST_BREAK,
GILDED_CHEST_STEP,
GILDED_CHEST_PLACE,
GILDED_CHEST_HIT,
GILDED_CHEST_STEP
)
@JvmField
val DISPLAY_CASE_ADD_ITEM = this.create("display_case.add_item")
@JvmField
val DISPLAY_CASE_REMOVE_ITEM = this.create("display_case.remove_item")
@JvmField
val DISPLAY_CASE_BREAK = this.create("display_case.break")
@JvmField
val DISPLAY_CASE_HIT = this.create("display_case.hit")
@JvmField
val DISPLAY_CASE_PLACE = this.create("display_case.place")
@JvmField
val DISPLAY_CASE_STEP = this.create("display_case.step")
@JvmField
val DISPLAY_CASE_SOUNDS = BlockSoundGroup(1f, 1f,
DISPLAY_CASE_BREAK,
DISPLAY_CASE_STEP,
DISPLAY_CASE_PLACE,
DISPLAY_CASE_HIT,
DISPLAY_CASE_STEP
)
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.of(cobblemonResource(name)))
}
@@ -9,18 +9,8 @@
package com.cobblemon.mod.common.advancement
import com.cobblemon.mod.common.Cobblemon
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 com.cobblemon.mod.common.advancement.criterion.*
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.advancement.criterion.Criterion
/**
@@ -30,37 +20,99 @@ import net.minecraft.advancement.criterion.Criterion
* @since October 26th, 2022
*/
object CobblemonCriteria {
val PICK_STARTER = this.create("pick_starter", SimpleCriterionTrigger(PokemonCriterion.CODEC))
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 CATCH_POKEMON = this.create("catch_pokemon", SimpleCriterionTrigger(CaughtPokemonCriterion.CODEC))
val DEFEAT_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("pokemon_defeated"),
SimpleCountableCriterionCondition::class.java
)
)
val CATCH_SHINY_POKEMON = this.create("catch_shiny_pokemon", SimpleCriterionTrigger(CountableCriterion.CODEC))
val COLLECT_ASPECT = this.create(
AspectCriterionTrigger(
cobblemonResource("aspects_collected"),
AspectCriterionCondition::class.java
)
)
val EGG_HATCH = this.create("eggs_hatched", SimpleCriterionTrigger(CountableCriterion.CODEC))
val POKEMON_INTERACT = this.create(
SimpleCriterionTrigger(
cobblemonResource("pokemon_interact"),
PokemonInteractCriterion::class.java
)
)
val EVOLVE_POKEMON = this.create("pokemon_evolved", SimpleCriterionTrigger(EvolvePokemonCriterion.CODEC))
val PARTY_CHECK = this.create(
SimpleCriterionTrigger(
cobblemonResource("party"),
PartyCheckCriterion::class.java
)
)
val WIN_BATTLE = this.create("battles_won", SimpleCriterionTrigger(BattleCountableCriterion.CODEC))
val LEVEL_UP = this.create(
SimpleCriterionTrigger(
cobblemonResource("level_up"),
LevelUpCriterionCondition::class.java
)
)
val DEFEAT_POKEMON = this.create("pokemon_defeated", SimpleCriterionTrigger(CountableCriterion.CODEC))
val PASTURE_USE = this.create(
SimpleCriterionTrigger(
cobblemonResource("pasture_use"),
PickStarterCriterionCondition::class.java
)
)
val COLLECT_ASPECT = this.create("aspects_collected", SimpleCriterionTrigger(AspectCriterion.CODEC))
val RESURRECT_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("resurrect_pokemon"),
PickStarterCriterionCondition::class.java
)
)
val POKEMON_INTERACT = this.create("pokemon_interact", SimpleCriterionTrigger(PokemonInteractCriterion.CODEC))
val TRADE_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("trade_pokemon"),
TradePokemonCriterionCondition::class.java
)
)
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("plant_tumblestone", SimpleCriterionTrigger(PlantTumblestoneCriterion.CODEC))
private fun <T : Criterion<*>> create(id: String, criteria: T): T = Cobblemon.implementation.registerCriteria(id, criteria)
private fun <T : Criterion<*>> create(criteria: T): T = Cobblemon.implementation.registerCriteria(criteria)
}
@@ -8,59 +8,38 @@
package com.cobblemon.mod.common.advancement.criterion
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
class AspectCriterion(
playerCtx: Optional<LootContextPredicate>,
val pokemon: Identifier,
val aspects: List<String>
): SimpleCriterionCondition<MutableMap<Identifier, MutableSet<String>>>(playerCtx) {
open class AspectCriterionTrigger(identifier: Identifier, criterionClass: Class<AspectCriterionCondition>) : SimpleCriterionTrigger<MutableMap<Identifier, MutableSet<String>>, AspectCriterionCondition>(identifier, criterionClass) {
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) }
}
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 }
}
}
//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,105 +10,58 @@ package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import com.cobblemon.mod.common.util.toJsonArray
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
import net.minecraft.util.Identifier
open class BattleCountableContext(var battle : PokemonBattle, times : Int) : CountableContext(times)
open class BattleCountableCriterionTrigger(identifier: Identifier, criterionClass: Class<BattleCountableCriterionCondition>) : SimpleCriterionTrigger<BattleCountableContext, BattleCountableCriterionCondition>(identifier, criterionClass) {
class BattleCountableCriterion(
playerCtx: Optional<LootContextPredicate>,
val battleTypes: List<String>,
count: Int
): CountableCriterion<BattleCountableContext>(playerCtx, count) {
}
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) }
class BattleCountableCriterionCondition(id: Identifier, predicate: LootContextPredicate) : CountableCriterionCondition<BattleCountableContext>(id, predicate) {
private var battleTypes = mutableListOf("any")
override fun fromJson(json: JsonObject) {
super.fromJson(json)
if(!json.get("battle_types").isJsonNull) {
battleTypes.clear()
json.get("battle_types").asJsonArray.asList().forEach() {
battleTypes.add(it.asString)
}
}
}
override fun toJson(json: JsonObject) {
super.toJson(json)
json.add("battle_types", battleTypes.toJsonArray())
}
override fun matches(player: ServerPlayerEntity, context: BattleCountableContext): Boolean {
var typeCheck = false
val advancementData = Cobblemon.playerData.get(player).advancementData
if (battleTypes.isEmpty() || battleTypes.contains("any")) {
typeCheck = true
}
if (battleTypes.contains("pvp")) {
typeCheck = context.battle.isPvP
context.times = advancementData.totalPvPBattleVictoryCount
}
if (battleTypes.contains("pvw")) {
typeCheck = context.battle.isPvW
context.times = advancementData.totalPvWBattleVictoryCount
}
if (battleTypes.contains("pvn")) {
typeCheck = context.battle.isPvN
context.times = advancementData.totalPvWBattleVictoryCount
}
if (battleTypes.size > 1) {
context.times = advancementData.totalBattleVictoryCount
}
return typeCheck && super.matches(player, context)
return context.times >= count && typeCheck
}
}
//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
// }
//}
open class BattleCountableContext(times : Int, var battle : PokemonBattle) : CountableContext(times)
@@ -8,13 +8,10 @@
package com.cobblemon.mod.common.advancement.criterion
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.EntityPredicate
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
import net.minecraft.util.Identifier
/**
* A context that is used when you require a [CountableContext] along with some type string.
@@ -24,38 +21,19 @@ import java.util.Optional
*/
open class CountablePokemonTypeContext(times: Int, var type: String) : CountableContext(times)
class CaughtPokemonCriterion(
playerCtx: Optional<LootContextPredicate>,
val type: String,
count: Int
): CountableCriterion<CountablePokemonTypeContext>(playerCtx, count) {
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)
}
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 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")
}
}
//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,69 +8,48 @@
package com.cobblemon.mod.common.advancement.criterion
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.EntityPredicate
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
import net.minecraft.util.Identifier
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
}
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 toJson(json: JsonObject) {
json.addProperty("count", count)
}
override fun matches(player: ServerPlayerEntity, context: T) = context.times >= count
}
///**
// * 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)
/**
* 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)
@@ -1,65 +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.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)
@@ -0,0 +1,38 @@
/*
* 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,43 +9,12 @@
package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.pokemon.Pokemon
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.EntityPredicate
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
import net.minecraft.util.Identifier
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) {
class LevelUpCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<LevelUpContext>(id, entity) {
var level = 0
var evolved = true
override fun toJson(json: JsonObject) {
@@ -67,4 +36,6 @@ class LevelUpCriterion(
}
return level == context.level && evolutionCheck == evolved
}
}*/
}
open class LevelUpContext(var level : Int, var pokemon : Pokemon)
@@ -10,62 +10,14 @@ 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.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.EntityPredicate
import com.cobblemon.mod.common.util.party
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
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){
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 {
@@ -99,4 +51,4 @@ class PartyCheckCriterion(
}
}
open class PartyCheckContext(val party : PlayerPartyStore)*/
open class PartyCheckContext(val party : PlayerPartyStore)
@@ -10,27 +10,26 @@ package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.pokemon.Pokemon
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.EntityPredicate
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
import net.minecraft.util.Identifier
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) }
/**
* 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)
}
override fun matches(player: ServerPlayerEntity, context: Pokemon): Boolean {
return properties.matches(context)
override fun fromJson(json: JsonObject) {
properties = PokemonProperties.parse(json.get("properties")?.asString ?: "")
}
override fun matches(player: ServerPlayerEntity, context: Pokemon) = properties.matches(context)
}
@@ -1,71 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.block.TumblestoneBlock
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.dynamic.Codecs
import net.minecraft.util.math.BlockPos
import java.util.Optional
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 matches(player: ServerPlayerEntity, context: PlantTumblestoneContext): Boolean {
return context.block.canGrow(context.pos, player.world)
}
}
///**
// * 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,33 +9,26 @@
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.EntityPredicate
import com.google.gson.JsonObject
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 PokemonInteractContext(val type: Identifier, val item: Identifier)
open class PokemonInteractContext(var type: Identifier, var item : Identifier)
class PokemonInteractCriterion(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<PokemonInteractContext>(id, entity) {
var type = "any"
var item = "any"
override fun toJson(json: JsonObject) {
json.addProperty("type", type)
json.addProperty("item", item)
}
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 fromJson(json: JsonObject) {
type = json.get("type")?.asString ?: "any"
item = json.get("item")?.asString ?: "any"
}
override fun matches(player: ServerPlayerEntity, context: PokemonInteractContext): Boolean {
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")
return (context.type == type.asIdentifierDefaultingNamespace() || type == "any") && (context.item == item.asIdentifierDefaultingNamespace() || item == "any")
}
}
@@ -8,28 +8,61 @@
package com.cobblemon.mod.common.advancement.criterion
import com.mojang.serialization.Codec
import com.google.gson.JsonObject
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 java.util.Optional
import net.minecraft.util.Identifier
class SimpleCriterionTrigger<T, C : SimpleCriterionCondition<T>>(
val codec: Codec<C>,
/**
* 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>
) : AbstractCriterion<C>() {
override fun getConditionsCodec() = codec
fun trigger(player: ServerPlayerEntity, context: T) {
return this.trigger(player) {
it.matches(player, context)
}
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
}
fun trigger(player: ServerPlayerEntity, context: T) = 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>(
val playerCtx: Optional<LootContextPredicate>
) : AbstractCriterion.Conditions {
override fun player() = playerCtx
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)
abstract fun matches(player: ServerPlayerEntity, context: T): Boolean
}
@@ -10,59 +10,12 @@ package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.predicate.entity.EntityPredicate
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.dynamic.Codecs
import java.util.Optional
import net.minecraft.util.Identifier
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) {
class TradePokemonCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<TradePokemonContext>(id, entity) {
var traded = "any"
var received = "any"
var tradedHeldItem = "any"
@@ -91,4 +44,4 @@ class TradePokemonCriterion(
}
}
open class TradePokemonContext(val traded: Pokemon, val received: Pokemon)*/
open class TradePokemonContext(val traded: Pokemon, val received: Pokemon)
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.api.abilities
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.DataKeys
import com.google.gson.JsonObject
import net.minecraft.nbt.NbtCompound
@@ -22,7 +21,7 @@ import net.minecraft.nbt.NbtCompound
* @author Qu
* @since January 9th, 2022
*/
open class Ability internal constructor(var template: AbilityTemplate, forced: Boolean) {
open class Ability internal constructor(var template: AbilityTemplate, var forced: Boolean) {
val name: String
get() = template.name
@@ -35,26 +34,16 @@ open class Ability internal constructor(var template: AbilityTemplate, forced: B
/**
* This represents the last known index of this backing ability in the species data.
* @see [Pokemon.updateAbility].
*/
var forced: Boolean = forced
internal set
/**
* This represents the last known index of this backing ability in the species data.
*
* @see [Pokemon.updateAbility].
* If -1 the ability will attempt to find the correct index or reroll itself, this is done in order to be compatible with existing data
* The value can be mutated however this is not recommended by API users.
*/
var index: Int = -1
internal set
/**
* The last known priority of this ability in the species data.
*
* @see [Pokemon.updateAbility].
* The value can be mutated however this is not recommended by API users.
*/
var priority = Priority.LOWEST
internal set
open fun saveToNBT(nbt: NbtCompound): NbtCompound {
nbt.putString(DataKeys.POKEMON_ABILITY_NAME, name)
@@ -73,23 +62,19 @@ open class Ability internal constructor(var template: AbilityTemplate, forced: B
}
open fun loadFromNBT(nbt: NbtCompound): Ability {
this.template = Abilities.getOrException(nbt.getString(DataKeys.POKEMON_ABILITY_NAME))
this.forced = nbt.getBoolean(DataKeys.POKEMON_ABILITY_FORCED)
forced = nbt.getBoolean(DataKeys.POKEMON_ABILITY_FORCED)
if (nbt.contains(DataKeys.POKEMON_ABILITY_INDEX) && nbt.contains(DataKeys.POKEMON_ABILITY_PRIORITY)) {
this.index = nbt.getInt(DataKeys.POKEMON_ABILITY_INDEX)
this.priority = Priority.valueOf(nbt.getString(DataKeys.POKEMON_ABILITY_PRIORITY))
}
return this
}
open fun loadFromJSON(json: JsonObject): Ability {
this.template = Abilities.getOrException(json.get(DataKeys.POKEMON_ABILITY_NAME).asString)
this.forced = json.get(DataKeys.POKEMON_ABILITY_FORCED)?.asBoolean ?: false
forced = json.get(DataKeys.POKEMON_ABILITY_FORCED)?.asBoolean ?: false
if (json.has(DataKeys.POKEMON_ABILITY_INDEX) && json.has(DataKeys.POKEMON_ABILITY_PRIORITY)) {
this.index = json.get(DataKeys.POKEMON_ABILITY_INDEX).asInt
this.priority = Priority.valueOf(json.get(DataKeys.POKEMON_ABILITY_PRIORITY).asString)
}
return this
}
}
@@ -28,6 +28,7 @@ class AbilityTemplate(
*/
fun create(forced: Boolean = false) = builder(this, forced)
/**
* Returns the Ability and loads the given NBT Tag into it.
*
@@ -41,5 +42,4 @@ class AbilityTemplate(
* Ability extensions need to write and read their needed data from here.
*/
fun create(json: JsonObject) = create().loadFromJSON(json)
}
@@ -13,13 +13,8 @@ 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.
@@ -27,7 +22,7 @@ import net.minecraft.world.gen.feature.ConfiguredFeature
* @author Licious
* @since October 29th, 2022
*/
enum class Apricorn : StringIdentifiable {
enum class Apricorn {
BLACK,
BLUE,
@@ -87,20 +82,4 @@ enum class Apricorn : StringIdentifiable {
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)
}
}
@@ -135,31 +135,28 @@ class BattleMessage(rawMessage: String) {
return this.actorAndActivePokemon(pnx, battle)
}
/**
* Queries an argument at the given [index] for a 'pnx' and uuid that will be parsed into a [BattlePokemon].
*
* @param index The index of the argument referencing the [BattlePokemon].
* @param battle The [PokemonBattle] being queried.
* @return The [BattlePokemon] if the argument exists and is successfully parsed; otherwise null.
/*
Example:
|-curestatus|p1: a8e5710e-6835-4387-99d3-331b45f848e6|slp|[msg]
|-curestatus|p1: 0686b20d-3019-4167-92e2-786834245743|slp|[msg]
// Function used to grab BattlePokemon (As we need to be able to grab non active pokemon to cure their status)
// by using playerNumber(actorID) and pokemon uuid(pokemonID)
*/
fun battlePokemon(index: Int, battle: PokemonBattle): BattlePokemon? {
val (actorID, pokemonID) = this.pnxAndUuid(index) ?: return null
return this.battlePokemon(actorID, pokemonID, battle)
fun getBattlePokemon(index: Int, battle: PokemonBattle): BattlePokemon? {
val actorAndPokemonID = this.argumentAt(index)?.takeIf { it.length >= 2 }?.split(": ") ?: return null
if (actorAndPokemonID.count() < 2) return null
val actorID = actorAndPokemonID[0]
val pokemonID = actorAndPokemonID[1]
return this.getBattlePokemon(actorID, pokemonID, battle)
}
/**
* Queries an optional argument identified by [optionalArg] for a 'pnx' and uuid that will be parsed into a [BattlePokemon].
*
* @param optionalArg The id of the optional argument referencing the [BattlePokemon].
* @param battle The [PokemonBattle] being queried.
* @return The [BattlePokemon] if the argument exists and is successfully parsed; otherwise null.
*/
fun battlePokemonFromOptional(battle: PokemonBattle, optionalArg: String = "of"): BattlePokemon? {
val optional = this.optionalArguments.get(optionalArg) ?: return null
val pokemonID = optional.takeIf { it.length >= 2 }?.split(":")?.takeIf { it.size == 2 } ?: return null
val pnx = pokemonID[0].takeIf { it.matches(PNX_MATCHER) || it.matches(PN_MATCHER) } ?: return null
val uuid = pokemonID[1].trim()
return this.battlePokemon(pnx, uuid, battle)
fun getSourceBattlePokemon(battle: PokemonBattle): BattlePokemon? {
val sourcePokemonArgument = this.optionalArguments.get("of") ?: return null
val actorAndPokemonID = sourcePokemonArgument.takeIf { it.length >= 2 }?.split(": ") ?: return null
if (actorAndPokemonID.count() < 2) return null
val actorID = actorAndPokemonID[0]
val pokemonID = actorAndPokemonID[1]
return this.getBattlePokemon(actorID, pokemonID, battle)
}
/**
@@ -170,7 +167,7 @@ class BattleMessage(rawMessage: String) {
*/
fun pnxAndUuid(index: Int): Pair<String, String>? {
val argument = this.argumentAt(index)?.takeIf { it.length >= 2 }?.split(":")?.takeIf { it.size == 2 } ?: return null
val pnx = argument[0].takeIf { it.matches(PNX_MATCHER) || it.matches(PN_MATCHER) } ?: return null
val pnx = argument[0].takeIf { it.matches(PNX_MATCHER) } ?: return null
val uuid = argument[1].trim()
return pnx to uuid
}
@@ -237,7 +234,7 @@ class BattleMessage(rawMessage: String) {
null
}
private fun battlePokemon(pnx: String, pokemonID: String, battle: PokemonBattle): BattlePokemon? = try {
private fun getBattlePokemon(pnx: String, pokemonID: String, battle: PokemonBattle): BattlePokemon? = try {
battle.getBattlePokemon(pnx, pokemonID)
} catch (_: Exception) {
null
@@ -8,11 +8,13 @@
package com.cobblemon.mod.common.api.battles.model
import com.bedrockk.molang.runtime.MoLangEnvironment
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.Environment
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
@@ -20,6 +22,8 @@ import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
import com.cobblemon.mod.common.api.battles.model.actor.FleeableBattleActor
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.api.text.red
@@ -37,10 +41,10 @@ import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
import com.cobblemon.mod.common.battles.interpreter.ContextManager
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.battles.ForfeitActionResponse
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.client.battle.BattleEndPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleMusicPacket
import com.cobblemon.mod.common.pokemon.evolution.progress.DefeatEvolutionProgress
import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHitsEvolutionProgress
import com.cobblemon.mod.common.pokemon.evolution.requirements.DefeatRequirement
@@ -103,11 +107,6 @@ open class PokemonBattle(
var turn: Int = 1
private set
private var ticks: Int = 0
set(value) { field = value.coerceAtMost(Int.MAX_VALUE) } // go outside
/** The current duration of the battle in seconds. */
val time: Int get() = ticks % 20
var dispatchResult = GO
val dispatches = ConcurrentLinkedDeque<BattleDispatch>()
@@ -167,7 +166,7 @@ open class PokemonBattle(
fun getActor(player: ServerPlayerEntity) = actors.firstOrNull { it.isForPlayer(player) }
/**
* Gets a [BattleActor] and an [ActiveBattlePokemon] from a pnx key, e.g. p2a
* Gets a BattleActor and an [ActiveBattlePokemon] from a pnx key, e.g. p2a
*
* Returns null if either the pn or x is invalid.
*/
@@ -180,11 +179,6 @@ open class PokemonBattle(
return actor to pokemon
}
/**
* Gets a [BattlePokemon] from a pnx key and uuid.
*
* Returns null if the pnx key is invalid or the uuid does not exist.
*/
fun getBattlePokemon(pnx: String, pokemonID: String): BattlePokemon {
val actor = actors.find { it.showdownId == pnx.substring(0, 2) }
?: throw IllegalStateException("Invalid pnx: $pnx - unknown actor")
@@ -396,9 +390,8 @@ open class PokemonBattle(
return
}
if (started) {
ticks++
if (isPvW && !ended && dispatches.isEmpty()) checkFlee()
if (started && isPvW && !ended && dispatches.isEmpty()) {
checkFlee()
}
}
@@ -440,24 +433,13 @@ open class PokemonBattle(
}
fun checkForInputDispatch() {
if (checkForfeit()) return // ignore actors that are still choosing, their choices don't matter anymore
val readyToInput = (actors.any { !it.mustChoose && it.responses.isNotEmpty() } && actors.none { it.mustChoose })
val readyToInput = actors.any { !it.mustChoose && it.responses.isNotEmpty() } && actors.none { it.mustChoose }
if (readyToInput && captureActions.isEmpty()) {
actors.filter { it.responses.isNotEmpty() }.forEach { it.writeShowdownResponse() }
actors.forEach { it.responses.clear() ; it.request = null }
}
}
/** Forces Showdown to end the battle when a [BattleActor] chooses to forfeit. */
private fun checkForfeit(): Boolean {
val forfeit = actors.find { it.responses.any { it is ForfeitActionResponse } }
return forfeit?.let {
this.dispatchWaiting { this.broadcastChatMessage(battleLang("forfeit", it.getName()).red()) }
writeShowdownAction(">forcelose ${it.showdownId}")
true
} ?: false
}
/**
* Creates a [Text] representation of an error to interpret a battle message.
* This also logs the error, the goal of this function is to make sure users see missing interpretations and report them to us.
@@ -497,4 +479,4 @@ open class PokemonBattle(
return queryStruct
}
}
}
@@ -48,7 +48,7 @@ object Berries : JsonDataRegistry<Berry> {
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(MulchVariant::class.java, MulchVariantAdapter)
.registerTypeAdapter(NumberRange.DoubleRange::class.java, FloatNumberRangeAdapter)
.registerTypeAdapter(NumberRange.FloatRange::class.java, FloatNumberRangeAdapter)
.registerTypeAdapter(Status::class.java, StatusAdapter)
.registerTypeAdapter(TypeToken.getParameterized(Collection::class.java, Box::class.java).type, BoxCollectionAdapter)
.registerTypeAdapter(Box::class.java, BoxAdapter)
@@ -103,4 +103,4 @@ object Berries : JsonDataRegistry<Berry> {
*/
fun getByName(name: String): Berry? = this.getByIdentifier(cobblemonResource(name))
}
}
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.drop
import com.cobblemon.mod.common.api.events.CobblemonEvents.LOOT_DROPPED
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.entity.LivingEntity
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
@@ -89,8 +88,6 @@ class DropTable {
amount: IntRange = this.amount
) {
val drops = getDrops(amount).toMutableList()
val heldItem = (entity as PokemonEntity).pokemon.heldItemNoCopy()
if (!heldItem.isEmpty) entity.dropItem(heldItem.item)
LOOT_DROPPED.postThen(
event = LootDroppedEvent(this, player, entity, drops),
ifSucceeded = { it.drops.forEach { it.drop(entity, world, pos, player) } }
@@ -1,97 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.entity.pokemon
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.effects.IllusionEffect
import com.cobblemon.mod.common.entity.pokemon.effects.TransformEffect
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import net.minecraft.nbt.NbtCompound
import java.util.concurrent.CompletableFuture
import kotlin.reflect.KClass
/**
* Represents a temporary alteration to how a [PokemonEntity] is rendered and behaves. May include a temporary visual effect
* when applied.
*
* @author Segfault Guy
* @since March 5th, 2024
*/
interface EntityEffect {
/**
* Starts this effect for the provided [PokemonEntity].
*
* @return A [CompletableFuture] that completes after the effect has been applied. Or null if the effect failed to start.
*/
fun start(entity: PokemonEntity): CompletableFuture<PokemonEntity>?
/**
* Ends this effect for the provided [PokemonEntity].
*
* @return A [CompletableFuture] that completes after the effect has been reverted. Or null if the effect failed to end.
*/
fun end(entity: PokemonEntity): CompletableFuture<PokemonEntity>?
/** Saves this effect to NBT. */
fun saveToNbt(): NbtCompound
/** Loads this effect from NBT. */
fun loadFromNBT(nbt: NbtCompound)
companion object {
private val effects = mutableMapOf<String, KClass<out EntityEffect>>()
private val defaults = mutableMapOf<String, () -> EntityEffect>()
init {
register(IllusionEffect.ID, IllusionEffect::class, ::IllusionEffect)
register(TransformEffect.ID, TransformEffect::class, ::TransformEffect)
}
fun <T : EntityEffect> register(id: String, type: KClass<T>, default: () -> T) {
effects[id] = type
defaults[id] = default
}
fun createDefault(id: String): EntityEffect? = defaults[id]?.invoke()
fun loadFromNbt(nbt: NbtCompound): EntityEffect? {
if (nbt.contains(DataKeys.ENTITY_EFFECT_ID)) {
val id = nbt.getString(DataKeys.ENTITY_EFFECT_ID)
return createDefault(id)?.also { it.loadFromNBT(nbt) }
}
return null
}
}
}
/** An [EntityEffect] that modifies the dimensions of a [PokemonEntity]. */
interface PhysicalEffect : EntityEffect {
val scale: Float
}
/** An [EntityEffect] that alters the physical appearance of a [PokemonEntity] to match a [mock]. */
interface MocKEffect : PhysicalEffect {
val mock: PokemonProperties
val exposedSpecies: Species?
get() = this.mock.species?.let { PokemonSpecies.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
val exposedForm: FormData?
get() = this.mock.form?.let {
formID -> this.exposedSpecies?.forms?.firstOrNull { it.formOnlyShowdownId().equals(formID, true) } }
?: this.exposedSpecies?.standardForm
}
@@ -33,7 +33,6 @@ import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEv
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionDisplayEvent
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionTestedEvent
import com.cobblemon.mod.common.api.events.pokemon.interaction.ExperienceCandyUseEvent
import com.cobblemon.mod.common.api.events.pokemon.interaction.HeldItemUpdatedEvent
import com.cobblemon.mod.common.api.events.pokemon.interaction.PokemonInteractionGUICreationEvent
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
@@ -67,8 +66,6 @@ object CobblemonEvents {
val EVOLUTION_COMPLETE = EventObservable<EvolutionCompleteEvent>()
@JvmField
val POKEMON_NICKNAMED = CancelableObservable<PokemonNicknamedEvent>()
@JvmField
val HELD_ITEM_UPDATED = CancelableObservable<HeldItemUpdatedEvent>()
@JvmField
val THROWN_POKEBALL_HIT = CancelableObservable<ThrownPokeballHitEvent>()
@@ -1,36 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokemon.interaction
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.entity.LivingEntity
import net.minecraft.item.ItemStack
/**
* Event published when a held item is being updated on a [Pokemon].
* Cancelling the event will prevent any action from being taken on
* the Pokémon.
*
* [newItem] differs from [originalStack] in that the former is a
* one-sized version of the original (Pokémon cannot hold stacks) and
* changing it in any way will not affect the original item held by
* the player.
*
* @author Whatsy, Hiroku
* @since November 13th, 2023
*/
data class HeldItemUpdatedEvent(
val cause: LivingEntity?,
val pokemon: Pokemon,
val originalStack: ItemStack,
val decrement: Boolean,
val oldItem: ItemStack,
val newItem: ItemStack
) : Cancelable()
@@ -11,6 +11,7 @@ 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
@@ -47,7 +48,7 @@ class Fossil(
}
return this.fossils.all { fossil ->
ingredients.any { fossil.test(it) }
ingredients.any { fossil.item.fits(it.item, Registries.ITEM) && fossil.nbt.test(it) }
}
}
@@ -63,7 +64,7 @@ class Fossil(
return ingredients.all { ingredient ->
ingredients.count { item -> ingredient.itemMatches(item.registryEntry) } <=
this.fossils.count { fossil -> fossil.test(ingredient) }
this.fossils.count { fossil -> fossil.item.fits(ingredient.item, Registries.ITEM) && fossil.nbt.test(ingredient) }
}
}
@@ -73,7 +74,7 @@ class Fossil(
* @return True if the [ItemStack] is an ingredient, false otherwise.
*/
fun isIngredient(itemStack: ItemStack): Boolean {
return this.fossils.any { it.test(itemStack) }
return this.fossils.any { it.item.fits(itemStack.item, Registries.ITEM) && it.nbt.test(itemStack) }
}
}
@@ -47,10 +47,10 @@ abstract class ParentWidget(
super.mouseMoved(pMouseX, pMouseY)
}
override fun mouseScrolled(mouseX: Double, mouseY: Double, horizontalAmount: Double, verticalAmount: Double): Boolean {
override fun mouseScrolled(pMouseX: Double, pMouseY: Double, pDelta: Double): Boolean {
return children.any {
it.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount)
} || super.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount)
it.mouseScrolled(pMouseX, pMouseY, pDelta)
} || super.mouseScrolled(pMouseX, pMouseY, pDelta)
}
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
@@ -1,74 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.item.ability
import com.cobblemon.mod.common.api.abilities.*
import com.cobblemon.mod.common.item.interactive.ability.AbilityTypeChanger
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbility
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbilityType
/**
* Represents a change operation for a [Pokemon.ability].
* This is backed by the associated [PotentialAbility].
*
* @param T The type of [PotentialAbility].
*/
interface AbilityChanger<T : PotentialAbility> {
/**
* The [PotentialAbilityType] of type [T].
*/
val type: PotentialAbilityType<T>
/**
* Collects all the possible targets for the [type] and the given [pokemon].
*
* @param pokemon The [Pokemon] being queried.
* @return A set containing all their legal abilities associated with [type], can be empty.
*/
fun queryPossible(pokemon: Pokemon): Set<AbilityTemplate>
/**
* Attempts to update the ability of the given [pokemon].
*
* @param pokemon The [Pokemon] attempting to change an ability.
* @return If the operation was successful.
*/
fun performChange(pokemon: Pokemon): Boolean
/**
* Checks if the current [PotentialAbilityType] of a [Pokemon.ability] is possible to swap from with this.
*
* @param type The [PotentialAbilityType] or null if the ability is forced and/or illegal.
* @return If the operation can be performed.
*/
fun canChangeFrom(type: PotentialAbilityType<*>?): Boolean
companion object {
/**
* An implementation of [AbilityChanger] for [CommonAbility].
*
* While this can be used in custom data scenarios this functions most "game-like" if the Pokémon only has 2 common abilities.
*/
@JvmStatic
val COMMON_ABILITY: AbilityChanger<CommonAbility> = AbilityTypeChanger(CommonAbilityType) { other -> other == CommonAbilityType }
/**
* An implementation of [AbilityChanger] for [HiddenAbility].
*
* While this can be used in custom data scenarios this functions most "game-like" if the Pokémon only has 1 hidden ability.
*/
@JvmStatic
val HIDDEN_ABILITY: AbilityChanger<HiddenAbility> = AbilityTypeChanger(HiddenAbilityType) { other -> other == CommonAbilityType || other == HiddenAbilityType }
}
}
@@ -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.effectiveName().string }
stringify = { it.entityName }
)
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.DoubleRange::class.java, FloatNumberRangeAdapter)
.registerTypeAdapter(NumberRange.FloatRange::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.lengthX.pow(2)) + 1.5F + sqrt(2 * target.boundingBox.lengthX.pow(2))).toFloat()
val proximity = this.proximity.takeIf { it != -1F } ?: (sqrt(2 * user.boundingBox.xLength.pow(2)) + 1.5F + sqrt(2 * target.boundingBox.xLength.pow(2))).toFloat()
if (target.distanceTo(user) !in proximity..20F) {
future.complete(Unit)
@@ -9,9 +9,7 @@
package com.cobblemon.mod.common.api.multiblock
import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
import net.minecraft.block.BlockRenderType
import net.minecraft.block.BlockState
import net.minecraft.block.BlockWithEntity
import net.minecraft.block.*
import net.minecraft.block.entity.BlockEntity
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.player.PlayerEntity
@@ -22,7 +20,6 @@ import net.minecraft.util.Hand
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import net.minecraft.world.WorldView
/**
* A block that can be part of a [MultiblockStructure]
@@ -37,8 +34,8 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
itemStack: ItemStack?
) {
if (world is ServerWorld) {
val multiblockEntity = world.getBlockEntity(pos) as? MultiblockEntity
multiblockEntity?.multiblockBuilder?.validate(world)
val multiblockEntity = world.getBlockEntity(pos) as MultiblockEntity
multiblockEntity.multiblockBuilder?.validate(world)
}
super.onPlaced(world, pos, state, placer, itemStack)
}
@@ -54,22 +51,21 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
if(hand == Hand.OFF_HAND) {
return ActionResult.SUCCESS
}
val entity = world.getBlockEntity(pos) as MultiblockEntity?
if (entity?.multiblockStructure != null) {
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?): BlockState {
val result = super.onBreak(world, pos, state, player)
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
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)
}
}
return result
}
override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? {
@@ -81,11 +77,6 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
return BlockRenderType.MODEL
}
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)
fun markRemoved(world: World):Unit
fun onTriggerEvent(state: BlockState?, world: ServerWorld?, pos: BlockPos?, random: Random?)
}
@@ -205,8 +205,8 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
createDefault("ancient_wing_ball", throwPower = 2.5f, ancient = true)
createDefault("ancient_jet_ball", throwPower = 2.5f, ancient = true)
createDefault("ancient_origin_ball", GuaranteedModifier(), ancient = true)
// Luxury ball effect, low priority as it must be triggered before soothe bell as of gen 4
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(priority = Priority.LOW) { event ->
// Luxury ball effect
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(priority = Priority.LOWEST) { event ->
var increment = (event.newFriendship - event.pokemon.friendship).toFloat()
if (increment <= 1F) {
event.pokemon.caughtBall.effects.filterIsInstance<FriendshipEarningBoostEffect>()
@@ -13,21 +13,18 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.abilities.Ability
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.api.types.tera.TeraTypes
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.*
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
@@ -55,11 +52,6 @@ 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()
@@ -302,18 +294,6 @@ open class PokemonProperties {
)
fun apply(pokemon: Pokemon) {
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
this.customProperties.forEach { it.apply(pokemon) }
this.commonApply(pokemon)
}
fun apply(pokemonEntity: PokemonEntity) {
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
this.customProperties.forEach { it.apply(pokemonEntity) }
this.commonApply(pokemonEntity.pokemon)
}
private fun commonApply(pokemon: Pokemon) {
species?.let {
return@let try {
if (it == "random") {
@@ -333,8 +313,8 @@ open class PokemonProperties {
friendship?.let { pokemon.setFriendship(it) }
pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace())?.let { pokeball -> pokemon.caughtBall = pokeball } }
nature?.let { Natures.getNature(it.asIdentifierDefaultingNamespace())?.let { nature -> pokemon.nature = nature } }
ability?.let { this.createAbility(it, pokemon.form)?.let(pokemon::updateAbility) }
status?.let { Statuses.getStatus(it)?.let { status -> if (status is PersistentStatus) pokemon.applyStatus(status) } }
ability?.let { Abilities.getOrException(it).create(true).let { ability -> pokemon.ability = ability } }
status?.let { Statuses.getStatus(it)?.let { pokemon.applyStatus(it as PersistentStatus) } }
customProperties.forEach { it.apply(pokemon) }
ivs?.let { ivs ->
ivs.forEach { stat ->
@@ -346,7 +326,7 @@ open class PokemonProperties {
pokemon.setEV(stat.key, stat.value)
}
}
teraType?.let { TeraTypes.get(it.asIdentifierDefaultingNamespace())?.let { type -> pokemon.teraType = type } }
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemon.teraType = type } }
dmaxLevel?.let { pokemon.dmaxLevel = it }
gmaxFactor?.let { pokemon.gmaxFactor = it }
tradeable?.let { pokemon.tradeable = it }
@@ -375,17 +355,69 @@ open class PokemonProperties {
pokemon.updateAspects()
}
fun apply(pokemonEntity: PokemonEntity) {
species?.let {
return@let try {
if (it == "random") {
PokemonSpecies.species.random()
} else {
PokemonSpecies.getByIdentifier(it.asIdentifierDefaultingNamespace())
}
} catch (e: InvalidIdentifierException) {
null
}
}?.let { pokemonEntity.pokemon.species = it }
nickname?.let { pokemonEntity.pokemon.nickname = it }
form?.let { formID -> pokemonEntity.pokemon.species.forms.firstOrNull { it.formOnlyShowdownId().equals(formID, true) } }?.let { form -> pokemonEntity.pokemon.form = form }
level?.let { pokemonEntity.pokemon.level = it }
shiny?.let { pokemonEntity.pokemon.shiny = it }
gender?.let { pokemonEntity.pokemon.gender = it }
friendship?.let { pokemonEntity.pokemon.setFriendship(it) }
pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace())?.let { pokeball -> pokemonEntity.pokemon.caughtBall = pokeball } }
nature?.let { Natures.getNature(it.asIdentifierDefaultingNamespace())?.let { nature -> pokemonEntity.pokemon.nature = nature } }
ability?.let { Abilities.getOrException(it).create(true).let { ability -> pokemonEntity.pokemon.ability = ability } }
status?.let { Statuses.getStatus(it)?.let { pokemonEntity.pokemon.applyStatus(it as PersistentStatus) } }
customProperties.forEach { it.apply(pokemonEntity) }
ivs?.let { ivs ->
ivs.forEach { stat ->
pokemonEntity.pokemon.setIV(stat.key, stat.value)
}
}
evs?.let { evs ->
evs.forEach { stat ->
pokemonEntity.pokemon.setEV(stat.key, stat.value)
}
}
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemonEntity.pokemon.teraType = type } }
dmaxLevel?.let { pokemonEntity.pokemon.dmaxLevel = it }
gmaxFactor?.let { pokemonEntity.pokemon.gmaxFactor = it }
tradeable?.let { pokemonEntity.pokemon.tradeable = it }
// If it has been explicitly set to NONE, clear the stored information, including the originalTrainer property
originalTrainerType?.let {
if (it == OriginalTrainerType.NONE) {
pokemonEntity.pokemon.removeOriginalTrainer()
originalTrainer = null
}
}
originalTrainer?.let { ot ->
val type = originalTrainerType ?: pokemonEntity.pokemon.originalTrainerType
when (type) {
OriginalTrainerType.PLAYER -> {
when (ot.length) {
in 3..16 -> server()?.userCache?.findByName(ot)?.get()?.id // OT is a Username
36 -> UUID.fromString(ot) // OT is a UUID
else -> null // OT is invalid
}?.let { uuid -> pokemonEntity.pokemon.setOriginalTrainer(uuid) }
}
OriginalTrainerType.NPC -> pokemonEntity.pokemon.setOriginalTrainer(ot)
else -> {}
}
pokemonEntity.pokemon.refreshOriginalTrainer()
}
pokemonEntity.pokemon.updateAspects()
}
fun matches(pokemon: Pokemon): Boolean {
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
return this.commonMatches(pokemon) && this.customProperties.none { !it.matches(pokemon) }
}
fun matches(pokemonEntity: PokemonEntity): Boolean {
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
return this.commonMatches(pokemonEntity.pokemon) && this.customProperties.none { !it.matches(pokemonEntity) }
}
private fun commonMatches(pokemon: Pokemon): Boolean {
level?.takeIf { it != pokemon.level }?.let { return false }
shiny?.takeIf { it != pokemon.shiny }?.let { return false }
gender?.takeIf { it != pokemon.gender }?.let { return false }
@@ -403,7 +435,7 @@ open class PokemonProperties {
return false
}
}
nickname?.takeIf { it.string != pokemon.nickname?.string }?.let { return false }
nickname?.takeIf { it != pokemon.nickname }?.let { return false }
form?.takeIf { !it.equals(pokemon.form.name, true) }?.let { return false }
friendship?.takeIf { it != pokemon.friendship }?.let { return false }
pokeball?.takeIf { it != pokemon.caughtBall.name.toString() }?.let { return false }
@@ -416,23 +448,59 @@ open class PokemonProperties {
evs?.forEach{ stat ->
if (stat.value != pokemon.evs[stat.key]) { return false }
}
teraType?.takeIf { it.asIdentifierDefaultingNamespace() != pokemon.teraType.id }?.let { return false }
teraType?.takeIf { it != pokemon.teraType.name }?.let { return false }
dmaxLevel?.takeIf { it != pokemon.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != pokemon.gmaxFactor }?.let { return false }
tradeable?.takeIf { it != pokemon.tradeable }?.let { return false }
originalTrainer?.takeIf { it != pokemon.originalTrainer }?.let{ return false }
originalTrainerType?.takeIf { it != pokemon.originalTrainerType }?.let{ return false }
return true
return customProperties.none { !it.matches(pokemon) }
}
fun matches(pokemonEntity: PokemonEntity): Boolean {
level?.takeIf { it != pokemonEntity.pokemon.level }?.let { return false }
shiny?.takeIf { it != ("shiny" in pokemonEntity.pokemon.aspects) }?.let { return false }
gender?.takeIf { it != pokemonEntity.pokemon.gender }?.let { return false }
species?.run {
try {
val species = if (this == "random") {
PokemonSpecies.species.random()
} else {
PokemonSpecies.getByIdentifier(this.asIdentifierDefaultingNamespace()) ?: return@run
}
if (pokemonEntity.pokemon.species != species) {
return false
}
} catch (e: InvalidIdentifierException) {}
}
nickname?.takeIf { it != pokemonEntity.pokemon.nickname }?.let { return false }
form?.takeIf { !it.equals(pokemonEntity.pokemon.form.name, true) }?.let { return false }
friendship?.takeIf { it != pokemonEntity.pokemon.friendship }?.let { return false }
pokeball?.takeIf { it != pokemonEntity.pokemon.caughtBall.name.toString() }?.let { return false }
nature?.takeIf { it != pokemonEntity.pokemon.nature.name.toString() }?.let { return false }
ability?.takeIf { it != pokemonEntity.pokemon.ability.name }?.let { return false }
status?.takeIf { it != pokemonEntity.pokemon.status?.status?.showdownName }?.let { return false }
ivs?.forEach{ stat ->
if (stat.value != pokemonEntity.pokemon.ivs[stat.key]) { return false }
}
evs?.forEach{ stat ->
if (stat.value != pokemonEntity.pokemon.evs[stat.key]) { return false }
}
teraType?.takeIf { it != pokemonEntity.pokemon.teraType.name }?.let { return false }
dmaxLevel?.takeIf { it != pokemonEntity.pokemon.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != pokemonEntity.pokemon.gmaxFactor }?.let { return false }
tradeable?.takeIf { it != pokemonEntity.pokemon.tradeable }?.let { return false }
originalTrainer?.takeIf { it != pokemonEntity.pokemon.originalTrainer }?.let{ return false }
originalTrainerType?.takeIf { it != pokemonEntity.pokemon.originalTrainerType }?.let{ return false }
return customProperties.none { !it.matches(pokemonEntity) }
}
// TODO Review the need for this, usage for us is in commands that deviate from the behavior established by multiple Pokémon query equivalent
fun isSubSetOf(properties: PokemonProperties): Boolean {
level?.takeIf { it != properties.level }?.let { return false }
shiny?.takeIf { it != ("shiny" in properties.aspects) }?.let { return false }
gender?.takeIf { it != properties.gender }?.let { return false }
species?.run {
try {
// TODO get context on this bit, it's highly unlikely two randoms would result in the same Pokémon, doesn't mean the prop is not equal?
val species = if (this == "random") {
PokemonSpecies.species.random()
} else {
@@ -441,9 +509,9 @@ open class PokemonProperties {
if (properties.species != species.toString()) {
return false
}
} catch (_: InvalidIdentifierException) {}
} catch (e: InvalidIdentifierException) {}
}
nickname?.takeIf { it.string != properties.nickname?.string }?.let { return false }
nickname?.takeIf { it != properties.nickname }?.let { return false }
form?.takeIf { !it.equals(properties.form, true) }?.let { return false }
friendship?.takeIf { it != properties.friendship }?.let { return false }
pokeball?.takeIf { it != properties.pokeball }?.let { return false }
@@ -483,18 +551,20 @@ open class PokemonProperties {
// TEST YOUR LUCK!
fun roll(pokemon: Pokemon) {
val baseTypes = pokemon.form.types.toList()
val baseTypes = pokemon.species.types.toList()
if (this.shiny == null) pokemon.shiny = Cobblemon.config.shinyRate.checkRate()
if (this.teraType == null) pokemon.teraType =
if (Cobblemon.config.teraTypeRate.checkRate()) TeraTypes.random(true)
else TeraTypes.forElementalType(baseTypes.random())
if (Cobblemon.config.teraTypeRate.checkRate()) ElementalTypes.all().filter { !baseTypes.contains(it) }.random()
else baseTypes.random()
}
// If the config value is at least 1, then do 1/x and use that as the property chance
private fun Float.checkRate(): Boolean = this >= 1 && (Random.Default.nextFloat() < 1 / this)
fun createEntity(world: World): PokemonEntity {
return PokemonEntity(world, create())
}
// TODO Codecs at some point
fun saveToNBT(): NbtCompound {
val nbt = NbtCompound()
originalString.let { nbt.putString(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT, it) }
@@ -502,7 +572,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.Serialization.toJsonString(it)) }
nickname?.let { nbt.putString(DataKeys.POKEMON_NICKNAME, Text.Serializer.toJson(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) }
@@ -523,14 +593,13 @@ open class PokemonProperties {
return nbt
}
// TODO Codecs at some point
fun loadFromNBT(tag: NbtCompound): PokemonProperties {
originalString = tag.getString(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT)
level = if (tag.contains(DataKeys.POKEMON_LEVEL)) tag.getInt(DataKeys.POKEMON_LEVEL) else null
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.Serialization.fromJson(tag.getString(DataKeys.POKEMON_NICKNAME)) else null
nickname = if (tag.contains(DataKeys.POKEMON_NICKNAME)) Text.Serializer.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
@@ -552,7 +621,6 @@ open class PokemonProperties {
return this
}
// TODO Codecs at some point
fun saveToJSON(): JsonObject {
val json = JsonObject()
originalString.let { json.addProperty(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT, it) }
@@ -560,7 +628,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.Serialization.toJsonString(it)) }
nickname?.let { json.addProperty(DataKeys.POKEMON_NICKNAME, Text.Serializer.toJson(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) }
@@ -582,14 +650,13 @@ open class PokemonProperties {
return json
}
// TODO Codecs at some point
fun loadFromJSON(json: JsonObject): PokemonProperties {
originalString = json.get(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT)?.asString ?: ""
level = json.get(DataKeys.POKEMON_LEVEL)?.asInt
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.Serialization.fromJson(it) }
nickname = json.get(DataKeys.POKEMON_NICKNAME)?.asString?.let { Text.Serializer.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
@@ -660,21 +727,4 @@ open class PokemonProperties {
fun copy(): PokemonProperties {
return PokemonProperties().loadFromJSON(saveToJSON())
}
// If the config value is at least 1, then do 1/x and use that as the property chance
private fun Float.checkRate(): Boolean = this >= 1 && (Random.Default.nextFloat() < 1 / this)
/**
* Attempts to find an ability by ID and resolve if it should be forced or if it's legal for the given form.
*
* @param id The literal ID of the [Ability] being mapped.
* @param form The [FormData] whose [FormData.abilities] is queried for legality.
* @return The [Ability] with the [Ability.forced] state necessary for the given [form].
*/
private fun createAbility(id: String, form: FormData): Ability? {
val ability = Abilities.get(id) ?: return null
val potentialAbility = form.abilities.firstOrNull { potential -> potential.template == ability } ?: return ability.create(true)
return potentialAbility.template.create(false)
}
}
}
@@ -48,11 +48,6 @@ fun interface PokemonPropertyExtractor {
@JvmField
val STATUS = add { pokemon, properties -> properties.status = pokemon.status?.status?.showdownName }
@JvmField
val ILLUSION = mutableListOf(SPECIES, FORM, ASPECTS, GENDER, NICKNAME, SHINY)
@JvmField
val TRANSFORM = mutableListOf(SPECIES, FORM, ASPECTS, GENDER)
fun add(extractor: PokemonPropertyExtractor): PokemonPropertyExtractor {
ALL.add(extractor)
return extractor
@@ -34,10 +34,4 @@ interface PassiveEvolution : Evolution {
return false
}
/**
* If the evolution will disappear once the conditions are no longer met.
*/
val permanent: Boolean
}
@@ -11,12 +11,10 @@ package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.substitute
import com.google.gson.JsonObject
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
/**
* A [SpeciesFeatureProvider] which is a string value selected from a fixed list of choices. Parameters exist
@@ -26,46 +24,15 @@ import net.minecraft.network.PacketByteBuf
* @since November 30th, 2022
*/
open class ChoiceSpeciesFeatureProvider(
override var keys: List<String>,
override val keys: List<String>,
var default: String? = null,
var choices: List<String> = listOf(),
var isAspect: Boolean = true,
var aspectFormat: String = "{{choice}}"
) : SynchronizedSpeciesFeatureProvider<StringSpeciesFeature>, CustomPokemonPropertyType<StringSpeciesFeature>, AspectProvider {
override var needsKey = true
override var visible = false
) : SpeciesFeatureProvider<StringSpeciesFeature>, CustomPokemonPropertyType<StringSpeciesFeature>, AspectProvider {
override val needsKey = true
fun getAspect(feature: StringSpeciesFeature) = aspectFormat.substitute("choice", feature.value)
override fun encode(buffer: PacketByteBuf) {
buffer.writeCollection(keys) { _, value -> buffer.writeString(value) }
buffer.writeNullable(default) { _, value -> buffer.writeString(value) }
buffer.writeCollection(choices) { _, value -> buffer.writeString(value) }
buffer.writeBoolean(isAspect)
buffer.writeString(aspectFormat)
buffer.writeBoolean(needsKey)
}
override fun decode(buffer: PacketByteBuf) {
keys = buffer.readList { buffer.readString() }
default = buffer.readNullable { buffer.readString() }
choices = buffer.readList { buffer.readString() }
isAspect = buffer.readBoolean()
aspectFormat = buffer.readString()
needsKey = buffer.readBoolean()
}
override fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<StringSpeciesFeature>? {
return null
}
override fun invoke(buffer: PacketByteBuf, name: String): StringSpeciesFeature? {
return if (name in keys) {
StringSpeciesFeature(name, "").also { it.decode(buffer) }
} else {
null
}
}
fun getAllAspects(): MutableList<String> {
val aspects = choices.toMutableList()
choices.forEach {
@@ -78,10 +45,10 @@ open class ChoiceSpeciesFeatureProvider(
internal constructor(): this(emptyList())
override fun get(pokemon: Pokemon) = pokemon.getFeature<StringSpeciesFeature>(keys.first())
fun get(pokemon: Pokemon) = pokemon.getFeature<StringSpeciesFeature>(keys.first())
override fun invoke(pokemon: Pokemon): StringSpeciesFeature? {
val existing = get(pokemon)
val existing = pokemon.getFeature<StringSpeciesFeature>(keys.first())
return if (existing != null && existing.value in choices) {
existing
} else {
@@ -121,7 +88,7 @@ open class ChoiceSpeciesFeatureProvider(
override fun provide(pokemon: Pokemon): Set<String> {
return if (isAspect) {
get(pokemon)?.let { setOf(getAspect(it)) } ?: emptySet()
pokemon.getFeature<StringSpeciesFeature>(keys.first())?.let { setOf(getAspect(it)) } ?: emptySet()
} else {
emptySet()
}
@@ -12,12 +12,10 @@ import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.pokemon.Pokemon
import com.google.gson.JsonObject
import kotlin.random.Random
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
/**
* A simple [SpeciesFeature] that is a true/false flag value. It implements [CustomPokemonProperty] for use in
@@ -28,7 +26,7 @@ import net.minecraft.network.PacketByteBuf
* @author Hiroku
* @since May 13th, 2022
*/
open class FlagSpeciesFeature(override val name: String) : SynchronizedSpeciesFeature, CustomPokemonProperty {
open class FlagSpeciesFeature(override val name: String) : SpeciesFeature, CustomPokemonProperty {
constructor(name: String, enabled: Boolean): this(name) {
this.enabled = enabled
}
@@ -55,14 +53,6 @@ open class FlagSpeciesFeature(override val name: String) : SynchronizedSpeciesFe
return this
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeBoolean(enabled)
}
override fun decode(buffer: PacketByteBuf) {
enabled = buffer.readBoolean()
}
override fun asString() = "$name=$enabled"
override fun apply(pokemon: Pokemon) {
@@ -81,37 +71,11 @@ open class FlagSpeciesFeature(override val name: String) : SynchronizedSpeciesFe
override fun matches(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(name)?.enabled == enabled
}
class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpeciesFeature>, CustomPokemonPropertyType<FlagSpeciesFeature>, AspectProvider {
override var keys: List<String> = emptyList()
// Uses get() = true because that way there's no backing field. It MUST be true, this way no JSON trickery will overwrite it
class FlagSpeciesFeatureProvider : SpeciesFeatureProvider<FlagSpeciesFeature>, CustomPokemonPropertyType<FlagSpeciesFeature>, AspectProvider {
override val keys: List<String>
override val needsKey get() = true
var default: String? = null
var isAspect = true
override var visible: Boolean = false
override fun invoke(buffer: PacketByteBuf, name: String): FlagSpeciesFeature? {
return if (name in keys) {
FlagSpeciesFeature(name).also { it.decode(buffer) }
} else {
null
}
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeCollection(keys) { _, value -> buffer.writeString(value) }
buffer.writeNullable(default) { _, value -> buffer.writeString(value) }
buffer.writeBoolean(isAspect)
}
override fun decode(buffer: PacketByteBuf) {
keys = buffer.readList { it.readString() }
default = buffer.readNullable { it.readString() }
isAspect = buffer.readBoolean()
}
override fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<FlagSpeciesFeature>? {
return null
}
val isAspect = true
override fun examples() = setOf("true", "false")
@@ -130,10 +94,8 @@ class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpecie
constructor(vararg keys: String) : this(keys.toList())
override fun get(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(keys.first())
override fun invoke(pokemon: Pokemon): FlagSpeciesFeature? {
return get(pokemon)
return pokemon.getFeature(keys.first())
?: when (default) {
"random" -> FlagSpeciesFeature(keys.first(), Random.Default.nextBoolean())
in setOf("true", "false") -> FlagSpeciesFeature(keys.first(), default.toBoolean())
@@ -8,17 +8,13 @@
package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.GlobalSpeciesFeatureSyncPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.SpeciesFeatureProviderAdapter
import com.cobblemon.mod.common.util.adapters.Vec3dAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
@@ -28,7 +24,6 @@ import net.minecraft.nbt.NbtCompound
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec3d
/**
* A registry very similar to [SpeciesFeatures] but the [SpeciesFeatureProvider] contained within it
@@ -47,15 +42,11 @@ object GlobalSpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
override val gson: Gson = GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(SpeciesFeatureProvider::class.java, SpeciesFeatureProviderAdapter)
.registerTypeAdapter(Vec3d::class.java, Vec3dAdapter)
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.create()
override val typeToken: TypeToken<SpeciesFeatureProvider<*>> = TypeToken.get(SpeciesFeatureProvider::class.java)
override val resourcePath: String = "global_species_features"
override fun sync(player: ServerPlayerEntity) {
player.sendPacket(GlobalSpeciesFeatureSyncPacket(codeFeatures + resourceFeatures))
}
override fun sync(player: ServerPlayerEntity) {}
override fun reload(data: Map<Identifier, SpeciesFeatureProvider<*>>) {
resourceFeatures.keys.toList().forEach(this::unregister)
@@ -68,10 +59,6 @@ object GlobalSpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
fun getFeatures() = (resourceFeatures.keys + codeFeatures.keys).mapNotNull(this::getFeature)
fun loadOnClient(entries: Collection<Map.Entry<String, SpeciesFeatureProvider<*>>>) {
codeFeatures.putAll(entries.map { it.toPair() })
}
private fun register(name: String, provider: SpeciesFeatureProvider<*>, isCoded: Boolean) {
val mapping = if (isCoded) codeFeatures else resourceFeatures
if (provider is AspectProvider) {
@@ -1,181 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.JsonObject
import com.google.gson.annotations.SerializedName
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec3d
/**
* A species feature value that's just an integer. Complex stuff.
* @author Hiroku
*/
class IntSpeciesFeature(override var name: String) : SynchronizedSpeciesFeature, CustomPokemonProperty {
var value = 0
constructor(): this("")
constructor(name: String, value: Int): this(name) {
this.value = value
}
override fun saveToNBT(pokemonNBT: NbtCompound): NbtCompound {
pokemonNBT.putInt(name, value)
return pokemonNBT
}
override fun loadFromNBT(pokemonNBT: NbtCompound): SynchronizedSpeciesFeature {
value = pokemonNBT.getInt(name)
return this
}
override fun saveToJSON(pokemonJSON: JsonObject): JsonObject {
pokemonJSON.addProperty(name, value)
return pokemonJSON
}
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature {
value = pokemonJSON.get(name).asInt
return this
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeInt(value)
}
override fun decode(buffer: PacketByteBuf) {
value = buffer.readInt()
}
override fun asString() = "$name=$value"
override fun apply(pokemon: Pokemon) {
val featureProvider = SpeciesFeatures.getFeature(name) ?: return
if (featureProvider in SpeciesFeatures.getFeaturesFor(pokemon.species)) {
val existingFeature = pokemon.getFeature<IntSpeciesFeature>(name)
if (existingFeature != null) {
existingFeature.value = value
} else {
pokemon.features.add(IntSpeciesFeature(name, value))
}
pokemon.updateAspects()
}
}
override fun matches(pokemon: Pokemon) = pokemon.getFeature<IntSpeciesFeature>(name)?.value == value
}
class IntSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<IntSpeciesFeature>, CustomPokemonPropertyType<IntSpeciesFeature> {
class DisplayData : Encodable, Decodable {
var name: String = ""
@SerializedName(value = "colour" /* fuck you we use real english */, alternate = ["color"])
var colour = Vec3d(255.0, 255.0, 255.0)
var underlay: Identifier? = null
var overlay: Identifier? = null
override fun decode(buffer: PacketByteBuf) {
name = buffer.readString()
colour = Vec3d(buffer.readDouble(), buffer.readDouble(), buffer.readDouble())
underlay = buffer.readNullable { buffer.readIdentifier() }
overlay = buffer.readNullable { buffer.readIdentifier() }
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeString(name)
buffer.writeDouble(colour.x)
buffer.writeDouble(colour.y)
buffer.writeDouble(colour.z)
buffer.writeNullable(underlay) { _, value -> buffer.writeIdentifier(value) }
buffer.writeNullable(overlay) { _, value -> buffer.writeIdentifier(value) }
}
}
override var keys = listOf<String>()
// Uses get() = true because that way there's no backing field. It MUST be true, this way no JSON trickery will overwrite it
override val needsKey get() = true
override var visible = false
var default: Int? = null
var min = 0
var max = 100
var display: DisplayData? = null
override fun fromString(value: String?) = value?.toIntOrNull()?.takeIf { it in min..max }?.let { IntSpeciesFeature(keys.first(), it) }
override fun examples() = emptyList<String>()
override fun invoke(buffer: PacketByteBuf, name: String): IntSpeciesFeature? {
return if (name in keys) {
IntSpeciesFeature(name, buffer.readInt())
} else {
null
}
}
override fun invoke(pokemon: Pokemon): IntSpeciesFeature? {
return get(pokemon) ?: default?.let { IntSpeciesFeature(keys.first(), it) }
}
override fun invoke(nbt: NbtCompound): IntSpeciesFeature? {
return if (nbt.contains(keys.first())) {
IntSpeciesFeature(keys.first(), nbt.getInt(keys.first()))
} else {
null
}
}
override fun invoke(json: JsonObject): IntSpeciesFeature? {
return if (json.has(keys.first())) {
IntSpeciesFeature(keys.first(), json.get(keys.first()).asInt)
} else {
null
}
}
override fun get(pokemon: Pokemon) = pokemon.features.filterIsInstance<IntSpeciesFeature>().find { it.name in keys }
override fun encode(buffer: PacketByteBuf) {
buffer.writeCollection(keys) { _, value -> buffer.writeString(value) }
buffer.writeNullable(default) { _, value -> buffer.writeInt(value) }
buffer.writeInt(min)
buffer.writeInt(max)
buffer.writeNullable(display) { _, value -> value.encode(buffer) }
}
override fun decode(buffer: PacketByteBuf) {
keys = buffer.readList { buffer.readString() }
default = buffer.readNullable { buffer.readInt() }
min = buffer.readInt()
max = buffer.readInt()
display = buffer.readNullable { DisplayData().also { it.decode(buffer) } }
}
override fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<IntSpeciesFeature>? {
return display?.let {
BarSummarySpeciesFeatureRenderer(
name = keys.first(),
displayName = it.name.asTranslated(),
min = min,
max = max,
colour = it.colour,
underlay = it.underlay ?: cobblemonResource("textures/gui/summary/summary_stats_other_bar.png"),
overlay = it.overlay ?: cobblemonResource("textures/gui/summary/summary_stats_generic_overlay.png"),
pokemon = pokemon
)
}
}
}
@@ -8,13 +8,10 @@
package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.SpeciesFeatureAssignmentSyncPacket
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
@@ -41,24 +38,17 @@ object SpeciesFeatureAssignments : JsonDataRegistry<SpeciesFeatureAssignment> {
override val typeToken = TypeToken.get(SpeciesFeatureAssignment::class.java)
override val resourcePath = "species_feature_assignments"
private val assignments = mutableMapOf<Identifier, MutableSet<String>>()
private val assignments = mutableMapOf<String, MutableSet<String>>()
override fun sync(player: ServerPlayerEntity) {
player.sendPacket(SpeciesFeatureAssignmentSyncPacket(assignments))
}
override fun sync(player: ServerPlayerEntity) {}
override fun reload(data: Map<Identifier, SpeciesFeatureAssignment>) {
data.values.forEach {
it.pokemon.forEach { pokemon ->
assignments.getOrPut(pokemon.asIdentifierDefaultingNamespace()) { mutableSetOf() }.addAll(it.features)
assignments.getOrPut(pokemon) { mutableSetOf() }.addAll(it.features)
}
}
this.observable.emit(this)
}
fun loadOnClient(data: Map<Identifier, MutableSet<String>>) {
this.assignments.clear()
this.assignments.putAll(data)
}
fun getFeatures(species: Species) = assignments[species.resourceIdentifier] ?: emptySet()
fun getFeatures(species: Species) = assignments[species.showdownId()] ?: emptySet()
}
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.pokemon.Pokemon
import com.google.gson.JsonObject
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
/**
* A provider of [SpeciesFeature]s. These must be registered via [SpeciesFeatures] either programmatically or
@@ -8,17 +8,13 @@
package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.StandardSpeciesFeatureSyncPacket
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.SpeciesFeatureProviderAdapter
import com.cobblemon.mod.common.util.adapters.Vec3dAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
@@ -26,7 +22,6 @@ import com.google.gson.reflect.TypeToken
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec3d
/**
* A registry for [SpeciesFeatureProvider]s. This is the backbone of species-specific data such as
@@ -53,15 +48,11 @@ object SpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
override val gson: Gson = GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(SpeciesFeatureProvider::class.java, SpeciesFeatureProviderAdapter)
.registerTypeAdapter(Vec3d::class.java, Vec3dAdapter)
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.create()
override val typeToken: TypeToken<SpeciesFeatureProvider<*>> = TypeToken.get(SpeciesFeatureProvider::class.java)
override val resourcePath: String = "species_features"
override fun sync(player: ServerPlayerEntity) {
player.sendPacket(StandardSpeciesFeatureSyncPacket(codeFeatures + resourceFeatures))
}
override fun sync(player: ServerPlayerEntity) {}
override fun reload(data: Map<Identifier, SpeciesFeatureProvider<*>>) {
resourceFeatures.keys.toList().forEach(this::unregister)
@@ -71,9 +62,6 @@ object SpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
fun getCodeFeature(name: String) = resourceFeatures[name]
fun getResourceFeature(name: String) = codeFeatures[name]
fun getFeature(name: String) = getCodeFeature(name) ?: getResourceFeature(name)
fun loadOnClient(entries: Collection<Map.Entry<String, SpeciesFeatureProvider<*>>>) {
codeFeatures.putAll(entries.map { it.toPair() })
}
fun getFeatures() = (resourceFeatures.keys + codeFeatures.keys).mapNotNull(this::getFeature)
fun getFeaturesFor(species: Species): List<SpeciesFeatureProvider<*>> {
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.pokemon.Pokemon
import com.google.gson.JsonObject
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
/**
* A species feature value that is a string value.
@@ -23,7 +22,7 @@ import net.minecraft.network.PacketByteBuf
class StringSpeciesFeature(
override val name: String,
var value: String
) : SynchronizedSpeciesFeature, CustomPokemonProperty {
) : SpeciesFeature, CustomPokemonProperty {
override fun saveToNBT(pokemonNBT: NbtCompound): NbtCompound {
pokemonNBT.putString(name, value)
return pokemonNBT
@@ -44,14 +43,6 @@ class StringSpeciesFeature(
return this
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeString(value)
}
override fun decode(buffer: PacketByteBuf) {
value = buffer.readString()
}
override fun asString() = "$name=$value"
override fun apply(pokemon: Pokemon) {
@@ -1,20 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
/**
* A species feature that can be synchronized to the client. These must be managed by a [SynchronizedSpeciesFeatureProvider].
*
* @author Hiroku
* @since November 13th, 2023
*/
interface SynchronizedSpeciesFeature : SpeciesFeature, Encodable, Decodable
@@ -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.api.pokemon.feature
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.network.PacketByteBuf
/**
* A species feature provider that will be synchronized to the client. For it to be renderable in the summary
* screen of a Pokémon it must also provide a [SummarySpeciesFeatureRenderer] in [getRenderer]. Note that you
* really don't want to initialize a renderer outside of that function call because if the server ever initializes
* it then you're going to get crashes on dedicated servers (this is a bad thing).
*
* @author Hiroku
* @since November 13th, 2023
*/
interface SynchronizedSpeciesFeatureProvider<T : SynchronizedSpeciesFeature> : SpeciesFeatureProvider<T>, Encodable, Decodable {
var visible: Boolean
operator fun invoke(buffer: PacketByteBuf, name: String): T?
/** Gets the feature from this Pokémon, if it has been created yet. */
fun get(pokemon: Pokemon): T?
/** Only run this from the client. */
fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<T>?
}
@@ -49,25 +49,6 @@ enum class Stats(override val identifier: Identifier, override val displayName:
*/
val BATTLE_ONLY: Set<Stat> = EnumSet.of(EVASION, ACCURACY)
/** Gets the [Stat] from the respective Showdown id. */
fun getStat(statKey: String) = when(statKey) {
"atk" -> ATTACK
"def" -> DEFENCE
"spa" -> SPECIAL_ATTACK
"spd" -> SPECIAL_DEFENCE
"spe" -> SPEED
"evasion" -> EVASION
else -> ACCURACY
}
/** Gets the severity lang key from the respective boost/unboost stage. */
fun getSeverity(stages: Int) = when(stages) {
0 -> "cap.single"
1 -> "slight"
2 -> "sharp"
else -> "severe"
}
}
}
@@ -20,6 +20,7 @@ import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec3d
/**
* Effect details for the actual particles of a particle effect.
@@ -28,7 +29,7 @@ import net.minecraft.util.Identifier
* @since January 4th, 2023
*/
class BedrockParticle(
var texture: Identifier = Identifier("minecraft:textures/particles/bubble.png"),
var texture: Identifier = Identifier("minecraft:textures/particle/bubble.png"),
var material: ParticleMaterial = ParticleMaterial.ALPHA,
var uvMode: ParticleUVMode = StaticParticleUVMode(),
var sizeX: Expression = NumberExpression(0.15),
@@ -43,106 +44,48 @@ class BedrockParticle(
var cameraMode: ParticleCameraMode = RotateXYZCameraMode(),
var tinting: ParticleTinting = ExpressionParticleTinting(),
var collision: ParticleCollision = ParticleCollision(),
var environmentLighting: Boolean = false,
var creationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
var expirationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
var timeline: EventTriggerTimeline = EventTriggerTimeline(mutableMapOf())
var environmentLighting: Boolean = false
) {
class ExpressionSet(
val sizeX: Expression,
val sizeY: Expression,
val maxAge: Expression,
val killExpression: Expression,
val updateExpressions: MutableList<Expression>,
val renderExpressions: MutableList<Expression>
)
/*
"creation_event": "event_sendspark",
"expiration_event": ["event_spark", "event_afterspark", "event_capturestar"],
"timeline": {
"21.00": "event_spark"
}
*/
class EventSet(
val creationEvents: MutableList<SimpleEventTrigger>,
val expirationEvents: MutableList<SimpleEventTrigger>,
val timeline: EventTriggerTimeline
)
// kill plane maybe
companion object {
val EXPRESSION_SET_CODEC = RecordCodecBuilder.create<ExpressionSet> { instance ->
instance.group(
EXPRESSION_CODEC.fieldOf("sizeX").forGetter { it.sizeX },
EXPRESSION_CODEC.fieldOf("sizeY").forGetter { it.sizeY },
EXPRESSION_CODEC.fieldOf("maxAge").forGetter { it.maxAge },
EXPRESSION_CODEC.fieldOf("killExpression").forGetter { it.killExpression },
ListCodec(EXPRESSION_CODEC).fieldOf("updateExpressions").forGetter { it.updateExpressions },
ListCodec(EXPRESSION_CODEC).fieldOf("renderExpressions").forGetter { it.renderExpressions }
).apply(instance, ::ExpressionSet)
}
val EVENT_SET_CODEC = RecordCodecBuilder.create<EventSet> { instance ->
instance.group(
ListCodec(SimpleEventTrigger.CODEC).fieldOf("creationEvents").forGetter { it.creationEvents },
ListCodec(SimpleEventTrigger.CODEC).fieldOf("expirationEvents").forGetter { it.expirationEvents },
EventTriggerTimeline.CODEC.fieldOf("timeline").forGetter { it.timeline }
).apply(instance, ::EventSet)
}
val CODEC: Codec<BedrockParticle> = RecordCodecBuilder.create { instance ->
instance.group(
Identifier.CODEC.fieldOf("texture").forGetter { it.texture },
PrimitiveCodec.STRING.fieldOf("material").forGetter { it.material.name },
ParticleUVMode.codec.fieldOf("uvMode").forGetter { it.uvMode },
EXPRESSION_SET_CODEC.fieldOf("expressionSet").forGetter {
ExpressionSet(
it.sizeX,
it.sizeY,
it.maxAge,
it.killExpression,
it.updateExpressions,
it.renderExpressions
)
},
EXPRESSION_CODEC.fieldOf("sizeX").forGetter { it.sizeX },
EXPRESSION_CODEC.fieldOf("sizeY").forGetter { it.sizeY },
EXPRESSION_CODEC.fieldOf("maxAge").forGetter { it.maxAge },
EXPRESSION_CODEC.fieldOf("killExpression").forGetter { it.killExpression },
ListCodec(EXPRESSION_CODEC).fieldOf("updateExpressions").forGetter { it.updateExpressions },
ListCodec(EXPRESSION_CODEC).fieldOf("renderExpressions").forGetter { it.renderExpressions },
ParticleMotion.codec.fieldOf("motion").forGetter { it.motion },
ParticleRotation.codec.fieldOf("rotation").forGetter { it.rotation },
ParticleViewDirection.codec.fieldOf("viewDirection").forGetter { it.viewDirection },
ParticleCameraMode.codec.fieldOf("cameraMode").forGetter { it.cameraMode },
ParticleTinting.codec.fieldOf("tinting").forGetter { it.tinting },
ParticleCollision.CODEC.fieldOf("collision").forGetter { it.collision },
PrimitiveCodec.BOOL.fieldOf("environmentLighting").forGetter { it.environmentLighting },
EVENT_SET_CODEC.fieldOf("eventSet").forGetter {
EventSet(
it.creationEvents,
it.expirationEvents,
it.timeline
)
}
PrimitiveCodec.BOOL.fieldOf("environmentLighting").forGetter { it.environmentLighting }
).apply(instance) {
texture, materialStr, uvMode, expressionSet, motion, rotation, viewDirection, cameraMode, tinting, collision, environmentLighting, eventSet ->
texture, materialStr, uvMode, sizeX, sizeY, maxAge, killExpression, updateExpressions,
renderExpressions, motion, rotation, viewDirection, cameraMode, tinting, collision, environmentLighting ->
BedrockParticle(
texture = texture,
material = ParticleMaterial.valueOf(materialStr),
uvMode = uvMode,
sizeX = expressionSet.sizeX,
sizeY = expressionSet.sizeY,
maxAge = expressionSet.maxAge,
killExpression = expressionSet.killExpression,
updateExpressions = expressionSet.updateExpressions,
renderExpressions = expressionSet.renderExpressions,
sizeX = sizeX,
sizeY = sizeY,
maxAge = maxAge,
killExpression = killExpression,
updateExpressions = updateExpressions,
renderExpressions = renderExpressions,
motion = motion,
rotation = rotation,
viewDirection = viewDirection,
cameraMode = cameraMode,
tinting = tinting,
collision = collision,
environmentLighting = environmentLighting,
creationEvents = eventSet.creationEvents,
expirationEvents = eventSet.expirationEvents,
timeline = eventSet.timeline
environmentLighting = environmentLighting
)
}
}
@@ -165,9 +108,6 @@ class BedrockParticle(
ParticleTinting.writeToBuffer(buffer, tinting)
collision.writeToBuffer(buffer)
buffer.writeBoolean(environmentLighting)
buffer.writeCollection(creationEvents) { pb, event -> event.encode(pb) }
buffer.writeCollection(expirationEvents) { pb, event -> event.encode(pb) }
timeline.encode(buffer)
}
fun readFromBuffer(buffer: PacketByteBuf) {
@@ -187,8 +127,5 @@ class BedrockParticle(
tinting = ParticleTinting.readFromBuffer(buffer)
collision.readFromBuffer(buffer)
environmentLighting = buffer.readBoolean()
creationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
expirationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
timeline.decode(buffer)
}
}
@@ -10,9 +10,7 @@ package com.cobblemon.mod.common.api.snowstorm
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.ListCodec
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import com.mojang.serialization.codecs.UnboundedMapCodec
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
@@ -29,8 +27,7 @@ class BedrockParticleEffect(
var emitter: BedrockParticleEmitter = BedrockParticleEmitter(),
var particle: BedrockParticle = BedrockParticle(),
var curves: MutableList<MoLangCurve> = mutableListOf(),
var space: ParticleSpace = ParticleSpace(),
var events: MutableMap<String, ParticleEvent> = mutableMapOf()
var space: ParticleSpace = ParticleSpace()
) {
companion object {
val CODEC: Codec<BedrockParticleEffect> = RecordCodecBuilder.create { instance ->
@@ -39,16 +36,14 @@ class BedrockParticleEffect(
BedrockParticleEmitter.CODEC.fieldOf("emitter").forGetter { it.emitter },
BedrockParticle.CODEC.fieldOf("particle").forGetter { it.particle },
ListCodec(MoLangCurve.codec).fieldOf("curves").forGetter { it.curves },
ParticleSpace.CODEC.fieldOf("space").forGetter { it.space },
UnboundedMapCodec(PrimitiveCodec.STRING, ParticleEvent.CODEC).fieldOf("events").forGetter { it.events }
).apply(instance) { id, emitter, particle, curves, space, events ->
ParticleSpace.CODEC.fieldOf("space").forGetter { it.space }
).apply(instance) { id, emitter, particle, curves, space ->
BedrockParticleEffect(
id = id,
emitter = emitter,
particle = particle,
curves = curves.toMutableList(),
space = space,
events = events
space = space
)
}
}
@@ -60,7 +55,6 @@ class BedrockParticleEffect(
particle.writeToBuffer(buffer)
buffer.writeCollection(curves) { pb, curve -> MoLangCurve.writeToBuffer(buffer, curve) }
space.writeToBuffer(buffer)
buffer.writeMap(events, { _, v -> buffer.writeString(v) }) { _, event -> event.encode(buffer) }
}
fun readFromBuffer(buffer: PacketByteBuf) {
@@ -69,6 +63,5 @@ class BedrockParticleEffect(
particle.readFromBuffer(buffer)
curves = buffer.readList { MoLangCurve.readFromBuffer(buffer) }
space.readFromBuffer(buffer)
events = buffer.readMap({ buffer.readString() }) { ParticleEvent().also { it.decode(buffer) } }
}
}
@@ -29,12 +29,7 @@ class BedrockParticleEmitter(
var updateExpressions: MutableList<Expression> = mutableListOf(),
var rate: ParticleEmitterRate = InstantParticleEmitterRate(),
var shape: ParticleEmitterShape = SphereParticleEmitterShape(),
var lifetime: ParticleEmitterLifetime = OnceEmitterLifetime(NumberExpression(1.0)),
var eventTimeline: EventTriggerTimeline = EventTriggerTimeline(mutableMapOf()),
var creationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
var expirationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
var travelDistanceEvents: EventTriggerTimeline = EventTriggerTimeline(mutableMapOf()),
var loopingTravelDistanceEvents: MutableList<LoopingTravelDistanceEventTrigger> = mutableListOf()
var lifetime: ParticleEmitterLifetime = OnceEmitterLifetime(NumberExpression(1.0))
) {
companion object {
val CODEC: Codec<BedrockParticleEmitter> = RecordCodecBuilder.create { instance ->
@@ -43,24 +38,14 @@ class BedrockParticleEmitter(
ListCodec(EXPRESSION_CODEC).fieldOf("updateExpressions").forGetter { it.updateExpressions },
ParticleEmitterRate.codec.fieldOf("rate").forGetter { it.rate },
ParticleEmitterShape.codec.fieldOf("shape").forGetter { it.shape },
ParticleEmitterLifetime.codec.fieldOf("lifetime").forGetter { it.lifetime },
EventTriggerTimeline.CODEC.fieldOf("eventTimeline").forGetter { it.eventTimeline },
ListCodec(SimpleEventTrigger.CODEC).fieldOf("creationEvents").forGetter { it.creationEvents },
ListCodec(SimpleEventTrigger.CODEC).fieldOf("expirationEvents").forGetter { it.expirationEvents },
EventTriggerTimeline.CODEC.fieldOf("travelDistanceEvents").forGetter { it.travelDistanceEvents },
ListCodec(LoopingTravelDistanceEventTrigger.CODEC).fieldOf("loopingTravelDistanceEvents").forGetter { it.loopingTravelDistanceEvents }
).apply(instance) { startExpressions, updateExpressions, rate, shape, lifetime, eventTimeline, creationEvents, expirationEvents, travelDistanceEvents, loopingTravelDistanceEvents ->
ParticleEmitterLifetime.codec.fieldOf("lifetime").forGetter { it.lifetime }
).apply(instance) { startExpressions, updateExpressions, rate, shape, lifetime ->
BedrockParticleEmitter(
startExpressions = startExpressions,
updateExpressions = updateExpressions,
shape = shape,
rate = rate,
lifetime = lifetime,
eventTimeline = eventTimeline,
creationEvents = creationEvents,
expirationEvents = expirationEvents,
travelDistanceEvents = travelDistanceEvents,
loopingTravelDistanceEvents = loopingTravelDistanceEvents
lifetime = lifetime
)
}
}
@@ -72,11 +57,6 @@ class BedrockParticleEmitter(
ParticleEmitterRate.writeToBuffer(buffer, rate)
ParticleEmitterShape.writeToBuffer(buffer, shape)
ParticleEmitterLifetime.writeToBuffer(buffer, lifetime)
eventTimeline.encode(buffer)
buffer.writeCollection(creationEvents) { pb, event -> event.encode(pb) }
buffer.writeCollection(expirationEvents) { pb, event -> event.encode(pb) }
travelDistanceEvents.encode(buffer)
buffer.writeCollection(loopingTravelDistanceEvents) { pb, event -> event.encode(pb) }
}
fun readFromBuffer(buffer: PacketByteBuf) {
@@ -85,10 +65,5 @@ class BedrockParticleEmitter(
rate = ParticleEmitterRate.readFromBuffer(buffer)
shape = ParticleEmitterShape.readFromBuffer(buffer)
lifetime = ParticleEmitterLifetime.readFromBuffer(buffer)
eventTimeline.decode(buffer)
creationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
expirationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
travelDistanceEvents.decode(buffer)
loopingTravelDistanceEvents = buffer.readList { LoopingTravelDistanceEventTrigger(0.0, mutableListOf()).also { it.decode(buffer) } }
}
}
@@ -123,8 +123,10 @@ class RotateYCameraMode : ParticleCameraMode {
viewDirection: Vec3d
): Quaternionf {
val i = if (angle == 0F) 0F else MathHelper.lerp(deltaTicks, prevAngle, angle)
val q2 = RotationAxis.POSITIVE_Y.rotationDegrees(-cameraYaw)
val q2 = RotationAxis.POSITIVE_Y.rotationDegrees(/*180 - */cameraYaw)
q2.hamiltonProduct(RotationAxis.POSITIVE_Z.rotationDegrees(i))
return q2
}
@@ -189,10 +191,9 @@ class LookAtY : ParticleCameraMode {
cameraPitch: Float,
viewDirection: Vec3d
): Quaternionf {
val i = if (angle == 0F) 0F else MathHelper.lerp(deltaTicks, prevAngle, angle)
val q2 = RotationAxis.POSITIVE_Y.rotationDegrees(-cameraYaw)
q2.hamiltonProduct(RotationAxis.POSITIVE_Z.rotationDegrees(i))
return q2
val rotation = Quaternionf(0F, 0F, 0F, 1F)
rotation.hamiltonProduct(RotationAxis.POSITIVE_Y.rotationDegrees(-cameraYaw))
return rotation
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
@@ -136,11 +136,6 @@ class LoopingEmitterLifetime(var activeTime: Expression = 1.0.asExpression(), va
val interval = activeTimeValue + sleepTime
val displacement = emitterAge % interval
runtime.environment.setSimpleVariable("emitter_lifetime", activeTime)
if (emitterAge > activeTimeValue && sleepTime == 0.0) {
return ParticleEmitterAction.STOP
}
return if (displacement < activeTimeValue) {
ParticleEmitterAction.GO
} else {
@@ -145,7 +145,7 @@ class PointParticleEmitterShape(
override val type = ParticleEmitterShapeType.POINT
override fun getNewParticlePosition(runtime: MoLangRuntime, entity: Entity?) = runtime.resolveVec3d(offset)
override fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3d {
return Vec3d.ZERO
return getNewParticlePosition(runtime, entity) // Questionable
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
@@ -1,174 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.snowstorm
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.client.render.SnowstormParticle
import com.cobblemon.mod.common.util.asExpressionLike
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.network.PacketByteBuf
import net.minecraft.sound.SoundCategory
import net.minecraft.sound.SoundEvent
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec3d
/**
* An event that can be referenced from various particle event triggers. The events are not necessarily particles,
* but can be combinations of particles, sounds, and MoLang expressions.
*
* @author Hiroku
* @since March 2nd, 2024
*/
class ParticleEvent(
var particleEffect: EventParticleEffect? = null,
var soundEffect: EventSoundEffect? = null,
var expression: ExpressionLike? = null
): Encodable, Decodable {
companion object {
val CODEC: Codec<ParticleEvent> = RecordCodecBuilder.create { instance ->
instance.group(
EventParticleEffect.CODEC.optionalFieldOf("particle_effect", null).forGetter { it.particleEffect },
EventSoundEffect.CODEC.optionalFieldOf("sound_effect", null).forGetter { it.soundEffect },
PrimitiveCodec.STRING.optionalFieldOf("expression", null).forGetter { it.expression?.toString() }
).apply(instance) { particleEffect, soundEffect, expression ->
ParticleEvent(
particleEffect,
soundEffect,
expression?.asExpressionLike()
)
}
}
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeNullable(particleEffect) { pb, effect ->
pb.writeIdentifier(effect.effect)
pb.writeEnumConstant(effect.type)
pb.writeNullable(effect.expression) { pb, expr -> pb.writeString(expr.toString()) }
}
buffer.writeNullable(soundEffect) { pb, effect -> pb.writeIdentifier(effect.sound) }
buffer.writeNullable(expression) { pb, expr -> pb.writeString(expr.toString()) }
}
override fun decode(buffer: PacketByteBuf) {
particleEffect = buffer.readNullable { pb -> EventParticleEffect(
pb.readIdentifier(),
pb.readEnumConstant(EventParticleEffect.EventParticleType::class.java),
pb.readNullable { pb.readString().asExpressionLike() }
) }
soundEffect = buffer.readNullable { pb -> EventSoundEffect(pb.readIdentifier()) }
expression = buffer.readNullable { pb -> pb.readString().asExpressionLike() }
}
fun run(storm: ParticleStorm, particle: SnowstormParticle?) {
particleEffect?.let { effect ->
val bedrockParticleEffect = BedrockParticleEffectRepository.getEffect(effect.effect) ?: return@let
val rootMatrix = when (effect.type) {
EventParticleEffect.EventParticleType.EMITTER -> MatrixWrapper().updatePosition(storm.matrixWrapper.getOrigin())
EventParticleEffect.EventParticleType.EMITTER_BOUND -> storm.matrixWrapper
EventParticleEffect.EventParticleType.PARTICLE,
EventParticleEffect.EventParticleType.PARTICLE_WITH_VELOCITY -> (particle?.let { Vec3d(it.getX(), it.getY(), it.getZ()) } ?: Vec3d(storm.getX(), storm.getY(), storm.getZ())).let { MatrixWrapper().updatePosition(it) }
}
val sourceVelocity = when (effect.type) {
EventParticleEffect.EventParticleType.EMITTER -> storm.sourceVelocity().let { { it } }
EventParticleEffect.EventParticleType.EMITTER_BOUND -> storm.sourceVelocity
EventParticleEffect.EventParticleType.PARTICLE -> { { Vec3d.ZERO } }
EventParticleEffect.EventParticleType.PARTICLE_WITH_VELOCITY -> (particle?.let { Vec3d(it.getVelocityX(), it.getVelocityY(), it.getVelocityZ()) } ?: Vec3d.ZERO).let { { it } }
}
val newStorm = ParticleStorm(
effect = bedrockParticleEffect,
matrixWrapper = rootMatrix,
world = storm.world,
sourceVelocity = sourceVelocity,
sourceAlive = storm.sourceAlive,
sourceVisible = storm.sourceVisible,
onDespawn = {},
runtime = MoLangRuntime().setup().also { it.environment.structs["query"] = storm.runtime.environment.getQueryStruct() },
entity = storm.entity
)
effect.expression?.resolve(newStorm.runtime)
newStorm.spawn()
}
soundEffect?.let { effect ->
val position = particle?.let { Vec3d(it.getX(), it.getY(), it.getZ()) } ?: Vec3d(storm.getX(), storm.getY(), storm.getZ())
val world = storm.world
val soundEvent = SoundEvent.of(effect.sound)
world.playSound(position.x, position.y, position.z, soundEvent, SoundCategory.NEUTRAL, 1F, 1F, true)
}
expression?.resolve(storm.runtime)
}
}
/**
* A particle component of a [ParticleEvent]. It contains the effect to play, the spawning type, and a pre-run
* expression to run in the new storm's runtime.
*
* @author Hiroku
* @since March 2nd, 2024
*/
class EventParticleEffect(
val effect: Identifier,
val type: EventParticleType,
val expression: ExpressionLike? = null
) {
companion object {
val CODEC = RecordCodecBuilder.create<EventParticleEffect> { instance ->
instance.group(
Identifier.CODEC.fieldOf("effect").forGetter { it.effect },
PrimitiveCodec.STRING.fieldOf("type").forGetter { it.type.name },
PrimitiveCodec.STRING.optionalFieldOf("expression", null).forGetter { it.expression?.toString() }
).apply(instance) { effect, type, expression ->
EventParticleEffect(
effect,
EventParticleType.valueOf(type),
expression?.asExpressionLike()
)
}
}
}
enum class EventParticleType {
EMITTER,
EMITTER_BOUND,
PARTICLE,
PARTICLE_WITH_VELOCITY
}
}
/**
* A simple sound effect component of a [ParticleEvent]. Plays a sound at the particle location, or if run from an
* emitter event, at the emitter's location.
*
* @author Hiroku
* @since March 2nd, 2024
*/
class EventSoundEffect(
val sound: Identifier,
) {
companion object {
val CODEC = RecordCodecBuilder.create<EventSoundEffect> { instance ->
instance.group(
Identifier.CODEC.fieldOf("sound").forGetter { it.sound }
).apply(instance, ::EventSoundEffect)
}
}
}
@@ -1,118 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.snowstorm
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.client.render.SnowstormParticle
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import com.mojang.serialization.codecs.UnboundedMapCodec
import net.minecraft.network.PacketByteBuf
/*
* The different references to events that can be triggered by an effect.
*/
/**
* It really is simple.
*
* @author Hiroku
* @since March 2nd, 2024
*/
class SimpleEventTrigger(var event: String): Encodable, Decodable {
companion object {
val CODEC = RecordCodecBuilder.create<SimpleEventTrigger> { instance ->
instance.group(
PrimitiveCodec.STRING.fieldOf("event").forGetter { it.event }
).apply(instance, ::SimpleEventTrigger)
}
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeString(event)
}
override fun decode(buffer: PacketByteBuf) {
event = buffer.readString()
}
fun trigger(storm: ParticleStorm, particle: SnowstormParticle?) {
val event = storm.effect.events[event] ?: return
event.run(storm, particle)
}
}
/**
* An event trigger that outlines specific times at which point groups of effects will play. This can be used
* on the basis of time but is also used in the case of travel distance.
*
* @author Hiroku
* @since March 2nd, 2024
*/
class EventTriggerTimeline(var map: MutableMap<Double, MutableList<String>>): Encodable, Decodable {
companion object {
val CODEC = RecordCodecBuilder.create<EventTriggerTimeline> { instance ->
instance.group(
UnboundedMapCodec(PrimitiveCodec.DOUBLE, PrimitiveCodec.STRING.listOf()).fieldOf("map").forGetter { it.map }
).apply(instance, ::EventTriggerTimeline)
}
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeMap(map, { pb, k -> pb.writeDouble(k) }, { pb, v -> pb.writeCollection(v) { _, s -> pb.writeString(s) } })
}
override fun decode(buffer: PacketByteBuf) {
map = buffer.readMap({ pb -> pb.readDouble() }, { pb -> pb.readList { pb.readString() } })
}
fun check(storm: ParticleStorm, particle: SnowstormParticle?, previousTime: Double, newTime: Double) {
val events = map.entries.filter { it.key in previousTime..newTime }.flatMap { it.value }
events.forEach { event ->
val event = storm.effect.events[event] ?: return
event.run(storm, particle)
}
}
}
/**
* An event trigger that takes the total distance travelled by the emitter and modulos it across [distance] to know
* if we need to play it again.
*
* @author Hiroku
* @since March 2nd, 2024
*/
class LoopingTravelDistanceEventTrigger(var distance: Double, var events: MutableList<String>): Encodable, Decodable {
companion object {
val CODEC = RecordCodecBuilder.create<LoopingTravelDistanceEventTrigger> { instance ->
instance.group(
PrimitiveCodec.DOUBLE.fieldOf("distance").forGetter { it.distance },
PrimitiveCodec.STRING.listOf().fieldOf("events").forGetter { it.events }
).apply(instance, ::LoopingTravelDistanceEventTrigger)
}
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeDouble(distance)
buffer.writeCollection(events) { _, s -> buffer.writeString(s) }
}
override fun decode(buffer: PacketByteBuf) {
distance = buffer.readDouble()
events = buffer.readList { buffer.readString() }
}
fun check(storm: ParticleStorm, particle: SnowstormParticle?, previousDistance: Double, currentDistance: Double) {
if (previousDistance < distance && currentDistance >= distance) {
events.mapNotNull { storm.effect.events[it] }.forEach { it.run(storm, particle) }
}
}
}
@@ -140,7 +140,7 @@ class InwardsMotionDirection : ParticleMotionDirection {
return if (particlePos == emitterPos) {
Vec3d(storm.world.random.nextDouble() - 0.5, storm.world.random.nextDouble() - 0.5, storm.world.random.nextDouble() - 0.5)
} else {
emitterPos.subtract(particlePos)
particlePos.subtract(emitterPos)
}.normalize()
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
@@ -65,7 +65,7 @@ class DynamicParticleRotation(
override val type = ParticleRotationType.DYNAMIC
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun getInitialRotation(runtime: MoLangRuntime) = runtime.resolveDouble(startRotation)
override fun getInitialAngularVelocity(runtime: MoLangRuntime) = runtime.resolveDouble(speed) / 20
override fun getInitialAngularVelocity(runtime: MoLangRuntime) = runtime.resolveDouble(speed)
override fun getAngularAcceleration(runtime: MoLangRuntime, angularVelocity: Double): Double {
val acceleration = runtime.resolveDouble(acceleration)
val nextVelocity = angularVelocity + acceleration
@@ -10,19 +10,8 @@ package com.cobblemon.mod.common.api.spawning
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.entity.Despawner
import com.cobblemon.mod.common.api.spawning.condition.AreaSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.BasicSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.GroundedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
import com.cobblemon.mod.common.api.spawning.context.GroundedSpawningContext
import com.cobblemon.mod.common.api.spawning.context.LavafloorSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SeafloorSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.context.SubmergedSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SurfaceSpawningContext
import com.cobblemon.mod.common.api.spawning.condition.*
import com.cobblemon.mod.common.api.spawning.context.*
import com.cobblemon.mod.common.api.spawning.context.calculators.GroundedSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.LavafloorSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.SeafloorSpawningContextCalculator
@@ -32,6 +21,8 @@ import com.cobblemon.mod.common.api.spawning.context.calculators.SurfaceSpawning
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.fallingstar.FallingStarSpawner
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawner
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.preset.BasicSpawnDetailPreset
import com.cobblemon.mod.common.api.spawning.preset.BestSpawnerConfig
@@ -83,6 +74,8 @@ object BestSpawner {
var config = BestSpawnerConfig()
val spawnerManagers = mutableListOf<SpawnerManager>(CobblemonWorldSpawnerManager)
var defaultPokemonDespawner: Despawner<PokemonEntity> = CobblemonAgingDespawner(getAgeTicks = { it.ticksLived })
lateinit var fishingSpawner: FishingSpawner
lateinit var starSpawner: FallingStarSpawner
fun loadConfig() {
LOGGER.info("Starting the Best Spawner...")
@@ -91,6 +84,8 @@ object BestSpawner {
SpawningCondition.register(SubmergedSpawningCondition.NAME, SubmergedSpawningCondition::class.java)
SpawningCondition.register(GroundedSpawningCondition.NAME, GroundedSpawningCondition::class.java)
SpawningCondition.register(SurfaceSpawningCondition.NAME, SurfaceSpawningCondition::class.java)
SpawningCondition.register(FishingSpawningCondition.NAME, FishingSpawningCondition::class.java)
SpawningCondition.register(FallingStarSpawningCondition.NAME, FallingStarSpawningCondition::class.java)
LOGGER.info("Loaded ${SpawningCondition.conditionTypes.size} spawning condition types.")
SpawningContextCalculator.register(GroundedSpawningContextCalculator)
@@ -104,6 +99,8 @@ object BestSpawner {
SpawningContext.register(name = "lavafloor", clazz = LavafloorSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
SpawningContext.register(name = "submerged", clazz = SubmergedSpawningContext::class.java, defaultCondition = SubmergedSpawningCondition.NAME)
SpawningContext.register(name = "surface", clazz = SurfaceSpawningContext::class.java, defaultCondition = SurfaceSpawningCondition.NAME)
SpawningContext.register(name = "fishing", clazz = FishingSpawningContext::class.java, defaultCondition = FishingSpawningCondition.NAME)
SpawningContext.register(name = "fallingstar", clazz = FallingStarSpawningContext::class.java, defaultCondition = FallingStarSpawningCondition.NAME)
LOGGER.info("Loaded ${SpawningContext.contexts.size} spawning context types.")
@@ -118,5 +115,7 @@ object BestSpawner {
fun onServerStarted() {
spawnerManagers.forEach(SpawnerManager::onServerStarted)
fishingSpawner = FishingSpawner()
starSpawner = FallingStarSpawner()
}
}
@@ -12,8 +12,6 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawnerFactory
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import java.util.UUID
import net.minecraft.server.network.ServerPlayerEntity
@@ -36,7 +34,7 @@ object CobblemonWorldSpawnerManager : SpawnerManager() {
fun onPlayerLogin(player: ServerPlayerEntity) {
// Disables spawning
if (!Cobblemon.config.enableSpawning || server()?.gameRules?.getBoolean(CobblemonGameRules.DO_POKEMON_SPAWNING) == false) {
if (!Cobblemon.config.enableSpawning) {
return;
}
@@ -12,8 +12,6 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.api.spawning.spawner.TickingSpawner
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules.DO_POKEMON_SPAWNING
/**
* A manager of various spawners. This is a class in which you should register
@@ -51,7 +49,7 @@ open class SpawnerManager {
open fun onServerTick() {
// Disables spawning
if (!Cobblemon.config.enableSpawning || server()?.gameRules?.getBoolean(DO_POKEMON_SPAWNING) == false) {
if (!Cobblemon.config.enableSpawning) {
return
}
influences.removeIf { it.isExpired() }
@@ -0,0 +1,23 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.condition
import com.cobblemon.mod.common.api.spawning.context.FallingStarSpawningContext
class FallingStarSpawningCondition: SpawningCondition<FallingStarSpawningContext>() {
override fun contextClass() = FallingStarSpawningContext::class.java
override fun fits(ctx: FallingStarSpawningContext): Boolean {
return true
}
companion object {
const val NAME = "fallingstar"
}
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.util.itemRegistry
import net.minecraft.block.Block
import net.minecraft.item.Item
/**
* A spawning condition that applies to [FishingSpawningContext]s.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class FishingSpawningCondition: SpawningCondition<FishingSpawningContext>() {
override fun contextClass() = FishingSpawningContext::class.java
var rod: RegistryLikeCondition<Item>? = null
var neededNearbyBlocks: MutableList<RegistryLikeCondition<Block>>? = null
override fun fits(ctx: FishingSpawningContext): Boolean {
if (!super.fits(ctx)) {
return false
} else if (rod != null && !rod!!.fits(ctx.rodItem ?: return false, ctx.world.itemRegistry)) {
return false
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> ctx.nearbyBlockTypes.any { cond.fits(it, ctx.blockRegistry) } }) {
return false
} else {
return true
}
}
companion object {
const val NAME = "fishing"
}
}
@@ -76,4 +76,51 @@ interface AreaContextResolver {
return contexts
}
// fun resolveFishing(
// spawner: Spawner,
// contextCalculators: List<AreaSpawningContextCalculator<*>>,
// slice: WorldSlice
// ): List<AreaSpawningContext> {
// var pos = BlockPos.Mutable(1, 2, 3)
// val input = AreaSpawningInput(spawner, pos, slice)
// val contexts = mutableListOf<AreaSpawningContext>()
//
// var x = slice.baseX
// var y = slice.baseY
// var z = slice.baseZ
//
// while (x < slice.baseX + slice.length) {
// while (y < slice.baseY + slice.height) {
// while (z < slice.baseZ + slice.width) {
// pos.set(x, y, z)
// val vec = pos.toVec3d()
// if (slice.nearbyEntityPositions.none { it.isInRange(vec, 50.0) && it != slice.cause.entity }) {
// val fittedContextCalculator = contextCalculators
// .firstOrNull { calc -> calc.fits(input) && input.spawner.influences.none { !it.isAllowedPosition(input.world, input.position, calc) } }
// if (fittedContextCalculator != null) {
// val context = fittedContextCalculator.calculate(input)
// if (context != null) {
// contexts.add(context)
// // The position BlockPos has been used in a context, editing the same one
// // will cause entities to spawn at the wrong location (buried in walls, usually)
// // I made it so that our context calculators specifically take a copy of the
// // BlockPos but it'd still be exposed in custom contexts so fixing it here too.
// pos = BlockPos.Mutable(1, 2, 3)
// input.position = pos
// }
// }
// }
// z++
// }
// y++
// z = slice.baseZ
// }
// x++
// y = slice.baseY
// z = slice.baseZ
// }
//
// return contexts
// }
}
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.context
import com.cobblemon.mod.common.api.spawning.fallingstar.FallingStarSpawnCause
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
class FallingStarSpawningContext(
cause: FallingStarSpawnCause,
world: ServerWorld,
pos: BlockPos,
influences: MutableList<SpawningInfluence>
) : TriggerSpawningContext(
cause = cause,
world = world,
position = pos,
light = world.getLightLevel(pos),
skyLight = world.getLightLevel(pos.up()),
canSeeSky = world.isSkyVisibleAllowingSea(pos),
influences = influences
) {
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.context
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.util.getBlockStates
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.block.Block
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
/**
* A spawning context that was generated for a fishing action.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class FishingSpawningContext(
cause: FishingSpawnCause,
world: ServerWorld,
pos: BlockPos,
influences: MutableList<SpawningInfluence>
) : TriggerSpawningContext(
cause = cause,
world = world,
position = pos,
light = world.getLightLevel(pos),
skyLight = world.getLightLevel(pos.up()),
canSeeSky = world.isSkyVisibleAllowingSea(pos),
influences = influences
) {
val nearbyBlocks = world.getBlockStates(Box.of(pos.toVec3d(), 10.0, 10.0, 10.0))
val nearbyBlockTypes: List<Block> by lazy { nearbyBlocks.mapNotNull { it.block }.distinct() }
val rodStack = cause.rodStack
val rodItem = cause.rodItem
}
@@ -0,0 +1,34 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.context
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
/**
* A spawning context relating to triggered spawning. Uses a single-point structure cache which is
* the only contribution of this implementation.
*
* @author Hiroku
* @since February 3rd, 2024
*/
open class TriggerSpawningContext(
override val cause: SpawnCause,
override val world: ServerWorld,
override val position: BlockPos,
override val light: Int,
override val skyLight: Int,
override val canSeeSky: Boolean,
override val influences: MutableList<SpawningInfluence>
) : SpawningContext() {
val triggerStructureCache = StructureChunkCache()
override fun getStructureCache(pos: BlockPos) = triggerStructureCache
}
@@ -30,16 +30,9 @@ abstract class SingleEntitySpawnAction<T : Entity>(
override fun run(): EntitySpawnResult? {
val e = createEntity() ?: return null
e.setPosition(ctx.position.toVec3d().add(0.5, 1.0, 0.5))
var shouldSpawn = false
CobblemonEvents.ENTITY_SPAWN.postThen(SpawnEvent(e, ctx), ifSucceeded = {
ctx.world.spawnEntity(e)
shouldSpawn = true
})
return if (shouldSpawn) {
this.entity.emit(e)
EntitySpawnResult(listOf(e))
} else null
entity.emit(e)
CobblemonEvents.ENTITY_SPAWN.postThen(SpawnEvent(e, ctx), ifSucceeded = { ctx.world.spawnEntity(e) })
return EntitySpawnResult(listOf(e))
}
@@ -0,0 +1,20 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.fallingstar
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import net.minecraft.entity.Entity
class FallingStarSpawnCause(
spawner: Spawner,
bucket: SpawnBucket,
entity: Entity?
) : SpawnCause(spawner, bucket, entity)
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.fallingstar
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.context.FallingStarSpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.spawner.TriggerSpawner
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
open class FallingStarSpawner(
override val name: String = "fallingstar",
pool: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
) : TriggerSpawner<FallingStarSpawnCause>(name, pool) {
override fun parseContext(cause: FallingStarSpawnCause, world: ServerWorld, pos: BlockPos): FallingStarSpawningContext? {
return FallingStarSpawningContext(cause, world, pos, influences)
}
}
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.fishing
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import net.minecraft.entity.Entity
import net.minecraft.item.FishingRodItem
import net.minecraft.item.ItemStack
/**
* A spawning cause that is embellished with fishing information. Could probably also
* have the bobber entity or something.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class FishingSpawnCause(
spawner: Spawner,
bucket: SpawnBucket,
entity: Entity?,
val rodStack: ItemStack
) : SpawnCause(spawner, bucket, entity) {
val rodItem = rodStack.item as? FishingRodItem
}
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.fishing
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.spawner.TriggerSpawner
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
/**
* A spawner that takes in a [FishingSpawnCause] and spawns things.
*
* @author Hiroku
* @since February 3rd, 2024
*/
open class FishingSpawner(
override val name: String = "fishing",
pool: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
) : TriggerSpawner<FishingSpawnCause>(name, pool) {
override fun parseContext(cause: FishingSpawnCause, world: ServerWorld, pos: BlockPos): FishingSpawningContext? {
// Maybe confirm that it's water we're fishing in?
val ctx = FishingSpawningContext(
cause = cause,
world = world,
pos = pos,
influences = influences
)
return ctx
}
}
@@ -1,51 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.influence
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import net.minecraft.registry.tag.BlockTags
/**
* Implements a [SpawningInfluence] that prevents Pokémon spawning on certain blocks.
* This ensures that Pokémon don't spawn in areas that could disrupt player-built structures or mechanisms.
* The influence checks the block directly above the potential spawn location; if the block is in the restricted list,
* the spawn is disallowed.
*
* @author Aethen
* @since February 20th, 2024
*/
open class RestrictedSpawnBlocksInfluence
: SpawningInfluence {
// The list of blocks that Pokémon are not allowed to spawn on.
private val restrictedBlocks = BlockTags.RAILS
/**
* Affects the spawnable status of a Pokémon spawn detail.
* If the block directly above the spawn location is in the restricted list, the spawn is disallowed.
*
* @param detail The Pokémon spawn detail to be affected.
* @param ctx The context of the spawning event.
* @return `true` if the spawn is allowed, `false` if it is disallowed.
*/
override fun affectSpawnable(detail: SpawnDetail, ctx: SpawningContext): Boolean {
// If the spawn detail is not a Pokémon, it is not affected by this influence.
if (detail.type != "pokemon") return true
// Get the position of the potential spawn location.
val pokemonSpawnPos = ctx.position
// Examine the block immediately above the spawn block to check for a rail
// Potentially need a refactor if we ever want to check the spawn block itself.
return ctx.world.getBlockState(pokemonSpawnPos.withY(pokemonSpawnPos.y + 1)).isIn(restrictedBlocks).not()
}
}
@@ -63,6 +63,9 @@ abstract class AreaSpawner(
override fun run(cause: SpawnCause): Pair<SpawningContext, SpawnDetail>? {
val area = getArea(cause)
if (area?.world?.gameRules?.getBoolean(DO_POKEMON_SPAWNING) == false) {
return null
}
val constrainedArea = if (area != null) constrainArea(area) else null
if (constrainedArea != null) {

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