Compare commits

...
Author SHA1 Message Date
plastered_crab 870397ebb9 Merge branch 'main' into 1.21.1-TMs
# Conflicts:
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
2025-09-08 21:30:19 -07:00
PolymetaandGitLab e2ccb20402 Merge branch 'hydrophobic-gates' into 'main'
Removed Hydrophobia from cobblemon gates + consistency changes

See merge request cable-mc/cobblemon!1847
2025-09-08 21:30:49 +00:00
Gertimoshka 4ddc167254 Gates no longer self destruct then open and near water at random rate, also can be ingited by lava 2025-09-08 23:22:16 +03:00
PolymetaandGitLab 56ad481304 Merge branch 'implement_pokemon' into 'main'
Implement pokemon

See merge request cable-mc/cobblemon!1842
2025-09-07 09:04:19 +00:00
PolymetaandGitLab cc7a9ae44a Merge branch 'Move-Particles-1.7' into 'main'
YOU GET PARTICLES, YOU GET PARTICLES, AND YOU GET PARTICLES. SO MANY MOVES WITH PARTICLES.

See merge request cable-mc/cobblemon!1839
2025-09-07 09:02:16 +00:00
TyzillionandGitLab 664f79f3a7 YOU GET PARTICLES, YOU GET PARTICLES, AND YOU GET PARTICLES. SO MANY MOVES WITH PARTICLES. 2025-09-07 09:02:16 +00:00
Carmendar 558907eb5c Drednaw cry and sobble also smol 2025-09-07 03:31:26 -05:00
Carmendar 370b689ab4 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-07 03:21:59 -05:00
Carmendar e2740babc5 Togetic smol 2025-09-07 03:21:15 -05:00
Lennie 3393ab17d8 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-07 09:06:05 +02:00
Lennie 645029a9c7 Fixed wings being invisible for wattrel in its battle stance 2025-09-07 09:05:41 +02:00
Carmendar f168744ccd Merge branch 'main' into implement_pokemon 2025-09-06 23:49:08 -05:00
Carmendar 67210cb5f9 Fixed Parasect crash. 2025-09-06 23:15:44 -05:00
Carmendar 779fbb4f38 Removed walking privileges from Hydreigon and Volcarona, and fixed Magnezone's magnets being too spinny. 2025-09-06 21:01:31 -05:00
LandonandGitLab ce04f546e7 Merge branch 'ride_pokemon_event' into 'main'
Molang and Flowback for Riding Pokemon

See merge request cable-mc/cobblemon!1843
2025-09-07 01:46:58 +00:00
LandonandGitLab a36437ea16 Merge branch 'riding-behaviour-tweaks-01' into 'main'
Ride behaviour tweaks based on team feedback

See merge request cable-mc/cobblemon!1844
2025-09-07 01:35:50 +00:00
Jackson BowesandGitLab aa5af84283 Ride behaviour tweaks based on team feedback 2025-09-07 01:35:50 +00:00
Konoha e3031d2618 Use 'this' in tryRidingPokemon instead of unnecessary parameter. 2025-09-06 21:34:03 -04:00
Konoha 9e44ba1fb1 Fix getRideBoost function call 2025-09-06 21:31:55 -04:00
Konoha 1af66d4175 Revert function refactoring, set rideBoosts to private. 2025-09-06 21:21:43 -04:00
Konoha 144213cee8 Remove redundant check in tryRidingPokemon 2025-09-06 21:17:40 -04:00
Konoha cc95b4450e Make set_ride_boost and add_ride_boost to case insensitive. 2025-09-06 21:04:37 -04:00
Konoha 4bda306a3b Refactor "getRideBoosts" helper to avoid signature conflict on public "rideBoosts" 2025-09-06 20:18:14 -04:00
Carmendar 2ee556dc31 Updated Bastiodon third person camera. 2025-09-06 18:21:05 -05:00
Carmendar 4457df9aba Updated Bastiodon ride overlays and fixed Magnezone portrait. 2025-09-06 18:15:47 -05:00
Konoha 6dfb073242 Add Ride Boost Molang Functions 2025-09-06 19:04:44 -04:00
Jakotens e1bd0d688f Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-06 17:00:23 -06:00
Jakotens 0bce2d8645 Fix third-person camera positions for most rideable Pokemon. 2025-09-06 17:00:14 -06:00
LandonandGitLab dfe240f1d3 Merge branch 'riding-scrape' into 'main'
More riding scrape stuff

See merge request cable-mc/cobblemon!1841
2025-09-06 22:11:16 +00:00
Landon Wills f1ac0fe99f Scrape riding updates 2025-09-06 17:27:08 -04:00
Landon Wills c2f575bf22 Add condition so boat jumps do not run land jump animations 2025-09-06 17:11:22 -04:00
Konoha 5f76966678 Implement event handling function on InteractPokemonHandler
Update licenses.
2025-09-06 17:09:14 -04:00
Carmendar beddea1ecb Merge branch 'main' into implement_pokemon 2025-09-06 16:08:48 -05:00
Carmendar 04236fcf33 Prepped Bouffalant for scrape. 2025-09-06 15:59:12 -05:00
LandonandGitLab 05e87b7faf Merge branch 'riding-scrape' into 'main'
Add new scraper files

See merge request cable-mc/cobblemon!1840
2025-09-06 20:55:34 +00:00
Landon Wills e9d298834a Merge branch 'riding-scrape' into implement_pokemon 2025-09-06 16:14:59 -04:00
Landon Wills ce113cbb80 Add new condition to poser for ride style 2025-09-06 16:11:13 -04:00
Tyzillion 61ebe7124a Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-06 16:01:04 -04:00
Tyzillion 462c0b35b8 Starly Line remodels implemented. 2025-09-06 16:00:51 -04:00
Carmendar 0b55c88ec4 Prepped Rampardos for scrape, includes placeholder controller. 2025-09-06 14:51:38 -05:00
Landon Wills 942da6b2f6 Merge branch 'implement_pokemon' into riding-scrape 2025-09-06 15:45:20 -04:00
Konoha 224c38e0a7 Ride Pokemon Event and Flowback 2025-09-06 15:36:10 -04:00
Landon Wills 056917f7df Adjust basic behaviour in scraper, run scraper 2025-09-06 15:28:15 -04:00
Carmendar 2aa9379987 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-06 14:06:16 -05:00
Carmendar 3a6a3253a3 Prepped Carracosta for scrape, removed controller from Unfezant. 2025-09-06 14:05:48 -05:00
Landon Wills 1580a06a8e Run scraper 2025-09-06 14:32:26 -04:00
Tyzillion e2bfdd3e76 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/repository/VaryingModelRepository.kt
2025-09-06 14:30:50 -04:00
Jerzykv 1d24565aa6 Changelog 2025-09-06 18:03:55 +02:00
Jerzykv 2d80402688 Added Gholdengo and Gimmighoul remodels/retextures + cosmetics 2025-09-06 16:13:13 +02:00
Carmendar 20614b6b07 Merge branch 'main' into implement_pokemon 2025-09-06 04:58:47 -05:00
Carmendar b32b5a66a4 Prepped Venusaur, Parasect and Dusknoir for scraping. Adjusted portraits for Squirtle and Wartortle. - updated changelog. 2025-09-06 04:57:38 -05:00
Carmendar ad92804fce Prepped Amaura, Bastiodon and Ursaring for Scraping. Includes placeholder controllers. 2025-09-06 01:08:35 -05:00
GenotypeXD-Dev ca93955134 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-06 00:55:37 -05:00
HirokuandGitLab 01d99676f9 Merge branch 'mo-molang-mo-problems' into 'main'
Add ability to specify file paths for MoLang stores

See merge request cable-mc/cobblemon!1744
2025-09-06 05:55:06 +00:00
GenotypeXD-Dev 2c8fd29cf1 Implemented Pikachu Cosmetics (Ash caps, Captain Cap, Hair Fluff) and added more usable leaves for hawluchas cosmetic [Detective pikachu and Partner Pikachu Tomorrow] 2025-09-06 00:55:06 -05:00
Carmendar a9eec5f568 Removed Hisuian Avalugg from evo selection. 2025-09-06 00:47:30 -05:00
Hiroku e54eaa728b Merge branch 'main' into 'mo-molang-mo-problems'
# Conflicts:
#   common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt
2025-09-06 04:17:27 +00:00
Paul 4e550151e5 Merge remote-tracking branch 'origin/main' 2025-09-06 14:16:02 +10:00
Paul 67f5259742 Pokemon that don't defend themselves will instead run for their life when hurt 2025-09-06 14:15:52 +10:00
jackowes eb783e13ba un t-pose gyarados 2025-09-05 22:33:25 -05:00
HirokuandGitLab 68237e7161 Merge branch 'Special-Candy-Recipes-2-Electric-Boogaloo' into 'main'
Special candy balancing.

See merge request cable-mc/cobblemon!1832
2025-09-06 02:52:59 +00:00
Wi2tert ec9f7665e2 Push files!! 2025-09-06 10:16:19 +08:00
Wi2tert f19ae0120e Push files!! 2025-09-06 10:07:08 +08:00
Wi2tert d674ff1d40 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-06 10:01:51 +08:00
Wi2tert 37d6f58536 "Remodels": dusknoir 2025-09-06 10:00:26 +08:00
Jakotens 2ce23e072d Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-05 19:08:15 -06:00
Jakotens d37dfee032 Updated models and animations for Sneasel, Sneasel (Hisui), Weavile, and Sneasler. Added animations for Capsakid and Scovillain. Updated changelog to reflect previous changes. 2025-09-05 19:08:08 -06:00
HirokuandGitLab ee567a63b6 Merge branch 'hero-holo' into 'main'
Data Tweaks

See merge request cable-mc/cobblemon!1836
2025-09-05 23:45:13 +00:00
dhi_holoandGitLab 868dd9cc44 Data Tweaks 2025-09-05 23:45:12 +00:00
HirokuandGitLab 3f9b0002a9 Merge branch 'FixDespawns' into 'main'
Fixed despawning blocking on empty held items, fixed forced spawns having...

See merge request cable-mc/cobblemon!1834
2025-09-05 23:44:37 +00:00
Hiroku e07b15c8fc Use named parameters where same type arguments are chained 2025-09-05 23:41:40 +00:00
HirokuandGitLab 44c5c21fb5 Merge branch 'storage-saver' into 'main'
Protect the storages!

See merge request cable-mc/cobblemon!1837
2025-09-05 23:39:27 +00:00
HirokuandGitLab 9d13e86487 Merge branch 'wild-interact' into 'main'
Prevent interact wheel on wild pokemon

See merge request cable-mc/cobblemon!1833
2025-09-05 23:26:06 +00:00
Landon Wills afa395cfeb Just mount without interact UI for passengers 2025-09-05 15:00:29 -04:00
Konoha 831903b8e3 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-05 14:58:19 -04:00
Konoha d382f48e2d Implement stubbed item evo conditions for Duraludon and Kubfu 2025-09-05 14:57:59 -04:00
Landon Wills 2b2b44a535 Merge branch 'main' into wild-interact 2025-09-05 14:16:22 -04:00
GenotypeXD-Dev ac9d85929e Implemented Articuno, Zapdos, Moltres, And Mewtwo Remodels 2025-09-05 11:29:13 -05:00
GenotypeXD-Dev 88c981bd6a Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-05 09:33:32 -05:00
GenotypeXD-Dev f0baa21b1f Fearow line, Vulpix line, And hawlucha placeholder cries 2025-09-05 09:33:13 -05:00
Neovitalism 1d5d78b953 Protect the storages! 2025-09-05 04:29:14 -05:00
Paul f771248936 oop 2025-09-05 12:05:16 +10:00
JazzMcNade 35850ea996 Merge branch 'main' into FixDespawns 2025-09-04 20:31:04 -05:00
Landon Wills caaccf0e67 Prevent mounting if pokemon is in pasture 2025-09-04 21:29:45 -04:00
JazzMcNade e5de8bd26f Fixed despawning blocking on empty held items, fixed forced spawns having persistence do to having a held item. 2025-09-04 20:28:33 -05:00
Landon Wills 9528d375f4 Prevent interact wheel on wild pokemon 2025-09-04 21:21:24 -04:00
Paul 9aac7dc8e3 Fixed crash when baiting rods on servers 2025-09-05 11:12:12 +10:00
Paul 0bebd2a71e Fixed sleepers not waking up properly in battle if they were sleeping before 2025-09-05 11:09:51 +10:00
Hiroku 55874e6a84 Merge branch 'poser-tpconvb' into 'main'
Add new offset option for third person offset + renamed the third person...

See merge request cable-mc/cobblemon!1817
2025-09-05 00:38:11 +00:00
Paul cda0e10524 Combee can herd after Vespiquen specifically (because it's funnier this way) 2025-09-05 10:24:50 +10:00
Paul 153f888cf5 Don't follow drowsy pokemon (get a designated driver smh) 2025-09-05 10:14:01 +10:00
Paul 6972b7d49d Eject passengers from pokemon the moment it starts getting recalled, combee no herd 2025-09-05 10:10:26 +10:00
Hiroku d7f442e2d0 Merge branch 'main' into 'poser-tpconvb'
# Conflicts:
#   common/src/main/resources/assets/cobblemon/bedrock/pokemon/posers/0967_cyclizar/cyclizar.json
2025-09-04 23:51:50 +00:00
Carmendar cc15c0a487 Special candy balancing. 2025-09-04 18:10:29 -05:00
Lennie 4ee628ea7a Implemented riding animations & temp ride controller for piloswine 2025-09-05 00:04:30 +02:00
Lennie 64c0001aff Implemented wing flapping sounds for volcarona 2025-09-04 23:17:55 +02:00
Lennie 320b2ff528 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-04 23:08:35 +02:00
Lennie cf8a1f5447 Implemented Mamoswine animation changes & model edits and added placeholder ride controller 2025-09-04 23:08:19 +02:00
Sterrezo 696d98f7cd Rebalanced wing flap sound volumes and properly added sounds for large chitin wings 2025-09-04 23:07:42 +02:00
Sterrezo 909b90aea1 Update gyarados_cry.ogg 2025-09-04 22:56:39 +02:00
GenotypeXD-Dev 04296ae04e Implemented Hawlucha Tweaks+Forest Champion Mask Cosmetic 2025-09-04 14:24:02 -05:00
Kenji_64 025dea4201 Dragapult remodels! (will come back for new poser and anims for Draga) 2025-09-04 15:21:00 +02:00
Sterrezo a0630ff738 Added sounds for large chitin wings and updated medium steel wing sounds 2025-09-04 14:43:57 +02:00
Tyzillion 2a7503db06 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into implement_pokemon 2025-09-02 15:55:55 -04:00
Tyzillion 1480bd97c2 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/repository/VaryingModelRepository.kt
2025-09-02 15:55:42 -04:00
JPAKx4 3bb5ecd2b6 Add new offset option for third person offset + renamed the third person offset with no view bobbing. 2025-08-31 16:59:38 -05:00
Tyzillion e14e44a1c5 Mime.Jr and Mr.Mime model updated, Mr.Mime animations updated. 2025-08-30 17:58:00 -04:00
Tyzillion 46cc276e2e Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-08-30 17:17:52 -04:00
plastered_crab 0ed413070d Merge branch 'main' into 1.21.1-TMs 2025-08-25 21:58:27 -07:00
plastered_crab f66f5ffdf9 Get TMs up to date with main 2025-08-16 20:01:12 -07:00
plastered_crab ca280a7b3e Merge branch 'main' into 1.21.1-TMs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
2025-08-16 19:45:45 -07:00
JPAKx4 50cb670eee Merge remote-tracking branch 'origin/mo-molang-mo-problems' into mo-molang-mo-problems
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt
2025-08-11 11:33:57 -05:00
JPAK cfd3eb6c4c Add ability to access a global MoLang Store (multiserver for example) 2025-08-11 11:33:27 -05:00
plastered_crab 85284bff9d Get TMs up to date with main 2025-08-10 17:01:53 -07:00
plastered_crab fa4d33f725 Merge branch 'main' into 1.21.1-TMs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/types/ElementalTypes.kt
2025-08-10 16:55:57 -07:00
plastered_crab 4ce42cdf6d edits to ElementalType.kt 2025-07-29 17:12:14 -07:00
plastered_crab 1b8562ead8 edits to ElementalType.kt 2025-07-29 16:09:25 -07:00
plastered_crab b9d9b906fb Merge branch 'main' into 1.21.1-TMs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/moves/Learnset.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/types/ElementalType.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/world/feature/CobblemonPlacedFeatures.kt
2025-07-29 08:36:31 -07:00
JPAK 38cfd7eca8 Add ability to access a global MoLang Store (multiserver for example) 2025-07-15 15:03:04 -05:00
JPAK 2ba2eea801 Add functions to remove entity and spawn NPCs for Molangs 2025-07-13 13:16:44 -05:00
plastered_crab e9d079a223 Chance to add type gem clusters to works genned Type Gem growths underground 2025-07-09 23:03:13 -07:00
plastered_crab e2e42ee91c Chance to add type gem clusters to works genned Type Gem growths underground 2025-07-09 13:16:56 -07:00
plastered_crab 4b0208201b Chance to add type gem clusters to works genned Type Gem growths underground 2025-07-09 07:19:53 -07:00
plastered_crab bec3f97733 Fix some classes 2025-07-09 06:40:24 -07:00
plastered_crab 16cfe37dac Merge branch 'refs/heads/main' into 1.21.1-TMs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
2025-07-09 05:25:07 -07:00
plastered_crab 7381da863c Merge branch 'refs/heads/main' into 1.21.1-TMs 2025-06-29 20:55:28 -07:00
Test b0902825fa teach through drag-dropping in TMM 2025-06-27 01:34:45 -04:00
Test 31c489cba0 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into 1.21.1-TMs 2025-06-25 23:30:29 -04:00
Test 6a2f21d75a merge 2025-06-25 23:30:12 -04:00
Test e2c42a57c7 Merge branch '1.21.1-TMs' of https://gitlab.com/cable-mc/cobblemon into 1.21.1-TMs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
2025-06-25 22:48:43 -04:00
Test 8592b53216 tmm refactor 2025-06-25 22:39:42 -04:00
plastered_crab 49dcf0049f Merge branch 'refs/heads/main' into 1.21.1-TMs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt
2025-06-20 06:08:06 -07:00
plastered_crab 076bee923f Merge branch 'refs/heads/main' into 1.21.1-TMs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItemComponents.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonMenuType.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
2025-06-10 18:27:00 -07:00
Plastered_Crab aacdb33827 Nevemind put it here instead 2025-05-30 10:17:02 -07:00
Plastered_Crab 53e9ece930 Player's TM datastore gets synced when their pokemon learn a move 2025-05-30 10:13:08 -07:00
plastered_crab 87f08d9bab 75% saturation on the Type colors 2025-05-29 23:43:01 -07:00
plastered_crab a50a6326af Merge remote-tracking branch 'origin/1.21.1-TMs' into 1.21.1-TMs 2025-05-29 22:32:12 -07:00
plastered_crab 3c0eec833e Type Color and TM asset change 2025-05-29 22:31:27 -07:00
Plastered_Crab 5b0cfdffcb Merge branch 'main' into 1.21.1-TMs 2025-05-29 15:11:22 -07:00
plastered_crab d788e0402e Merge remote-tracking branch 'origin/1.21.1-TMs' into 1.21.1-TMs 2025-05-28 19:04:22 -07:00
Plastered_Crab a347dc5e2a Merge branch 'main' into 1.21.1-TMs 2025-05-28 10:52:13 -07:00
plastered_crab b2fe158a7b Merge branch 'refs/heads/main' into 1.21.1-TMs 2025-05-28 09:33:57 -07:00
plastered_crab faab7e8df0 Change type gem block smelting recipes 2025-05-27 23:22:37 -07:00
plastered_crab 9070e198f1 Type gem clusters are no longer items and can't be placed. 2025-05-27 23:05:14 -07:00
plastered_crab 45b2a0db4d alter type gem clusters to drop type gems when broken 2025-05-27 21:51:16 -07:00
plastered_crab 65d39050ea Merge branch 'refs/heads/main' into 1.21.1-TMs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItemComponents.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
#	common/src/main/resources/assets/cobblemon/sounds.json
2025-05-27 19:57:54 -07:00
plastered_crab 5da16df5ae Changes to Type Gem Cluster logic 2025-05-20 23:41:42 -07:00
plastered_crab 24fc4b05ab Toast appears now when learning a new TM + Licenses 2025-05-20 22:27:44 -07:00
Plastered_Crab 173902feb1 Starting code to display Toasts when unlocking new TMs 2025-05-20 19:14:41 -07:00
Plastered_Crab fd5f08fe84 Call syncTMsFromPokemon from add method in PokemonStore class. 2025-05-20 18:46:56 -07:00
Plastered_Crab d430ad6ca1 Move the syncTMsFromPokemon method to TMMoveManager 2025-05-20 18:21:05 -07:00
Plastered_Crab 1309c18292 Player unlocks TMs by catching pokemon with those moves and TM Machine lists only player learned moves 2025-05-20 18:08:42 -07:00
plastered_crab b6deebac97 Preparing for TM Move Data store 2025-05-19 15:16:01 -07:00
Plastered_Crab 83711f2dcb Merge branch '1.21.1-TMs-(dont-be-shelvish)' into 1.21.1-TMs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
2025-04-24 14:11:35 -07:00
Plastered_Crab 18e1a767af Use new TM Shelf Block and starting to get it working 2025-04-24 14:07:38 -07:00
Polymeta 6ae3efb461 fix merge and tidy up
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-04-24 19:29:38 +01:00
Polymeta 84e1934134 Merge branch 'refs/heads/main' into 1.21.1-TMs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlockEntities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItemComponents.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/data/CobblemonDataProvider.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
#	common/src/main/resources/cobblemon-common.accesswidener
#	fabric/src/main/kotlin/com/cobblemon/mod/fabric/CobblemonFabric.kt
2025-04-24 16:58:20 +01:00
Plastered_Crab 9aed9342ee Fix atrocious formatting 2025-04-23 13:33:54 -07:00
Plastered_Crab e4d0bf39ca Added in WIP tooltips for TMs 2025-04-23 13:26:32 -07:00
plastered_crab 847fa6842e Test Bookcase can hold TMs, Upgrade, Dubious Disc, Pokedex, etc (very WIP) 2025-04-22 13:51:15 -07:00
plastered_crab 08aa4f29bd Dubious Disc and Upgrade able to be placed in the Bookshelf too 2025-04-22 10:59:35 -07:00
plastered_crab 539218381b Corrected face being mirrored 2025-04-22 09:21:45 -07:00
plastered_crab 9dff39b4d7 Shelf is no longer upside down lmao 2025-04-22 08:42:24 -07:00
plastered_crab 1e578e94e5 Shelf renders without redish color 2025-04-22 08:32:35 -07:00
plastered_crab ec35c9b9bb shelf face rendered (upside down...) 2025-04-22 07:55:36 -07:00
plastered_crab 0deaeaeb7d fit the TM case textures onto shelves better 2025-04-22 07:22:49 -07:00
plastered_crab ec3d9e1a56 Test code for placing TMs in Chiseled Bookshelves 2025-04-21 17:11:10 -07:00
Polymeta 814f84ae42 licenses
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-04-18 01:11:33 +01:00
Polymeta 904be475ef Merge branch 'refs/heads/main' into 1.21.1-TMs
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/resources/cobblemon-common.accesswidener
2025-04-18 00:44:25 +01:00
Plastered_Crab 5edecbe81b Add crafting recipes / loot tables type gems, fix collision boxes, break rules, etc 2025-04-17 15:29:42 -07:00
Plastered_Crab f357033404 Fixed 100+ recipes, fixed CraftTMPacketHandler, adjusted GUI for TM Machine, and fixed names for some moves 2025-04-17 12:27:54 -07:00
Plastered_Crab 4800ae6649 Tweaking of some TM recipes 2025-04-17 09:29:21 -07:00
plastered_crab 60b0690c1e Add remaining 10 TM recipes. now all are present for testing 2025-04-17 00:40:00 -07:00
plastered_crab 82e31dd766 Add 330+ TM recipes for testing >:) 2025-04-17 00:00:34 -07:00
Guilherme Pereira 130e767e75 force type gems to grow halfway upon world generation 2025-03-28 05:02:33 -03:00
Guilherme Pereira b1a1d509ec make type gems grow naturally 2025-03-27 19:37:46 -03:00
Plastered_Crab 0698cf495e Type Gem Clusters fill in empty space when they grow grow stunted buds when hitting max capactity 2025-03-26 16:49:00 -07:00
Plastered_Crab 6047660405 Type Gem clusters grow in the same direction as their previous state more often and also fill in gaps with clusters 2025-03-26 14:53:05 -07:00
Plastered_Crab aaf3495f83 New transitional stages for the type gem clusters and fixes for rendering issues 2025-03-26 13:09:59 -07:00
Plastered_Crab ae6529c9fd Type gem clusters grow their respective type gem blocks when placed on the core 2025-03-25 17:05:03 -07:00
plastered_crab 45a96b2a42 Prepare for new Type Gem generation for each type of gem 2025-03-19 16:07:58 -07:00
Apion 4212903c67 Merge branch 'fix-tm-automation' into '1.21.1-TMs'
General Fixes and Implementation of the TM Machine for 1.21.1

See merge request cable-mc/cobblemon!1542
2025-03-01 08:52:01 +00:00
BuildTools be47dbcede Some Requested Changes 2025-03-01 06:05:38 +02:00
Apion 9556c57c69 Fix broken accesswideners 2025-02-23 21:33:54 -08:00
BuildTools f72b65bfac Fix for TMM GUI elements being offset in wrong postion after a previus TMM GUI Fix 2025-02-22 13:13:28 +02:00
BuildTools 6121700c9b Fixed disk in TMM rendering in wrong location after fixing TMM rendering backwards 2025-02-22 12:20:52 +02:00
BuildTools 91c968e743 A better fix for a previos TMM GUI fix 2025-02-22 12:05:30 +02:00
BuildTools 8fed6f4823 Fixed TMM rendering backwards and now the TMM will eject disc from its facing direction proper 2025-02-22 10:31:43 +02:00
BuildTools 34e4b39d75 Added Properties to TMM regarding breaking the block and loot table (based of PC) 2025-02-22 09:52:58 +02:00
BuildTools a21c70ce46 Resizing TMM GUI no longer breaks the GUI Buttons 2025-02-21 23:00:52 +02:00
BuildTools 8865a71f4c TMM will now output signal when ready to craft Blank TM and will drop Filter TM on break 2025-02-21 20:32:52 +02:00
BuildTools 142cadc187 TMM can now craft blank TMs if no TM is set in the filter, as well as allow taking out the filter TM 2025-02-21 19:43:36 +02:00
BuildTools 66f4c2531d Fixed TMM GUI quick move feature to properly work as well as what is considered to be outside the GUI 2025-02-19 20:16:00 +02:00
BuildTools 3f8ddd5f7d TM Machine now turns on/off and plays sound then opened/closed 2025-02-16 22:09:07 +02:00
BuildTools c076e14cbd Made Comparator read TM Machine and it provides power if the TM Machine has the materials to autocraft 2025-02-15 23:00:32 +02:00
BuildTools d724eb068b Fixed missing tooltip render call causing tooltips over items not showing in TM Machine 2025-02-15 13:10:15 +02:00
BuildTools d3ac263e29 Made the item form of TM Machine render properly in usual cases 2025-02-15 12:32:35 +02:00
BuildTools a6b7fe1f17 TM can now finally autocraft TMs based on filtered TM by receiving redstone signal 2025-02-15 09:50:28 +02:00
BuildTools ec714ccb84 Fixed Filtering for Hopper input not working 2025-02-12 19:53:54 +02:00
BuildTools ab0a129ba1 TM Machine GUI and Inventory synced and working properly 2025-02-12 18:45:32 +02:00
Guilherme Pereira a3f272b2aa Merge branch 'main' into 1.21.1-TMs 2025-02-06 03:48:28 -03:00
Guilherme Pereira 77d91ed979 make last type gems growing from core not fully grow into blocks 2025-01-29 03:56:35 -03:00
Guilherme Pereira 9907ba17c1 update licenses 2025-01-27 06:28:51 -03:00
Guilherme Pereira a6f0e19a44 type gem core now generates more gems 2025-01-27 06:27:27 -03:00
Guilherme Pereira 7ee064b04d make type gem bud grow into a block in its final stage 2025-01-27 05:29:14 -03:00
Guilherme Pereira f2e4c735f7 add type gems 2025-01-27 05:04:04 -03:00
Guilherme Pereira 5345bd321f update licenses 2025-01-21 19:08:49 -03:00
Guilherme Pereira eab8d0063b fix tms item sprite colors 2025-01-21 19:08:00 -03:00
Guilherme Pereira 6877977234 tm branch ressurect attempt 2025-01-17 23:23:01 -03:00
plastered_crab 60cb81362e Added some small langs 2025-01-09 07:46:53 -08:00
plastered_crab 088acb8e86 Added TMs, created TechnicalMachineRegistrySyncPacket.kt, TM json files are loading, TM machine GUI working better, altered obtain methods, and more 2025-01-08 20:04:24 -08:00
plastered_crab 77f59f06cb add tm_disk.geo file 2025-01-08 10:49:41 -08:00
plastered_crab a0882c6105 TM branch lives to see another day! (Still needs some issues fixed) 2025-01-08 10:49:21 -08:00
plastered_crab d50eb52172 Merge branch 'refs/heads/main' into 1.21.1-TMs 2025-01-07 19:49:30 -08:00
Plastered_Crab 5e5cadd314 Some more changes to unported files 2024-12-26 16:34:29 -08:00
Plastered_Crab 75b06f82f0 Oops forgor two files (they were not staged oops again) 2024-12-26 16:21:04 -08:00
Plastered_Crab 2ab050aa60 Oops forgor two files 2024-12-26 16:20:25 -08:00
Plastered_Crab 6d2f645e4a More work to port over TM stuff to 1.21.1 2024-12-26 16:10:04 -08:00
Plastered_Crab 9f937e29f2 Merge branch 'main' into 1.21.1-TMs 2024-12-26 13:54:27 -08:00
Plastered_Crab abc61a96c8 Merge branch 'main' into 1.21.1-TMs 2024-12-17 10:21:05 -08:00
Plastered_Crab b1d10822d4 Merge branch 'main' into 1.21.1-TMs 2024-12-12 08:35:22 -08:00
Plastered_Crab 09d984456d Start of new branch for 1.21.1 version of TM Machine code (this was painful) 2024-11-26 15:04:32 -08:00
1895 changed files with 120140 additions and 20326 deletions
+152
View File
@@ -12,6 +12,7 @@
- Added cosmetic item functionality for Pokémon. Certain cosmetic items can be given to applicable Pokémon via the interact menu.
- Added the various log blocks as cosmetic items for Timburr and Komala.
- Added cosmetics for Spoink, Gurdurr, Conkeldurr, Squirtle Line, Sneasler, Sandile line, Treecko line, Braixen, Delphox, and Dragonite.
- Added Gilded Chest cosmetics for Chest Gimmighoul and Gholdengo
- Added `visibility/hidden`, `visibility/hat` and `visibility/face` tags to control where and how certain items are rendered.
- Added Pokémon markings, toggleable within the summary.
- Added `/boxcount` command to change PC boxes amount
@@ -38,6 +39,7 @@
- Pokémon with the ability Volt Absorb are immune to lightning damage and receive Regeneration II for a short duration
- Ground type Pokémon are immune to lightning damage
- Added functionality to Everstone when held by a Pokémon; suppresses evolution notification and hides evolve button in summary interface.
- Added optional `filePath` parameter to MoLang functions to allow for global stores (ex. `../global/`). Path must end in a forward slash.
- Added new optional property `attachment_options` for most EmitterShapes to be attached to the locator/entities scale, rotation, and/or position. Position is true by default.
- Galarica Nut bushes now generate on beaches.
- Some Pokémon now pitch their bodies in the direction they're moving.
@@ -197,6 +199,8 @@
- Tropius
- Nosepass
- Probopass
- Sneasel
- Weavile
- Sneasler
- Braixen
- Delphox
@@ -239,6 +243,7 @@
- Volcarona
- Dwebble
- Crustle
- Mr. Mime
- Flygon
- Pichu
- Pikachu
@@ -257,6 +262,21 @@
- Klinklang
- Baltoy
- Claydol
- Mamoswine
- Rufflet
- Braviary
- Girafarig
- Farigiraf
- Rookidee
- Corvisquire
- Corviknight
- Venipede
- Whirlipede
- Scolipede
- Venusaur
- Parasect
- Bastiodon
- Amaura
### Model updates for the following Pokémon
- Cleffa
@@ -335,13 +355,34 @@
- Murkrow
- Zorua
- Zoroark
- Mime.Jr
- Mr. Mime
- Pichu
- Pikachu
- Raichu
- Alolan Raichu
- Dusknoir
- Deino
- Zweilous
- Hydreigon
- Dreepy
- Drakloak
- Dragapult
- Mamoswine
- Rookidee
- Corvisquire
- Corviknight
- Venipede
- Whirlipede
- Scolipede
- Farigiraf
- Staryu
- Starmie
- Gimmighoul
- Gholdengo
- Starly
- Staravia
- Staraptor
### Changes
- Changed pokemon caught and seen count to update based on the current pokedex being looked
@@ -404,6 +445,7 @@
- Updated `PokemonSentEvent` parameters to include the Position and Level of the Pokémon being sent out.
- Updated `EvolutionCompleteEvent` parameters to include the Source Pokemon that evolved.
- Updated `HatchEggEvent.Post` to include the Pokemon that hatched.
- Roseli Berry Trees now naturally generate in their preferred biomes.
### Fixes
- Fixed game crashing when removing national pokedex using datapacks
@@ -485,6 +527,7 @@
- Fixed global species features... not working. Since they were created. Oops.
- Fixed Pokémon with alternate forms being created with an incorrect 'forced' tag on their ability.
- Fixed busted abilities and moves in Pokémon data due to removed datapacks etc. causing storage corruption. It now just rerolls their ability / uses Tackle.
- Fixed singular Pokémon corruption causing entire storage corruption. Storages will now skip corrupted Pokémon and print an error to console.
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
@@ -616,6 +659,115 @@
- Added `q.pokemon.is_rideable` Returns whether the Pokémon is rideable.
- Fixed `background` field and added `textColor` field for dialogues.
### Particles
Added new/updated particles for the following moves:
- Absorb
- Acid
- Acid Spray
- Aerial Ace
- Amnesia
- Aurora Beam
- Bite
- Body Slam
- Bubble
- Bubble Beam
- Bulldoze
- Bullet Punch
- Close Combat
- Confusion
- Crunch
- Cotton Guard
- Double Team
- Dragon Claw
- Egg Bomb
- Ember
- Eruption
- Explosion
- Fire Blast
- Fire Punch
- Fire Spin
- Flame Burst
- Flame Charge
- Flamethrower
- Flame Wheel
- Frost Breath
- Frustration
- Giga Drain
- Growl
- Haze
- Hidden Power
- Horn Attack
- Hyper Fang
- Hypnosis
- Ice Beam
- Ice Punch
- Ice Shard
- Icy Wind
- Infestation
- Kinesis
- Lava Plume
- Leech Life
- Leech Seed
- Leer
- Lick
- Magical Leaf
- Mega Drain
- Minimize
- Mist
- Mud Bomb
- MudSport
- Mystical Fire
- Nasty Plot
- Poison Fang
- Poison Gas
- Poison Powder
- Population Bomb
- Pound
- Powder
- Powder Snow
- Present
- Protect
- Psybeam
- Psychic
- Pursuit
- Quick Attack
- Rage Powder
- Razor Leaf
- Return
- Rock Throw
- Sand Attack
- Scary Face
- Scratch
- Seed Bomb
- Seismic Toss
- Self Destruct
- Shadow Ball
- Shadow Claw
- Sing
- Sleep Powder
- Sludge Bomb
- Smokescreen
- Spike
- Spore
- Stomp
- String Shot
- Stun Spore
- Super Fang
- Swords Dance
- Tackle
- Tail Whip
- Thunder
- Thunder Punch
- Thunderbolt
- Thunder Shock
- Thunder Wave
- Toxic
- Water Gun
- Water Pulse
- Water Sport
- Will-O-Wisp
- Withdraw
## [1.6.1 (January 26th, 2025)](#1-6-1)
### Additions
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.invoker;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.inventory.MenuType.MenuSupplier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(MenuType.class)
public interface MenuTypeInvoker {
@Invoker("<init>")
static <T extends AbstractContainerMenu> MenuType<T> create(MenuSupplier<T> factory, FeatureFlagSet requiredFeatures) {
throw new UnsupportedOperationException();
}
}
@@ -56,10 +56,7 @@ import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreManager
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.api.storage.player.adapter.DexDataMongoBackend
import com.cobblemon.mod.common.api.storage.player.adapter.DexDataNbtBackend
import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataJsonBackend
import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataMongoBackend
import com.cobblemon.mod.common.api.storage.player.adapter.*
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
@@ -342,8 +339,12 @@ object Cobblemon {
val pokedexNbtFactory = CachedPlayerDataStoreFactory(DexDataNbtBackend())
pokedexNbtFactory.setup(server)
val tmMoveFactory = CachedPlayerDataStoreFactory(TMMoveNbtBackend())
tmMoveFactory.setup(server)
playerDataManager.setFactory(generalJsonFactory, PlayerInstancedDataStoreTypes.GENERAL)
playerDataManager.setFactory(pokedexNbtFactory, PlayerInstancedDataStoreTypes.POKEDEX)
playerDataManager.setFactory(tmMoveFactory, PlayerInstancedDataStoreTypes.TM_MOVES)
if (config.storageFormat == "nbt") {
NBTStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
@@ -370,8 +371,14 @@ object Cobblemon {
val pokedexMongoFactory = CachedPlayerDataStoreFactory(DexDataMongoBackend(mongoClient, config.mongoDBDatabaseName, "PokeDexCollection"))
pokedexMongoFactory.setup(server)
val tmMovesMongoFactory = CachedPlayerDataStoreFactory(
TMMoveMongoBackend(mongoClient, config.mongoDBDatabaseName, "TMMovesCollection")
)
tmMovesMongoFactory.setup(server)
playerDataManager.setFactory(generalMongoFactory, PlayerInstancedDataStoreTypes.GENERAL)
playerDataManager.setFactory(pokedexMongoFactory, PlayerInstancedDataStoreTypes.POKEDEX)
playerDataManager.setFactory(tmMovesMongoFactory, PlayerInstancedDataStoreTypes.TM_MOVES)
MongoDBStoreAdapter(mongoClient, config.mongoDBDatabaseName)
} catch (e: ClassNotFoundException) {
LOGGER.error("MongoDB driver not found.")
@@ -39,6 +39,9 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
@JvmField
val LECTERN: BlockEntityType<LecternBlockEntity> = this.create("lectern", BlockEntityType.Builder.of(::LecternBlockEntity, CobblemonBlocks.LECTERN).build(null))
@JvmField
val TM_SHELF: BlockEntityType<TMShelfBlockEntity> = this.create("tm_shelf", BlockEntityType.Builder.of(::TMShelfBlockEntity, CobblemonBlocks.TM_SHELF).build(null))
@JvmField
val BERRY = this.create("berry", BlockEntityType.Builder.of(::BerryBlockEntity, *CobblemonBlocks.berries().values.toTypedArray()).build(null))
@@ -48,6 +51,8 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
val SIGN: BlockEntityType<CobblemonSignBlockEntity> = this.create("sign", BlockEntityType.Builder.of(::CobblemonSignBlockEntity, CobblemonBlocks.APRICORN_SIGN, CobblemonBlocks.APRICORN_WALL_SIGN, CobblemonBlocks.SACCHARINE_SIGN, CobblemonBlocks.SACCHARINE_WALL_SIGN).build(null))
@JvmField
val HANGING_SIGN: BlockEntityType<CobblemonHangingSignBlockEntity> = this.create("hanging_sign", BlockEntityType.Builder.of(::CobblemonHangingSignBlockEntity, CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN, CobblemonBlocks.SACCHARINE_HANGING_SIGN, CobblemonBlocks.SACCHARINE_WALL_HANGING_SIGN).build(null))
@JvmField
val TM_MACHINE: BlockEntityType<TMMachineBlockEntity> = this.create("tm_machine", BlockEntityType.Builder.of(::TMMachineBlockEntity, CobblemonBlocks.TM_MACHINE).build(null))
@JvmField
val GILDED_CHEST: BlockEntityType<GildedChestBlockEntity> = this.create("chest", BlockEntityType.Builder.of(::GildedChestBlockEntity,
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.block.sign.CobblemonHangingSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonWallHangingSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonWallSignBlock
import com.cobblemon.mod.common.block.tmmachine.TMMachineBlock
import com.cobblemon.mod.common.block.general.HorizontalRotationCarpetBlock
import com.cobblemon.mod.common.block.general.HorizontalRotationalBlock
import com.cobblemon.mod.common.mixin.invoker.*
@@ -117,18 +118,13 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val STRIPPED_APRICORN_WOOD = log("stripped_apricorn_wood")
@JvmField
val APRICORN_PLANKS = this.create("apricorn_planks",
Block(
BlockBehaviour.Properties.of().mapColor(MapColor.DIRT).instrument(NoteBlockInstrument.BASS)
.strength(2.0f, 3.0f).sound(SoundType.WOOD)
)
)
val APRICORN_PLANKS = this.create("apricorn_planks", Block(BlockBehaviour.Properties.of().mapColor(MapColor.DIRT).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
@JvmField
val APRICORN_LEAVES = leaves("apricorn_leaves")
@JvmField
val APRICORN_FENCE = this.create("apricorn_fence", FenceBlock(BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
@JvmField
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(APRICORN_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(APRICORN_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).strength(2.0f, 3.0f).sound(SoundType.WOOD).ignitedByLava().forceSolidOn()))
@JvmField
val APRICORN_BUTTON = this.create("apricorn_button", BlocksInvoker.createWoodenButtonBlock(APRICORN_BLOCK_SET_TYPE))
@JvmField
@@ -136,16 +132,10 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val APRICORN_SIGN = this.create("apricorn_sign", CobblemonSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), APRICORN_WOOD_TYPE))
@JvmField //if you wonder why we don't copy of OAK_WALL_SIGN, then I can tell you its because it uses Properties#dropsLike to point to the SIGN loot table, but we cant use that in our setup due to a deeper rooted issue, so falling back to this and writing dedicated (duplicated) loot tables
val APRICORN_WALL_SIGN = this.create(
"apricorn_wall_sign",
CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), APRICORN_WOOD_TYPE)
)
@JvmField // If you wonder why we don't copy OAK_WALL_SIGN, then I can tell you it's because it uses Properties#dropsLike to point to the SIGN loot table, but we can't use that in our setup due to a deeper rooted issue, so falling back to this and writing dedicated (duplicated) loot tables
val APRICORN_WALL_SIGN = this.create("apricorn_wall_sign", CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), APRICORN_WOOD_TYPE))
@JvmField
val APRICORN_HANGING_SIGN = this.create(
"apricorn_hanging_sign",
CobblemonHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), APRICORN_WOOD_TYPE)
)
val APRICORN_HANGING_SIGN = this.create("apricorn_hanging_sign", CobblemonHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), APRICORN_WOOD_TYPE))
@JvmField
val APRICORN_WALL_HANGING_SIGN = this.create("apricorn_wall_hanging_sign", CobblemonWallHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), APRICORN_WOOD_TYPE))
@JvmField
@@ -189,9 +179,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
val SACCHARINE_PLANKS = this.create("saccharine_planks", Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
@JvmField
val SACCHARINE_LEAVES = this.create("saccharine_leaves", SaccharineLeafBlock(
BlockBehaviour.Properties.of().mapColor(
MapColor.PLANT
).strength(0.2f).randomTicks().sound(SoundType.GRASS).noOcclusion()
BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).strength(0.2f).randomTicks().sound(SoundType.GRASS).noOcclusion()
.isValidSpawn { _: BlockState?, _: BlockGetter?, _: BlockPos?, entity: EntityType<*>? ->
entity === EntityType.OCELOT || entity === EntityType.PARROT
}.isSuffocating { _: BlockState?, _: BlockGetter?, _: BlockPos? ->
@@ -205,7 +193,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val SACCHARINE_FENCE = this.create("saccharine_fence", FenceBlock(BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
@JvmField
val SACCHARINE_FENCE_GATE = this.create("saccharine_fence_gate", FenceGateBlock(SACCHARINE_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
val SACCHARINE_FENCE_GATE = this.create("saccharine_fence_gate", FenceGateBlock(SACCHARINE_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD).ignitedByLava().forceSolidOn()))
@JvmField
val SACCHARINE_BUTTON = this.create("saccharine_button", BlocksInvoker.createWoodenButtonBlock(BlockSetType.OAK))
@JvmField
@@ -248,31 +236,31 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
val POKE_SNACK = this.create("poke_snack", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(LIT)) 3 else 0 }, true))
@JvmField
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
@JvmField
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
@JvmField
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
@JvmField
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
@JvmField
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
@JvmField
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
@JvmField
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
@JvmField
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
@JvmField
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
@JvmField
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
@JvmField
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
@JvmField
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
@JvmField
val TUMBLESTONE_BLOCK = this.create("tumblestone_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_ORANGE).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@@ -281,6 +269,51 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val BLACK_TUMBLESTONE_BLOCK = this.create("black_tumblestone_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val TYPE_GEM_CORE = this.create("type_gem_core", TypeGemCoreBlock(BlockBehaviour.Properties.of().strength(4.0f, 10.0f)))
private val typeGemBlocks = mutableMapOf<ResourceLocation, Block>()
@JvmField val TYPE_GEM_BLOCK_NORMAL = typeGemBlock("type_gem_block_normal")
@JvmField val TYPE_GEM_BLOCK_FIRE = typeGemBlock("type_gem_block_fire")
@JvmField val TYPE_GEM_BLOCK_WATER = typeGemBlock("type_gem_block_water")
@JvmField val TYPE_GEM_BLOCK_ELECTRIC = typeGemBlock("type_gem_block_electric")
@JvmField val TYPE_GEM_BLOCK_GRASS = typeGemBlock("type_gem_block_grass")
@JvmField val TYPE_GEM_BLOCK_ICE = typeGemBlock("type_gem_block_ice")
@JvmField val TYPE_GEM_BLOCK_FIGHTING = typeGemBlock("type_gem_block_fighting")
@JvmField val TYPE_GEM_BLOCK_POISON = typeGemBlock("type_gem_block_poison")
@JvmField val TYPE_GEM_BLOCK_GROUND = typeGemBlock("type_gem_block_ground")
@JvmField val TYPE_GEM_BLOCK_FLYING = typeGemBlock("type_gem_block_flying")
@JvmField val TYPE_GEM_BLOCK_PSYCHIC = typeGemBlock("type_gem_block_psychic")
@JvmField val TYPE_GEM_BLOCK_BUG = typeGemBlock("type_gem_block_bug")
@JvmField val TYPE_GEM_BLOCK_ROCK = typeGemBlock("type_gem_block_rock")
@JvmField val TYPE_GEM_BLOCK_GHOST = typeGemBlock("type_gem_block_ghost")
@JvmField val TYPE_GEM_BLOCK_DRAGON = typeGemBlock("type_gem_block_dragon")
@JvmField val TYPE_GEM_BLOCK_DARK = typeGemBlock("type_gem_block_dark")
@JvmField val TYPE_GEM_BLOCK_STEEL = typeGemBlock("type_gem_block_steel")
@JvmField val TYPE_GEM_BLOCK_FAIRY = typeGemBlock("type_gem_block_fairy")
private val typeGemClusters = mutableMapOf<ResourceLocation, TypeGemClusterBlock>()
@JvmField val TYPE_GEM_CLUSTER_NORMAL = typeGemCluster("type_gem_cluster_normal", TYPE_GEM_BLOCK_NORMAL, cobblemonResource("normal_gem"))
@JvmField val TYPE_GEM_CLUSTER_FIRE = typeGemCluster("type_gem_cluster_fire", TYPE_GEM_BLOCK_FIRE, cobblemonResource("fire_gem"))
@JvmField val TYPE_GEM_CLUSTER_WATER = typeGemCluster("type_gem_cluster_water", TYPE_GEM_BLOCK_WATER, cobblemonResource("water_gem"))
@JvmField val TYPE_GEM_CLUSTER_ELECTRIC = typeGemCluster("type_gem_cluster_electric", TYPE_GEM_BLOCK_ELECTRIC, cobblemonResource("electric_gem"))
@JvmField val TYPE_GEM_CLUSTER_GRASS = typeGemCluster("type_gem_cluster_grass", TYPE_GEM_BLOCK_GRASS, cobblemonResource("grass_gem"))
@JvmField val TYPE_GEM_CLUSTER_ICE = typeGemCluster("type_gem_cluster_ice", TYPE_GEM_BLOCK_ICE, cobblemonResource("ice_gem"))
@JvmField val TYPE_GEM_CLUSTER_FIGHTING = typeGemCluster("type_gem_cluster_fighting", TYPE_GEM_BLOCK_FIGHTING, cobblemonResource("fighting_gem"))
@JvmField val TYPE_GEM_CLUSTER_POISON = typeGemCluster("type_gem_cluster_poison", TYPE_GEM_BLOCK_POISON, cobblemonResource("poison_gem"))
@JvmField val TYPE_GEM_CLUSTER_GROUND = typeGemCluster("type_gem_cluster_ground", TYPE_GEM_BLOCK_GROUND, cobblemonResource("ground_gem"))
@JvmField val TYPE_GEM_CLUSTER_FLYING = typeGemCluster("type_gem_cluster_flying", TYPE_GEM_BLOCK_FLYING, cobblemonResource("flying_gem"))
@JvmField val TYPE_GEM_CLUSTER_PSYCHIC = typeGemCluster("type_gem_cluster_psychic", TYPE_GEM_BLOCK_PSYCHIC, cobblemonResource("psychic_gem"))
@JvmField val TYPE_GEM_CLUSTER_BUG = typeGemCluster("type_gem_cluster_bug", TYPE_GEM_BLOCK_BUG, cobblemonResource("bug_gem"))
@JvmField val TYPE_GEM_CLUSTER_ROCK = typeGemCluster("type_gem_cluster_rock", TYPE_GEM_BLOCK_ROCK, cobblemonResource("rock_gem"))
@JvmField val TYPE_GEM_CLUSTER_GHOST = typeGemCluster("type_gem_cluster_ghost", TYPE_GEM_BLOCK_GHOST, cobblemonResource("ghost_gem"))
@JvmField val TYPE_GEM_CLUSTER_DRAGON = typeGemCluster("type_gem_cluster_dragon", TYPE_GEM_BLOCK_DRAGON, cobblemonResource("dragon_gem"))
@JvmField val TYPE_GEM_CLUSTER_DARK = typeGemCluster("type_gem_cluster_dark", TYPE_GEM_BLOCK_DARK, cobblemonResource("dark_gem"))
@JvmField val TYPE_GEM_CLUSTER_STEEL = typeGemCluster("type_gem_cluster_steel", TYPE_GEM_BLOCK_STEEL, cobblemonResource("steel_gem"))
@JvmField val TYPE_GEM_CLUSTER_FAIRY = typeGemCluster("type_gem_cluster_fairy", TYPE_GEM_BLOCK_FAIRY, cobblemonResource("fairy_gem"))
@JvmField
val POLISHED_TUMBLESTONE = this.create("polished_tumblestone", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_ORANGE).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
@@ -454,7 +487,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val SOUL_CAMPFIRE = create("soul_campfire", CampfireBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.PODZOL).strength(2.0F).lightLevel{9}, true))
@JvmField
val RELIC_COIN_POUCH = create("relic_coin_pouch", CoinPouchBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.RELIC_COIN_POUCH_SOUNDS).pushReaction(PushReaction.DESTROY).noOcclusion(), true))
@JvmField
@@ -490,9 +522,25 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val PC = create("pc", PCBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if ((it.getValue(PCBlock.ON) as Boolean) && (it.getValue(PCBlock.PART) == PCBlock.PCPart.TOP)) 13 else 0 }))
val TM_MACHINE = create(
"tm_machine",
TMMachineBlock(
BlockBehaviour.Properties.of()
.mapColor(MapColor.COLOR_GREEN)
.sound(SoundType.METAL)
.pushReaction(PushReaction.BLOCK)
.strength(2F)
.noOcclusion()
)
)
@JvmField
val LECTERN = create("lectern", LecternBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.WOOD).strength(2.5F).ignitedByLava().lightLevel { if (it.getValue(LecternBlock.EMIT_LIGHT) as Boolean) 13 else 0 }))
@JvmField
val TM_SHELF = create("tm_shelf", TMShelfBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.METAL).strength(2.5F)))
@JvmField
val DISPLAY_CASE = create("display_case", DisplayCaseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F).requiresCorrectToolForDrops()))
@@ -723,4 +771,28 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
val block = BlocksInvoker.createLeavesBlock(SoundType.GRASS)
return this.create(name, block)
}
private fun typeGemBlock(name: String): Block {
val typeGemBlock = this.create(name, Block(BlockBehaviour.Properties.of().strength(3.0f, 6.0f)))
val identifier = cobblemonResource(name)
typeGemBlocks[identifier] = typeGemBlock
return typeGemBlock
}
fun typeGemBlocks() = typeGemBlocks.toMap()
private fun typeGemCluster(name: String, gemBlock: Block, dropItemId: ResourceLocation): Block {
val gemClusterBlock = this.create(name, TypeGemClusterBlock(BlockBehaviour.Properties.of().strength(2.0f, 3.0f).pushReaction(PushReaction.DESTROY), gemBlock, dropItemId))
TypeGemClusterBlock.gemToClusterMap[gemBlock] = gemClusterBlock
val identifier = cobblemonResource(name)
typeGemClusters[identifier] = gemClusterBlock
return gemClusterBlock
}
fun typeGemClusters() = typeGemClusters.toMap()
}
@@ -19,6 +19,7 @@ import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.PreparableReloadListener
import net.minecraft.tags.TagKey
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.level.GameRules
import net.minecraft.world.level.ItemLike
import net.minecraft.world.level.biome.Biome
@@ -56,6 +56,11 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
.networkSynchronized(FoodColourComponent.PACKET_CODEC)
.build())
val TM_MOVE: DataComponentType<TMMoveComponent> = create("tm_move", DataComponentType.builder<TMMoveComponent>()
.persistent(TMMoveComponent.CODEC)
.networkSynchronized(TMMoveComponent.PACKET_CODEC)
.build())
val INGREDIENT: DataComponentType<IngredientComponent> = create("ingredient", DataComponentType.builder<IngredientComponent>()
.persistent(IngredientComponent.CODEC)
.networkSynchronized(IngredientComponent.PACKET_CODEC)
@@ -83,6 +88,7 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:bait_effects"), BAIT_EFFECTS)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:flavour"), FLAVOUR)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food_colour"), FOOD_COLOUR)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:tm_move"), TM_MOVE)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ingredient"), INGREDIENT)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:mob_effects"), MOB_EFFECTS)
@@ -400,7 +400,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val OLD_GATEAU = regionalFoodItem("old_gateau", 64, 9, 0.1335f, false)
@JvmField
val CASTELIACONE = regionalFoodItem("casteliacone", 64, 5, .28f, false)
val CASTELIACONE = regionalFoodItem("casteliacone", 64, 5, 0.28f, false)
@JvmField
val LUMIOSE_GALETTE = regionalFoodItem("lumiose_galette", 64, 4, 0.4f, false)
@JvmField
@@ -457,6 +457,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val PC = blockItem("pc", CobblemonBlocks.PC)
@JvmField
val TM_MACHINE = blockItem("tm_machine", CobblemonBlocks.TM_MACHINE)
@JvmField
val TM_SHELF = blockItem("tm_shelf", CobblemonBlocks.TM_SHELF)
@JvmField
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
@JvmField
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
@@ -695,7 +699,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val SUPERB_REMEDY = compostableItem("superb_remedy", RemedyItem(RemedyItem.SUPERB), 0.85f)
@JvmField
val MOOMOO_MILK = create("moomoo_milk", CobblemonItem(Item.Properties().craftRemainder(Items.GLASS_BOTTLE)))
val MOOMOO_MILK = create("moomoo_milk", CobblemonItem(Item.Properties().craftRemainder(Items.GLASS_BOTTLE).stacksTo((16))))
@JvmField
val POTION = create("potion", PotionItem(PotionType.POTION))
@@ -1191,6 +1195,95 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val BLACK_TUMBLESTONE_BLOCK = blockItem("black_tumblestone_block", CobblemonBlocks.BLACK_TUMBLESTONE_BLOCK)
@JvmField
val TYPE_GEM_CORE = blockItem("type_gem_core", CobblemonBlocks.TYPE_GEM_CORE)
/* @JvmField
val TYPE_GEM_BLOCK = blockItem("type_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK)
@JvmField
val TYPE_GEM_CLUSTER = blockItem("type_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER)
@JvmField
val LARGE_TYPE_GEM = blockItem("large_type_gem", CobblemonBlocks.LARGE_TYPE_GEM)
@JvmField
val MEDIUM_TYPE_GEM = blockItem("medium_type_gem", CobblemonBlocks.MEDIUM_TYPE_GEM)
@JvmField
val SMALL_TYPE_GEM = blockItem("small_type_gem", CobblemonBlocks.SMALL_TYPE_GEM)*/
/*
@JvmField
val TYPE_GEM_CLUSTER_NORMAL = blockItem("type_gem_cluster_normal", CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL)
@JvmField
val TYPE_GEM_CLUSTER_FIRE = blockItem("type_gem_cluster_fire", CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE)
@JvmField
val TYPE_GEM_CLUSTER_WATER = blockItem("type_gem_cluster_water", CobblemonBlocks.TYPE_GEM_CLUSTER_WATER)
@JvmField
val TYPE_GEM_CLUSTER_ELECTRIC = blockItem("type_gem_cluster_electric", CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC)
@JvmField
val TYPE_GEM_CLUSTER_GRASS = blockItem("type_gem_cluster_grass", CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS)
@JvmField
val TYPE_GEM_CLUSTER_ICE = blockItem("type_gem_cluster_ice", CobblemonBlocks.TYPE_GEM_CLUSTER_ICE)
@JvmField
val TYPE_GEM_CLUSTER_FIGHTING = blockItem("type_gem_cluster_fighting", CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING)
@JvmField
val TYPE_GEM_CLUSTER_POISON = blockItem("type_gem_cluster_poison", CobblemonBlocks.TYPE_GEM_CLUSTER_POISON)
@JvmField
val TYPE_GEM_CLUSTER_GROUND = blockItem("type_gem_cluster_ground", CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND)
@JvmField
val TYPE_GEM_CLUSTER_FLYING = blockItem("type_gem_cluster_flying", CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING)
@JvmField
val TYPE_GEM_CLUSTER_PSYCHIC = blockItem("type_gem_cluster_psychic", CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC)
@JvmField
val TYPE_GEM_CLUSTER_BUG = blockItem("type_gem_cluster_bug", CobblemonBlocks.TYPE_GEM_CLUSTER_BUG)
@JvmField
val TYPE_GEM_CLUSTER_ROCK = blockItem("type_gem_cluster_rock", CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK)
@JvmField
val TYPE_GEM_CLUSTER_GHOST = blockItem("type_gem_cluster_ghost", CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST)
@JvmField
val TYPE_GEM_CLUSTER_DRAGON = blockItem("type_gem_cluster_dragon", CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON)
@JvmField
val TYPE_GEM_CLUSTER_DARK = blockItem("type_gem_cluster_dark", CobblemonBlocks.TYPE_GEM_CLUSTER_DARK)
@JvmField
val TYPE_GEM_CLUSTER_STEEL = blockItem("type_gem_cluster_steel", CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL)
@JvmField
val TYPE_GEM_CLUSTER_FAIRY = blockItem("type_gem_cluster_fairy", CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY)
*/
@JvmField
val TYPE_GEM_BLOCK_NORMAL = blockItem("type_gem_block_normal", CobblemonBlocks.TYPE_GEM_BLOCK_NORMAL)
@JvmField
val TYPE_GEM_BLOCK_FIRE = blockItem("type_gem_block_fire", CobblemonBlocks.TYPE_GEM_BLOCK_FIRE)
@JvmField
val TYPE_GEM_BLOCK_WATER = blockItem("type_gem_block_water", CobblemonBlocks.TYPE_GEM_BLOCK_WATER)
@JvmField
val TYPE_GEM_BLOCK_ELECTRIC = blockItem("type_gem_block_electric", CobblemonBlocks.TYPE_GEM_BLOCK_ELECTRIC)
@JvmField
val TYPE_GEM_BLOCK_GRASS = blockItem("type_gem_block_grass", CobblemonBlocks.TYPE_GEM_BLOCK_GRASS)
@JvmField
val TYPE_GEM_BLOCK_ICE = blockItem("type_gem_block_ice", CobblemonBlocks.TYPE_GEM_BLOCK_ICE)
@JvmField
val TYPE_GEM_BLOCK_FIGHTING = blockItem("type_gem_block_fighting", CobblemonBlocks.TYPE_GEM_BLOCK_FIGHTING)
@JvmField
val TYPE_GEM_BLOCK_POISON = blockItem("type_gem_block_poison", CobblemonBlocks.TYPE_GEM_BLOCK_POISON)
@JvmField
val TYPE_GEM_BLOCK_GROUND = blockItem("type_gem_block_ground", CobblemonBlocks.TYPE_GEM_BLOCK_GROUND)
@JvmField
val TYPE_GEM_BLOCK_FLYING = blockItem("type_gem_block_flying", CobblemonBlocks.TYPE_GEM_BLOCK_FLYING)
@JvmField
val TYPE_GEM_BLOCK_PSYCHIC = blockItem("type_gem_block_psychic", CobblemonBlocks.TYPE_GEM_BLOCK_PSYCHIC)
@JvmField
val TYPE_GEM_BLOCK_BUG = blockItem("type_gem_block_bug", CobblemonBlocks.TYPE_GEM_BLOCK_BUG)
@JvmField
val TYPE_GEM_BLOCK_ROCK = blockItem("type_gem_block_rock", CobblemonBlocks.TYPE_GEM_BLOCK_ROCK)
@JvmField
val TYPE_GEM_BLOCK_GHOST = blockItem("type_gem_block_ghost", CobblemonBlocks.TYPE_GEM_BLOCK_GHOST)
@JvmField
val TYPE_GEM_BLOCK_DRAGON = blockItem("type_gem_block_dragon", CobblemonBlocks.TYPE_GEM_BLOCK_DRAGON)
@JvmField
val TYPE_GEM_BLOCK_DARK = blockItem("type_gem_block_dark", CobblemonBlocks.TYPE_GEM_BLOCK_DARK)
@JvmField
val TYPE_GEM_BLOCK_STEEL = blockItem("type_gem_block_steel", CobblemonBlocks.TYPE_GEM_BLOCK_STEEL)
@JvmField
val TYPE_GEM_BLOCK_FAIRY = blockItem("type_gem_block_fairy", CobblemonBlocks.TYPE_GEM_BLOCK_FAIRY)
@JvmField
val POKEROD_SMITHING_TEMPLATE = create("pokerod_smithing_template", SmithingTemplateItem(
Component.translatable("item.minecraft.fishing_rod").blue(),
@@ -1415,44 +1508,49 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
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)
@JvmField
val TECHNICAL_MACHINE = this.create("technical_machine", TechnicalMachineItem(Properties()))
@JvmField
val BLANK_TM = this.create("blank_tm", CobblemonItem(Item.Properties()))
// Type Gems
@JvmField
val NORMAL_GEM = noSettingsItem("normal_gem")
val NORMAL_GEM = this.create("normal_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL, Item.Properties()))
@JvmField
val FIRE_GEM = noSettingsItem("fire_gem")
val FIRE_GEM = this.create("fire_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE, Item.Properties()))
@JvmField
val WATER_GEM = noSettingsItem("water_gem")
val WATER_GEM = this.create("water_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_WATER, Item.Properties()))
@JvmField
val GRASS_GEM = noSettingsItem("grass_gem")
val GRASS_GEM = this.create("grass_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS, Item.Properties()))
@JvmField
val ELECTRIC_GEM = noSettingsItem("electric_gem")
val ELECTRIC_GEM = this.create("electric_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC, Item.Properties()))
@JvmField
val ICE_GEM = noSettingsItem("ice_gem")
val ICE_GEM = this.create("ice_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_ICE, Item.Properties()))
@JvmField
val FIGHTING_GEM = noSettingsItem("fighting_gem")
val FIGHTING_GEM = this.create("fighting_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING, Item.Properties()))
@JvmField
val POISON_GEM = noSettingsItem("poison_gem")
val POISON_GEM = this.create("poison_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_POISON, Item.Properties()))
@JvmField
val GROUND_GEM = noSettingsItem("ground_gem")
val GROUND_GEM = this.create("ground_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND, Item.Properties()))
@JvmField
val FLYING_GEM = noSettingsItem("flying_gem")
val FLYING_GEM = this.create("flying_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING, Item.Properties()))
@JvmField
val PSYCHIC_GEM = noSettingsItem("psychic_gem")
val PSYCHIC_GEM = this.create("psychic_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC, Item.Properties()))
@JvmField
val BUG_GEM = noSettingsItem("bug_gem")
val BUG_GEM = this.create("bug_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_BUG, Item.Properties()))
@JvmField
val ROCK_GEM = noSettingsItem("rock_gem")
val ROCK_GEM = this.create("rock_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK, Item.Properties()))
@JvmField
val GHOST_GEM = noSettingsItem("ghost_gem")
val GHOST_GEM = this.create("ghost_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST, Item.Properties()))
@JvmField
val DRAGON_GEM = noSettingsItem("dragon_gem")
val DRAGON_GEM = this.create("dragon_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON, Item.Properties()))
@JvmField
val DARK_GEM = noSettingsItem("dark_gem")
val DARK_GEM = this.create("dark_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_DARK, Item.Properties()))
@JvmField
val STEEL_GEM = noSettingsItem("steel_gem")
val STEEL_GEM = this.create("steel_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL, Item.Properties()))
@JvmField
val FAIRY_GEM = noSettingsItem("fairy_gem")
val FAIRY_GEM = this.create("fairy_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY, Item.Properties()))
//@JvmField
//val BINDING_SOIL = blockItem("binding_soil", CobblemonBlocks.BINDING_SOIL)
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
import com.cobblemon.mod.common.platform.PlatformRegistry
import net.minecraft.core.Registry
import net.minecraft.core.registries.BuiltInRegistries
@@ -19,7 +20,8 @@ import net.minecraft.world.inventory.MenuType
object CobblemonMenuType : PlatformRegistry<Registry<MenuType<*>>, ResourceKey<Registry<MenuType<*>>>, MenuType<*>>() {
val COOKING_POT = MenuType.register("cooking_pot", ::CookingPotMenu)
val COOKING_POT = MenuType.register("cooking_pot_menu", ::CookingPotMenu)
val TM_MACHINE = MenuType.register("tm_machine_menu", ::TMMachineMenu)
override val registry: Registry<MenuType<*>>
get() = BuiltInRegistries.MENU
@@ -28,9 +28,7 @@ import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
import com.cobblemon.mod.common.client.net.effect.SaccharineLogBlockParticlesHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.PokedexUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.*
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
import com.cobblemon.mod.common.client.net.orientation.S2CUpdateOrientationHandler
@@ -132,9 +130,7 @@ import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferNotification
import com.cobblemon.mod.common.net.messages.client.trade.TradeProcessStartedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.*
import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
import com.cobblemon.mod.common.net.messages.server.BattleChallengeResponsePacket
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
@@ -151,6 +147,7 @@ import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
import com.cobblemon.mod.common.net.messages.server.behaviour.DamageOnCollisionPacket
import com.cobblemon.mod.common.net.messages.server.behaviour.SetEntityBehaviourPacket
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
import com.cobblemon.mod.common.net.messages.server.block.TMMachineTeachMovePacket
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonSelectedPacket
@@ -170,11 +167,7 @@ import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPack
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.*
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
import com.cobblemon.mod.common.net.messages.server.riding.DismountPokemonPacket
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket
@@ -202,6 +195,7 @@ import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestResponseHan
import com.cobblemon.mod.common.net.serverhandling.behaviour.DamageOnCollisionPacketHandler
import com.cobblemon.mod.common.net.serverhandling.behaviour.SetEntityBehaviourHandler
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
import com.cobblemon.mod.common.net.serverhandling.block.TMMachineTeachMoveHandler
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectedHandler
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSelectedHandler
@@ -222,6 +216,7 @@ import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandl
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.*
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
@@ -553,6 +548,7 @@ object CobblemonNetwork {
// Block entity
list.add(PacketRegisterInfo(AdjustBlockEntityViewerCountPacket.ID, AdjustBlockEntityViewerCountPacket::decode, AdjustBlockEntityViewerCountHandler))
list.add(PacketRegisterInfo(TMMachineTeachMovePacket.ID, TMMachineTeachMovePacket::decode, TMMachineTeachMoveHandler))
// Move select packets
list.add(PacketRegisterInfo(MoveSelectedPacket.ID, MoveSelectedPacket::decode, MoveSelectedHandler))
@@ -584,6 +580,10 @@ object CobblemonNetwork {
// Cooking
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
// TM Machine
list.add(PacketRegisterInfo(SetActiveTMPacket.ID, SetActiveTMPacket::decode, SetActiveTMPacketHandler))
list.add(PacketRegisterInfo(SetTMMachineContainerDataPacket.ID, SetTMMachineContainerDataPacket::decode, SetTMMachineContainerDataPacketHandler))
// Behaviour Packets
list.add(PacketRegisterInfo(SetEntityBehaviourPacket.ID, SetEntityBehaviourPacket::decode, SetEntityBehaviourHandler))
list.add(PacketRegisterInfo(DamageOnCollisionPacket.ID, DamageOnCollisionPacket::decode, DamageOnCollisionPacketHandler))
@@ -195,6 +195,22 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val TUMBLESTONE_STEP = this.create("block.tumblestone.step")
@JvmField
val TM_USE = this.create("item.tm.use")
@JvmField
val TM_MACHINE_CRAFT = this.create("block.tm_machine.craft")
@JvmField
val TM_MACHINE_OFF = this.create("block.tm_machine.off")
@JvmField
val TM_MACHINE_ON = this.create("block.tm_machine.on")
@JvmField
val TM_MACHINE_OPEN = this.create("block.tm_machine.open")
@JvmField
val TM_MACHINE_CLOSE = this.create("block.tm_machine.close")
@JvmField
val TM_MACHINE_PLACE_DISC = this.create("block.tm_machine.place_disc")
@JvmField
val EVOLUTION_STONE_BLOCK_BREAK = this.create("block.evolution_stone_block.break")
@JvmField
@@ -14,7 +14,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.advancements.critereon.ContextAwarePredicate
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerPlayer
import java.util.Optional
import java.util.*
class PlantTumblestoneContext(val pos: BlockPos, val block: TumblestoneBlock)
@@ -29,42 +29,6 @@ class PlantTumblestoneCriterion(
}
override fun matches(player: ServerPlayer, context: PlantTumblestoneContext): Boolean {
return context.block.canGrow(context.pos, player.level())
return context.block.canGrow(player.level().getBlockState(context.pos), context.pos, player.level())
}
}
///**
// * 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: ServerPlayer, context: PlantTumblestoneContext): Boolean {
// return context.tumbleStoneBlock.canGrow(context.pos, player.level())
// }
//}
//
///**
// * 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)
}
@@ -117,7 +117,7 @@ Example behaviour (`uses_healing_machine.json`):
"configurations": [
{
"type": "add_tasks_to_activity",
"activity": "idle",
"activity": "minecraft:idle",
"tasksByPriority": {
"1": [
"heal_using_healing_machine",
@@ -53,7 +53,8 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
it is PokemonEntity &&
it != currentLeader &&
it.pokemon.storeCoordinates.get()?.store !is PartyStore &&
it.getHerdSize() < it.behaviour.herd.maxSize
it.getHerdSize() < it.behaviour.herd.maxSize &&
!it.brain.hasMemoryValue(CobblemonMemories.POKEMON_DROWSY)
}.forEach { possibleLeader ->
possibleLeader as PokemonEntity
val matchingHerdLeader = entity.behaviour.herd.bestMatchLeader(entity, possibleLeader) ?: return@forEach
@@ -9,44 +9,36 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_DESIRED_DISTANCE
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_SPEED_MULTIPLIER
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.util.withQueryValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.ai.FleeFromAttackerTask
import com.cobblemon.mod.common.util.asExpression
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.SensorType
class FleeAttackerTaskConfig : SingleTaskConfig {
var condition = booleanVariable(SharedEntityVariables.FEAR_CATEGORY, "flee_attacker", true).asExpressible()
var speedMultiplier = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_SPEED_MULTIPLIER, 0.5).asExpressible()
var desiredDistance = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_DESIRED_DISTANCE, 9).asExpressible()
val avoidDurationTicks: ExpressionOrEntityVariable = Either.left("600".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
condition,
speedMultiplier,
desiredDistance
).asVariables()
override fun getVariables(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): List<MoLangConfigVariable> {
return listOf(avoidDurationTicks).asVariables()
}
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime) || entity !is PathfinderMob) return null
behaviourConfigurationContext.addMemories(MemoryModuleType.HURT_BY_ENTITY, MemoryModuleType.WALK_TARGET)
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
val speedMultiplier = speedMultiplier.resolveFloat(behaviourConfigurationContext.runtime)
val desiredDistance = desiredDistance.resolveInt(behaviourConfigurationContext.runtime)
return WrapperLivingEntityTask(
SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, speedMultiplier, desiredDistance, false),
PathfinderMob::class.java
): BehaviorControl<in LivingEntity> {
behaviourConfigurationContext.addMemories(
MemoryModuleType.HURT_BY_ENTITY,
MemoryModuleType.AVOID_TARGET
)
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
return FleeFromAttackerTask.create(avoidDurationTicks.asExpression())
}
}
@@ -162,11 +162,11 @@ on the entity type and this piggy-backs off the Minecraft list of aggressive mob
The 'calm_down' type creates a task that will make the entity calm down when it is no longer being hurt and is far from hostiles and its attacker.
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
### flee_attacker
The 'flee_attacker' type creates a task that will make the entity flee from its attacker when it is hurt.
### walk_away_from_avoid_target
The 'flee_attacker' type creates a task that will make the entity flee from its avoid target.
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the attacker. Defaults to 0.5 which is an elevated speed to usual.
- `desiredDistance`: A MoLang expression that determines the distance from the attacker that the entity should try to maintain. Defaults to 9.
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the avoid target. Defaults to 0.5 which is an elevated speed to usual.
- `desiredDistance`: A MoLang expression that determines the distance from the target that the entity should try to maintain. Defaults to 9.
### flee_nearest_hostile
The 'flee_nearest_hostile' type creates a task that will make the entity flee from the nearest hostile entity when it is hurt.
@@ -78,8 +78,9 @@ interface TaskConfig {
cobblemonResource("switch_to_panic_when_hurt") to SwitchToPanicWhenHurtTaskConfig::class.java,
cobblemonResource("switch_to_panic_when_hostiles_nearby") to SwitchToPanicWhenHostilesNearbyTaskConfig::class.java,
cobblemonResource("calm_down") to CalmDownTaskConfig::class.java,
cobblemonResource("flee_attacker") to FleeAttackerTaskConfig::class.java,
cobblemonResource("walk_away_from_avoid_target") to WalkAwayFromAvoidTargetTaskConfig::class.java,
cobblemonResource("flee_nearest_hostile") to FleeNearestHostileTaskConfig::class.java,
cobblemonResource("flee_attacker") to FleeAttackerTaskConfig::class.java,
cobblemonResource("fly_in_circles") to FlyInCirclesTaskConfig::class.java,
cobblemonResource("run_script") to RunScript::class.java,
cobblemonResource("look_in_direction") to LookInDirectionTaskConfig::class.java,
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_DESIRED_DISTANCE
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_SPEED_MULTIPLIER
import com.cobblemon.mod.common.util.asExpression
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.SensorType
class WalkAwayFromAvoidTargetTaskConfig : SingleTaskConfig {
var condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
var speedMultiplier = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_SPEED_MULTIPLIER, 0.7).asExpressible()
var desiredDistance = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_DESIRED_DISTANCE, 9).asExpressible()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
condition,
speedMultiplier,
desiredDistance
).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime) || entity !is PathfinderMob) return null
behaviourConfigurationContext.addMemories(MemoryModuleType.HURT_BY_ENTITY, MemoryModuleType.WALK_TARGET)
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
val speedMultiplier = speedMultiplier.resolveFloat(behaviourConfigurationContext.runtime)
val desiredDistance = desiredDistance.resolveInt(behaviourConfigurationContext.runtime)
return WrapperLivingEntityTask(
SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, speedMultiplier, desiredDistance, false),
PathfinderMob::class.java
)
}
}
@@ -149,6 +149,12 @@ object CobblemonEvents {
@JvmField
val TRADE_EVENT_POST = EventObservable<TradeEvent.Post>()
@JvmField
val RIDE_EVENT_PRE = CancelableObservable<RidePokemonEvent.Pre>()
@JvmField
val RIDE_EVENT_POST = EventObservable<RidePokemonEvent.Post>()
@JvmField
val LEVEL_UP_EVENT = EventObservable<LevelUpEvent>()
@@ -8,11 +8,9 @@
package com.cobblemon.mod.common.api.events.fishing
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.reactive.CancelableObservable
import com.cobblemon.mod.common.util.server
import net.minecraft.world.item.ItemStack
@@ -22,10 +20,12 @@ import net.minecraft.world.item.ItemStack
* @param bait The ItemStack of the bait that is set on the rod.
*/
class BaitSetEvent(val rod: ItemStack, val bait: ItemStack) : Cancelable() {
val context = mutableMapOf(
"rod" to rod.asMoLangValue(server()!!.registryAccess()),
"bait" to bait.asMoLangValue(server()!!.registryAccess())
)
val context = server()?.let { server ->
mutableMapOf(
"rod" to rod.asMoLangValue(server.registryAccess()),
"bait" to bait.asMoLangValue(server.registryAccess())
)
} ?: emptyMap()
val functions = moLangFunctionMap(
cancelFunc
)
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.util.server
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.server.level.ServerPlayer
/**
* The base for all events related to riding a Pokemon.
*
* @see [Pre]
* @see [Post]
*/
interface RidePokemonEvent {
val player: ServerPlayer
val pokemon: PokemonEntity
class Pre(
override val player: ServerPlayer,
override val pokemon: PokemonEntity
) : RidePokemonEvent, Cancelable() {
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"pokemon" to pokemon.struct
)
val functions = moLangFunctionMap(
cancelFunc
)
}
class Post(
override val player: ServerPlayer,
override val pokemon: PokemonEntity
) : RidePokemonEvent {
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"pokemon" to pokemon.struct
)
}
}
@@ -58,6 +58,7 @@ import com.cobblemon.mod.common.api.pokemon.experience.SidemodExperienceSource
import com.cobblemon.mod.common.api.pokemon.moves.LearnsetQuery
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.riding.Rideable
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
import com.cobblemon.mod.common.api.scheduling.Schedulable
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
@@ -488,27 +489,20 @@ object MoLangFunctions {
}
}
map.put("spawn_npc") { params ->
val x = params.getInt(0)
val y = params.getInt(1)
val z = params.getInt(2)
val npcClass = params.getString(3)
val pos = BlockPos(x, y, z)
if (!Level.isInSpawnableBounds(pos)) {
return@put DoubleValue.ZERO
}
val npc = NPCEntity(world) ?: return@put DoubleValue.ZERO
npc.moveTo(pos, npc.yRot, npc.xRot)
npc.npc = NPCClasses.getByIdentifier(npcClass.asIdentifierDefaultingNamespace()) ?: return@put DoubleValue.ZERO
npc.initialize(1)
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
val npcClass = params.getStringOrNull(3)?.let { NPCClasses.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
val level = params.getInt(4)
if(npcClass == null) return@put DoubleValue.ZERO
val npc = NPCEntity(world)
npc.moveTo(x, y, z, npc.yRot, npc.xRot)
npc.npc = npcClass
npc.initialize(level)
if (world.addFreshEntity(npc)) {
return@put npc.struct
} else {
return@put DoubleValue.ZERO
return@put npc.asMoLangValue()
}
return@put DoubleValue.ZERO
}
map.put("play_sound_on_server") { params ->
val sound = params.getString(0).asResource()
@@ -682,13 +676,13 @@ object MoLangFunctions {
map.put("party") { player.party().struct }
map.put("pc") { player.pc().struct }
map.put("has_permission") { params -> DoubleValue(Cobblemon.permissionValidator.hasPermission(player, params.getString(0), params.getIntOrNull(1) ?: 4)) }
map.put("data") { _ -> Cobblemon.molangData.load(player.uuid) }
map.put("save_data") { _ -> Cobblemon.molangData.save(player.uuid) }
map.put("data") { params -> Cobblemon.molangData.load(player.uuid, params.getStringOrNull(0)) }
map.put("save_data") { params -> Cobblemon.molangData.save(player.uuid, params.getStringOrNull(0)) }
map.put("in_battle") { DoubleValue(player.isInBattle()) }
map.put("battle") { player.getBattleState()?.first?.struct ?: DoubleValue.ZERO }
map.put("get_npc_data") { params ->
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
val data = Cobblemon.molangData.load(player.uuid)
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID ?: params.getString(0)
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(1))
if (data.map.containsKey(npcId)) {
return@put data.map[npcId]!!
} else {
@@ -698,9 +692,9 @@ object MoLangFunctions {
}
}
map.put("get_npc_variable") { params ->
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID ?: params.getString(0)
val variable = params.getString(1)
val data = Cobblemon.molangData.load(player.uuid)
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(2))
if (data.map.containsKey(npcId)) {
return@put (data.map[npcId] as VariableStruct).map[variable] ?: DoubleValue.ZERO
} else {
@@ -708,15 +702,16 @@ object MoLangFunctions {
}
}
map.put("set_npc_variable") { params ->
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID ?: params.getString(0)
val variable = params.getString(1)
val value = params.get<MoValue>(2)
val saveAfterwards = params.getBooleanOrNull(3) != false
val data = Cobblemon.molangData.load(player.uuid)
val path = params.getStringOrNull(4)
val data = Cobblemon.molangData.load(player.uuid, path)
val npcData = data.map.getOrPut(npcId) { VariableStruct() } as VariableStruct
npcData.map[variable] = value
if (saveAfterwards) {
Cobblemon.molangData.save(player.uuid)
Cobblemon.molangData.save(player.uuid, path)
}
return@put DoubleValue.ONE
}
@@ -999,6 +994,9 @@ object MoLangFunctions {
return@put if (entity.isStandingOn(blockStrings, depth)) DoubleValue.ONE else DoubleValue.ZERO
}
map.put("discard") {
entity.discard()
}
if (entity is PosableEntity) {
map.put("play_animation") { params ->
val animation = params.getString(0)
@@ -1585,6 +1583,13 @@ object MoLangFunctions {
}
struct
}
map.put("ride_boosts") {
val struct = QueryStruct(hashMapOf())
for (stat in RidingStat.entries) {
struct.addFunction(stat.name.lowercase()) { DoubleValue(pokemon.getRideBoost(stat) ?: 0.0) }
}
struct
}
map.put("natdex_number") {
DoubleValue(pokemon.species.nationalPokedexNumber.toDouble())
}
@@ -1790,6 +1795,21 @@ object MoLangFunctions {
return@put DoubleValue.ZERO
}
}
map.put("set_ride_boost") { params ->
val statName = params.getString(0).uppercase()
val stat = RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) } ?: return@put DoubleValue.ZERO
val value = params.getDoubleOrNull(1)?.toFloat() ?: return@put DoubleValue.ZERO
pokemon.setRideBoost(stat, value)
return@put DoubleValue.ONE
}
map.put("add_ride_boost") { params ->
val statName = params.getString(0).uppercase()
val stat = RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) } ?: return@put DoubleValue.ZERO
val value = params.getDoubleOrNull(1)?.toFloat() ?: return@put DoubleValue.ZERO
return@put DoubleValue(if (pokemon.addRideBoost(stat, value)) 1.0 else 0.0)
}
map.put("initialize_moveset") { params ->
val preferLatest = params.getBooleanOrNull(0) ?: true
pokemon.initializeMoveset(preferLatest)
@@ -2124,12 +2144,17 @@ object MoLangFunctions {
}
map.put("data") { params ->
val data = Cobblemon.molangData.load(UUID(0L, 0L))
return@put data
Cobblemon.molangData.load(
UUID(0L, 0L),
params.getStringOrNull(0)
)
}
map.put("save_data") {
Cobblemon.molangData.save(UUID(0L, 0L))
map.put("save_data") { params ->
Cobblemon.molangData.save(
UUID(0L, 0L),
params.getStringOrNull(index = 0)
)
return@put DoubleValue.ONE
}
@@ -54,6 +54,7 @@ class MoveSet : Iterable<Move> {
*/
fun getMoves() = moves.filterNotNull()
fun getMovesWithNulls() = moves.toList()
fun getMoveTemplates() = getMoves().map { move -> move.template }
fun hasSpace() = moves.any { it == null }
/**
@@ -8,10 +8,13 @@
package com.cobblemon.mod.common.api.pokedex
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.storage.player.InstancedPlayerData
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
import com.cobblemon.mod.common.api.tms.TMMoveManager
import com.cobblemon.mod.common.api.tms.TechnicalMachines
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
import com.cobblemon.mod.common.pokemon.Pokemon
@@ -21,6 +24,7 @@ import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import java.util.UUID
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.player.Player
class PokedexManager(
override val uuid: UUID,
@@ -464,7 +464,7 @@ open class PokemonProperties {
val stack = ItemStack(result.item)
stack.applyComponents(result.components)
if (stack.isEmpty) return@let
pokemon.swapHeldItem(stack)
pokemon.swapHeldItem(stack, decrement = true, aiCanDrop = false)
}
pokemon.updateAspects()
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.pokemon.moves
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.data.ClientDataSynchronizer
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.Moves
@@ -20,7 +21,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf
open class Learnset : ClientDataSynchronizer<Learnset> {
class Interpreter(
val loadMove: (JsonElement, Learnset) -> Boolean
val loadMove: (JsonElement, Learnset) -> Boolean
) {
companion object {
fun parseFromPrefixIntoList(prefix: String, list: (Learnset) -> MutableList<MoveTemplate>): Interpreter {
@@ -28,10 +29,10 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
val str = element.takeIf { it.isJsonPrimitive }?.asString ?: return@Interpreter false
if (str.startsWith(prefix)) {
Moves.getByName(str.substringAfter(":"))
?.let {
list(learnset).add(it)
return@Interpreter true
}
?.let {
list(learnset).add(it)
return@Interpreter true
}
}
return@Interpreter false
}
@@ -69,13 +70,12 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
}
val interpreters = mutableListOf(
tmInterpreter,
eggInterpreter,
tutorInterpreter,
legacyInterpreter,
specialInterpreter,
levelUpInterpreter,
formChangeInterpreter
tmInterpreter,
eggInterpreter,
tutorInterpreter,
legacyInterpreter,
specialInterpreter,levelUpInterpreter,
formChangeInterpreter
)
}
@@ -93,11 +93,25 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
val formChangeMoves = mutableListOf<MoveTemplate>()
fun getLevelUpMovesUpTo(level: Int) = levelUpMoves
.entries
.filter { it.key <= level }
.sortedBy { it.key }
.flatMap { it.value }
.toSet()
.entries
.filter { it.key <= level }
.sortedBy { it.key }
.flatMap { it.value }
.toSet()
fun tmLearnableMoves(): MutableList<MoveTemplate> {
val moves = mutableListOf<MoveTemplate>()
moves.addAll(tmMoves)
moves.addAll(tutorMoves)
moves.addAll(eggMoves)
moves.addAll(getLevelUpMovesUpTo(Cobblemon.config.maxPokemonLevel))
moves.addAll(evolutionMoves)
moves.addAll(formChangeMoves)
return moves
}
fun getAllLegalMoves(): Set<MoveTemplate> {
return levelUpMoves.values.flatten().toSet() +
@@ -62,10 +62,19 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
vehicle: PokemonEntity,
driver: Player
): Float {
return state.rideVelocity.get().length().toFloat()
}
override fun tick(
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
) {
if(vehicle.level().isClientSide) {
tickStamina(settings, state, vehicle, driver)
}
return state.rideVelocity.get().length().toFloat()
}
fun tickStamina(
@@ -448,8 +457,9 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
val hoverSpeed = 0.1
// Yaw locally when at or below hoverSpeed
val yawDampen = abs(cos(controller.pitch.toRadians())).pow(2) * abs(cos(controller.roll.toRadians())).pow(2)
val localYaw = if (currSpeed < hoverSpeed) xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed))).coerceIn(0.0, 1.0)
else xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f)
else xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f) * yawDampen
// Pitch locally up or down depending upon a number of factors:
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
@@ -463,8 +473,8 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
// Roll less when slow and not at all when at hoverSpeed or lower.
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
// pitching globally
val pitchInfluencedRollCorrection = 0.4*(abs(yInput - localPitch) * controller.roll.sign * -1.0)
val rollForce = if (currSpeed > hoverSpeed) xInput * sqrt((RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed))) + pitchInfluencedRollCorrection
val pitchInfluencedRollCorrection = 0.0 // if ( currSpeed > hoverSpeed * 2) 0.4*(abs(yInput - localPitch) * controller.roll.sign * -1.0) else 0.0
val rollForce = if (currSpeed > hoverSpeed) xInput * sqrt(abs(RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed)).coerceIn(0.0,1.0)) + pitchInfluencedRollCorrection
else 0.0
//yaw, pitch, roll
@@ -87,30 +87,17 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
vehicle: PokemonEntity,
driver: Player
): Float {
checkTooHigh(settings, state, vehicle)
tickStamina(settings, state, vehicle)
return state.speed.get().toFloat()
}
fun checkTooHigh(
override fun tick(
settings: HoverSettings,
state: HoverState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
) {
val heightLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
val pos = vehicle.position()
val level = Minecraft.getInstance().player?.level() ?: return
val hit = level.clip(
ClipContext(
pos,
pos.subtract(0.0, heightLimit, 0.0),
ClipContext.Block.COLLIDER,
ClipContext.Fluid.NONE,
vehicle
)
)
state.tooHigh.set(hit.type == HitResult.Type.MISS)
tickStamina(settings, state, vehicle)
}
fun tickStamina(
@@ -121,8 +108,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
val stam = state.stamina.get()
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr)).toFloat() / 20.0f
val newStam = if (state.tooHigh.get()) max(0.0f,stam - stamDrainRate)
else min(1.0f,stam + stamDrainRate * 2)
val newStam = min(1.0f,stam + stamDrainRate)
state.stamina.set(newStam)
}
@@ -142,11 +128,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
vehicle: PokemonEntity,
driver: LivingEntity
) {
val f = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
val lookYawLimit = 90.0f
val g = Mth.clamp(f, -lookYawLimit, lookYawLimit)
driver.yRotO += g - f
driver.yRot = driver.yRot + g - f
return
}
@@ -157,9 +139,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
driver: LivingEntity
): Vec2 {
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
return Vec2(driver.xRot, vehicle.yRot + turnAmount )
}
/*
@@ -174,26 +154,19 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
vehicle: PokemonEntity,
driver: LivingEntity
): Float {
//In degrees per second
val handling = 220.0
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr) / 20.0
val maxYawDiff = 90.0f
//Normalize the current rotation diff
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
val rotDiffNorm = rotDiff / maxYawDiff
// Normalize the current rotation diff
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot).coerceIn(-maxYawDiff,maxYawDiff)
val rotDiffInfl = sqrt(abs(rotDiff / maxYawDiff)) * rotDiff.sign
//Take the square root so that the ride levels out quicker when at lower differences between entity
//y and driver y
//This influences the speed of the turn based on how far in one direction you're looking
val rotDiffMod = (sqrt(abs(rotDiffNorm)) * rotDiffNorm.sign)
var turnRate = (handling.toFloat()) * rotDiffInfl
turnRate -= (turnRate * 0.5f) * (RidingBehaviour.scaleToRange(vehicle.deltaMovement.length(), 0.0, topSpeed).coerceIn(0.0, 1.0)).pow(2).toFloat()
//Take the inverse so that you turn less at higher speeds
val normSpeed = 1.0f // = 1.0f - 0.5f*RidingBehaviour.scaleToRange(state.rideVelocityocity.length(), 0.0, topSpeed).toFloat()
val turnRate = (handling.toFloat() / 20.0f)
//Ensure you only ever rotate as much difference as there is between the angles.
val turnSpeed = turnRate * rotDiffMod * normSpeed
// Ensure you only ever rotate as much difference as there is between the angles.
val turnSpeed = turnRate
val rotAmount = turnSpeed.coerceIn(-abs(rotDiff), abs(rotDiff))
return rotAmount
@@ -218,9 +191,9 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
): Vec3 {
// Convert these to their tick values
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) / 400.0
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20.0)
var newVelocity = vehicle.deltaMovement
var newVelocity = state.rideVelocity.get()
// Check for collisions and if found, update the speed to reflect it
if (vehicle.verticalCollision || vehicle.horizontalCollision) {
@@ -230,59 +203,75 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
// align the velocity vector to be in local vehicle space
val yawAligned = Matrix3f().rotateY(vehicle.yRot.toRadians())
newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
if (newVelocity.length() < 0.001) {
// Zero out the vector to ignore lingering small values that influence
// direction of movement when scaled by stored speed value.
newVelocity = Vec3.ZERO
}
newVelocity = newVelocity.normalize().scale(state.speed.get())
// Vertical movement based on driver input.
val vertInput = when {
driver.jumping && state.stamina.get() != 0.0f -> 1.0
driver.jumping -> 1.0
driver.isShiftKeyDown -> -1.0
else -> 0.0
}
// Normalize and scale to accel so that diagonal movement
// doesn't speed up the ride more than input in one direction
val inputVel = Vec3(
var inputVel = Vec3(
driver.xxa.sign.toDouble(),
vertInput,
driver.zza.sign.toDouble()
).normalize().scale(accel)
).normalize()
// Scale down input force that opposes our current movement. How much it is scaled
// down depends on our handling/skill
// val handlingMod = if(newVelocity.length() > 0.001) 1 - handling*sqrt(newVelocity.length() / topSpeed)*( 0.5 * (1 + newVelocity.normalize().dot(inputVel.normalize()) * -1.0))
// else 1.0
/******************************************************
* Boosting Logic
*****************************************************/
if (driver.level().isClientSide) {
if(Minecraft.getInstance().options.keySprint.isDown() && state.stamina.get() == 1.0f && !state.isBoosting.get()) {
state.isBoosting.set(true)
val localBoostVel = if (inputVel.horizontalDistance() == 0.0) Vec3(0.0, 0.0, 1.0) else Vec3(inputVel.x, 0.0, inputVel.z)
state.boostVec.set(localBoostVel)
state.boostTicks.set(0)
state.stamina.set(0.0f)
}
}
val maxBoostTicks = 20
val boostPower = (topSpeed * 20) * 0.1 * vehicle.runtime.resolveDouble(settings.jumpExpr)
if (state.isBoosting.get() && state.boostTicks.get() < maxBoostTicks) {
newVelocity = newVelocity.add(state.boostVec.get().scale(boostPower / maxBoostTicks))
state.boostTicks.set(state.boostTicks.get() + 1)
} else if (state.isBoosting.get()) {
state.isBoosting.set(false)
}
newVelocity = newVelocity.add(inputVel)
// Air Friction/Resistance
// When the dot between friction and input is -1 we want to apply none of it.
// In short we want to avoid applying friction that opposes our input force.
// otherwise we would not be able to reach our top speed when acceleration is
// low.
val frictionConst = vehicle.runtime.resolveDouble(settings.handlingExpr)
val frictionConst = 0.02
var frictionForce = newVelocity.scale(-1.0).scale(frictionConst)
val frictionApplied = if(inputVel.lengthSqr() != 0.0) 1 - 0.5 * (1 + -1.0 * frictionForce.normalize().dot(inputVel.normalize()))
val frictionApplied = if(inputVel.lengthSqr() != 0.0) 1 - (0.5*(1 + -1.0 * (inputVel.normalize().dot(frictionForce.normalize()))))
else 1.0
frictionForce = frictionForce.scale((frictionApplied).pow(4))
frictionForce = frictionForce.scale((frictionApplied))
newVelocity = newVelocity.add(frictionForce)
// Apply height limit forces
if (state.tooHigh.get()) {
newVelocity = newVelocity.scale(0.96)
if (state.stamina.get() == 0.0f) {
newVelocity = newVelocity.subtract(0.0, 0.01, 0.0)
}
}
// Ensure that if you're not applying force where the ride is facing then you apply a portion of debuff
val strafeDebuff = 0.5
val vehicleForwardVec = Vec3(0.0, 0.0, 1.0)
val playerForwardVec = inputVel //.yRot(vehicle.yRot)
val inputStrength = accel - (accel * strafeDebuff*(1 - vehicleForwardVec.dot(playerForwardVec).coerceIn(0.0, 1.0)))
inputVel = inputVel.scale(inputStrength)
newVelocity = newVelocity.add(inputVel)
// Set speed to new magnitude to avoid the horrible things minecraft friction
// does to our vectors :(
state.speed.set(min(topSpeed, newVelocity.length()))
// Check to see if this new velocity will exceed top speed and if it will then cap it
newVelocity = if (newVelocity.length() > topSpeed) newVelocity.normalize().scale(topSpeed) else newVelocity
val revertYawAligned = Matrix3f().rotateY(-vehicle.yRot.toRadians())
state.rideVelocity.set((newVelocity.toVector3f().mul(revertYawAligned)).toVec3d())
return newVelocity
}
@@ -355,7 +344,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
vehicle: PokemonEntity,
driver: Player
): Float {
return if (state.tooHigh.get()) 0.9f else 1.0f
return if (state.isBoosting.get()) 1.1f else 1.0f
}
override fun useAngVelSmoothing(
@@ -445,21 +434,21 @@ class HoverSettings : RidingBehaviourSettings {
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 13.0, 6.0)".asExpression()
private set
// Acceleration in blocks per second per second
// Acceleration in number of seconds to top speed
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'AIR', 7.0, 3.0)".asExpression()
"q.get_ride_stats('ACCELERATION', 'AIR', 2.0, 5.0)".asExpression()
private set
// Amount of seconds before stamina depletion
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 7, 3)".asExpression()
// Amount of seconds between boosts
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 1, 4)".asExpression()
private set
// Height in blocks above the ground before stamina loss
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 10.0, 5.0)".asExpression()
// Power of boost as a fraction of top speed
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 1.2, 0.2)".asExpression()
private set
// Amount of air friction applied. 0.2 means 20 percent of the velocity is opposed per tick
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 0.1, 0.02)".asExpression()
// Turn rate in degrees per second when stationary
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 360.0, 90)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
@@ -489,28 +478,41 @@ class HoverSettings : RidingBehaviourSettings {
class HoverState : RidingBehaviourState() {
var speed = ridingState(0.0, Side.CLIENT)
var tooHigh = ridingState(false, Side.CLIENT)
var isBoosting = ridingState(false, Side.BOTH)
var boostVec = ridingState(Vec3.ZERO, Side.CLIENT)
var boostTicks = ridingState(0, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeDouble(speed.get())
buffer.writeBoolean(tooHigh.get())
buffer.writeBoolean(isBoosting.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
speed.set(buffer.readDouble(), forced = true)
tooHigh.set(buffer.readBoolean(), forced = true)
isBoosting.set(buffer.readBoolean(), forced = true)
}
override fun reset() {
super.reset()
speed.set(0.0, forced = true)
isBoosting.set(false, forced = true)
boostVec.set(Vec3.ZERO, forced = true)
boostTicks.set(0, forced = true)
}
override fun copy() = HoverState().also {
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
it.stamina.set(this.stamina.get(), forced = true)
it.tooHigh.set(this.tooHigh.get(), forced = true)
it.isBoosting.set(this.isBoosting.get(), forced = true)
it.boostVec.set(this.boostVec.get(), forced = true)
it.boostTicks.set(this.boostTicks.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is HoverState) return false
if (previous.isBoosting.get() != isBoosting.get()) return true
return super.shouldSync(previous)
}
}
@@ -70,11 +70,14 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
}
override fun speed(settings: JetSettings, state: JetState, vehicle: PokemonEntity, driver: Player): Float {
return state.rideVelocity.get().length().toFloat()
}
override fun tick(settings: JetSettings, state: JetState, vehicle: PokemonEntity, driver: Player, input: Vec3) {
if(vehicle.level().isClientSide) {
handleBoosting(state)
tickStamina(settings, state, vehicle)
}
return state.rideVelocity.get().length().toFloat()
}
fun handleBoosting(
@@ -81,13 +81,21 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
vehicle: PokemonEntity,
driver: Player
): Float {
// Use this as a "tick" function and calculate sprinting and inAir state here
handleSprinting(settings, state, vehicle, driver)
inAirCheck(state, vehicle)
tickStamina(settings, state, vehicle, driver)
return state.rideVelocity.get().length().toFloat()
}
override fun tick(
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
) {
handleSprinting(state, driver)
inAirCheck(state, vehicle)
tickStamina(settings, state, vehicle)
}
fun inAirCheck(
state: HorseState,
vehicle: PokemonEntity,
@@ -100,16 +108,13 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
val standingOnSolid = canSupportEntity && !isAirOrLiquid
// inAir if not on the ground
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
state.inAir.set(inAir)
}
fun handleSprinting(
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player
) {
if (state.sprinting.get()) {
@@ -125,7 +130,6 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity,
driver: Player
) {
val stam = state.stamina.get()
var newStam = stam
@@ -228,22 +232,22 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
): Vec3 {
val canSprint = vehicle.runtime.resolveBoolean(settings.canSprint)
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
val jumpForce = vehicle.runtime.resolveDouble(settings.jumpExpr)
val jumpForce = vehicle.runtime.resolveDouble(settings.jumpExpr) * 0.75
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val walkSpeed = getWalkSpeed(vehicle)
val topSpeed = if(canSprint && state.sprinting.get()) rideTopSpeed else walkSpeed
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20.0)
var activeInput = false
/******************************************************
* Gather the previous velocity and set the horizontal
* fields of the velocity vector to the magnitude of the
* ones from deltaMovement. This is important as when a
* collision happens the Deltamovement will reflect it
* but rideVelocity won't.
* Gather the previous velocity and check for horizontal
* collisions
*****************************************************/
var newVelocity = Vec3(state.rideVelocity.get().x, 0.0, state.rideVelocity.get().z).normalize().scale(vehicle.deltaMovement.horizontalDistance())
newVelocity = newVelocity.add(0.0, state.rideVelocity.get().y, 0.0)
var newVelocity = state.rideVelocity.get() //.normalize().scale(vehicle.deltaMovement.horizontalDistance())
if (vehicle.horizontalCollision) {
newVelocity = newVelocity.normalize().scale(vehicle.deltaMovement.length())
}
/******************************************************
* Speed up and slow down based on input
@@ -282,12 +286,14 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
/******************************************************
* Gravity logic
*****************************************************/
if (vehicle.onGround()) { newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z) }
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
val terminalVel = 2.0
if (!vehicle.onGround() && state.jumpTicks.get() <= 0) {
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
val terminalVel = 2.0
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - gravity, -terminalVel), newVelocity.z)
} else if(vehicle.onGround()) {
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
}
val fallingForce = gravity
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - fallingForce, -terminalVel), newVelocity.z)
/******************************************************
* Ground Friction
@@ -299,8 +305,21 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
/******************************************************
* Jump Logic
*****************************************************/
if (driver.jumping && vehicle.onGround() && canJump && driver.deltaMovement.y <= 0.1) {
newVelocity = Vec3(newVelocity.x, jumpForce, newVelocity.z)
if (state.jumpTicks.get() > 0 || (state.jumpTicks.get() >= 0 && driver.jumping && vehicle.onGround() && canJump && driver.deltaMovement.y <= 0.1)) {
// Spread out jumpforce so that variable height jumps are possible
val jumpInputTicks = 6
if (driver.jumping && (state.jumpTicks.get() >= 0 && state.jumpTicks.get() < jumpInputTicks)) {
val appliedJumpForce = ((jumpForce*1.5) / jumpInputTicks) //* (1 - state.jumpTicks.get() / jumpInputTicks)
newVelocity = Vec3(newVelocity.x, newVelocity.y + appliedJumpForce, newVelocity.z)
state.jumpTicks.set(state.jumpTicks.get() + 1)
} else {
// Set delay before next jump is possible after hitting ground
val tickJumpDelay = 3
state.jumpTicks.set(-tickJumpDelay)
}
} else if (vehicle.onGround() && state.jumpTicks.get() < 0) {
// Tick off the delay once reaching the ground
state.jumpTicks.set(state.jumpTicks.get() + 1)
}
//Zero out lateral velocity possibly picked up from a controller transition
@@ -493,15 +512,15 @@ class HorseSettings : RidingBehaviourSettings {
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 0.8)), (1.0 / (20.0 * 3.0)))".asExpression()
"q.get_ride_stats('ACCELERATION', 'LAND', 0.5, 2.0)".asExpression()
private set
// Between 30 seconds and 10 seconds at the lowest when at full speed.
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
private set
//Between a one block jump and a ten block jump
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 1.5, 0.8)".asExpression()
//Between a one block jump and a six block jump
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 1.2, 0.45)".asExpression()
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 180.0, 40.0)".asExpression()
@@ -538,23 +557,27 @@ class HorseSettings : RidingBehaviourSettings {
class HorseState : RidingBehaviourState() {
var sprinting = ridingState(false, Side.CLIENT)
var inAir = ridingState(false, Side.CLIENT)
var jumpTicks = ridingState(0, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeBoolean(sprinting.get())
buffer.writeBoolean(inAir.get())
buffer.writeInt(jumpTicks.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
sprinting.set(buffer.readBoolean(), forced = true)
inAir.set(buffer.readBoolean(), forced = true)
jumpTicks.set(buffer.readInt(), forced = true)
}
override fun reset() {
super.reset()
sprinting.set(false, forced = true)
inAir.set(false, forced = true)
jumpTicks.set(0, forced = true)
}
override fun copy() = HorseState().also {
@@ -562,6 +585,7 @@ class HorseState : RidingBehaviourState() {
it.stamina.set(this.stamina.get(), forced = true)
it.sprinting.set(this.sprinting.get(), forced = true)
it.inAir.set(this.inAir.get(), forced = true)
it.jumpTicks.set(this.jumpTicks.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
@@ -43,8 +42,10 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
return RidingStyle.LIQUID
}
val poseProvider = PoseProvider<BoatSettings, BoatState>(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.isSwimming && entity.entityData.get(PokemonEntity.MOVING) })
val poseProvider = PoseProvider<BoatSettings, BoatState>(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { _, state, _ ->
abs(state.rideVelocity.get().horizontalDistance()) > 0.2
})
override fun isActive(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
if (state.jumpBuffer.get() != -1) {
@@ -297,11 +298,8 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
vehicle: PokemonEntity,
driver: Player
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
if (state.rideVelocity.get().z > topSpeed * 0.8) {
return Mth.lerp((state.rideVelocity.get().z - (topSpeed * 0.8)) / (topSpeed * 0.2), 1.0, 1.2).toFloat()
}
return 1.0f
val sprintFov = vehicle.runtime.resolveFloat(settings.sprintFovModifier)
return if (state.isVehicleSprinting.get()) sprintFov else 1.0f
}
override fun useAngVelSmoothing(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
@@ -314,7 +312,14 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
return cobblemonResource("no_pose")
val isInWater = Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
if (vehicle.isInWater || vehicle.isUnderWater) {
return@any true
}
val blockState = vehicle.level().getBlockState(it)
return@any !blockState.fluidState.isEmpty
}
return if (!isInWater) cobblemonResource("in_air") else cobblemonResource("no_pose")
}
override fun inertia(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Double {
@@ -395,6 +400,8 @@ class BoatSettings : RidingBehaviourSettings {
var sprintSpeedModifier = "1.5".asExpression()
private set
var sprintFovModifier = "1.2".asExpression()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
@@ -16,12 +16,6 @@ import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.orientation.OrientationController
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.behaviour.types.air.BirdSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.BirdState
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetState
import com.cobblemon.mod.common.api.riding.behaviour.types.air.RocketSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.RocketState
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
@@ -29,11 +23,13 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.math.geometry.toDegrees
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.client.Minecraft
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
@@ -42,6 +38,7 @@ import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import org.joml.Vector3f
import kotlin.math.abs
import kotlin.math.asin
import kotlin.math.cos
import kotlin.math.hypot
import kotlin.math.max
@@ -74,18 +71,28 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
}
override fun speed(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity, driver: Player): Float {
return state.speed.get().toFloat()
}
override fun tick(
settings: DolphinSettings,
state: DolphinState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
) {
if(vehicle.level().isClientSide) {
handleBoosting(state)
handleBoosting(state, vehicle)
tickStamina(settings, state, vehicle)
}
return state.speed.get().toFloat()
}
fun handleBoosting(
state: DolphinState,
vehicle: PokemonEntity
) {
//If the forward key is not held then it cannot be boosting
if(Minecraft.getInstance().options.keyUp.isDown() && state.stamina.get() != 0.0f) {
if(Minecraft.getInstance().options.keyUp.isDown() && state.stamina.get() != 0.0f && (vehicle.isInWater || vehicle.isUnderWater)) {
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
if (state.stamina.get() >= 0.25f) {
//If on the previous tick the boost key was held then don't change if the ride is boosting
@@ -145,18 +152,42 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
val boostMod = if (state.boosting.get()) runtime.resolveDouble(settings.jumpExpr) else 1.0
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0) * boostMod
val accel = (topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20.0)) * boostMod
state.lastVelocity.set(vehicle.deltaMovement.yRot(vehicle.yRot.toRadians()))
/*********************************************
* Breaching logic
*********************************************/
if (!vehicle.isInWater && !vehicle.isUnderWater)
{
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
val terminalVel = 2.0
val currVel = state.lastVelocity.get()
state.lastVelocity.set(Vec3(currVel.x, max(-terminalVel, currVel.y - gravity), currVel.z))
val breachBoost = 1.5
if (!state.hasBreached.get()) {
// Apply an upwards breaching force if boosting. This will enhance jumping out of water to feel more
// fun
if (state.boosting.get()) {
state.lastVelocity.set(Vec3(currVel.x, currVel.y + breachBoost*sin(-controller.pitch.toRadians()).coerceIn(0.0f,0.5f), currVel.z))
}
state.hasBreached.set(true)
} else {
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.4
val terminalVel = 2.0
state.lastVelocity.set(Vec3(currVel.x, max(-terminalVel, currVel.y - gravity), currVel.z)) //.yRot(vehicle.yRot.toRadians()))
}
return state.lastVelocity.get()
} else {
state.lastVelocity.set(vehicle.deltaMovement.yRot(vehicle.yRot.toRadians()))
state.hasBreached.set(false)
}
/*********************************************
* Handle collisions
*********************************************/
if (vehicle.horizontalCollision || vehicle.verticalCollision) {
state.speed.set(vehicle.deltaMovement.length())
// not actually friction
val frictionLimit = 0.1
val postColSpeed = min(max(vehicle.deltaMovement.length(), frictionLimit), state.speed.get())
state.speed.set(postColSpeed)
}
val currSpeed = state.speed.get()
val strafeFactor = runtime.resolveDouble(settings.strafeFactor)
val reverseDriveFactor = runtime.resolveDouble(settings.reverseDriveFactor)
@@ -178,9 +209,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
newVelocity = controller.orientation?.transform(newVelocity.toVector3f())?.toVec3d() ?: Vec3.ZERO
newVelocity = newVelocity.yRot(vehicle.yRot.toRadians())
val desiredVec = newVelocity.subtract(vehicle.deltaMovement)
//val inpVel = if (desiredVec.lengthSqr() > 0.001) desiredVec.normalize().scale(0.1) else Vec3.ZERO
return vehicle.deltaMovement.add(desiredVec)
//return newVelocity
}
override fun angRollVel(
@@ -207,7 +236,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
controller.applyGlobalYaw(trueYawDelt.toFloat() * yawDampen)
// Correct orientation when at low speeds
correctOrientation(settings, state, vehicle, controller, deltaTime)
correctOrientation(settings, state, vehicle, controller, driver, deltaTime)
//yaw, pitch, roll
return Vec3(0.0, state.currPitchCorrectionForce.get(), state.currRollCorrectionForce.get())
@@ -218,16 +247,19 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
state: DolphinState,
vehicle: PokemonEntity,
controller: OrientationController,
driver: Player,
deltaTime: Double
) {
// Calculate correcting roll force.
// Mult by cos(pitch) to ensure that it doesn't roll correct at when diving or climbing
val currSpeed = vehicle.deltaMovement.length()
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect)
val maxRollCorrectionRate = 10.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
val maxRollCorrectionRate = if (!(vehicle.isInWater || vehicle.isUnderWater)) 15.0f
else 10.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
val maxRollForce = maxRollCorrectionRate / 40.0
val rollArrivalDeg = 90.0
if(state.noInputTime.get() > rollCorrectionTimer) {
if((state.speed.get() < 0.1 && driver.zza.toDouble() == 0.0) || state.noInputTime.get() > rollCorrectionTimer || !(vehicle.isInWater || vehicle.isUnderWater)) {
val arrivalInfluence = (min(rollArrivalDeg,abs(controller.roll).toDouble()) / rollArrivalDeg)
val desiredRollForce = (0 - controller.roll).sign * maxRollCorrectionRate * arrivalInfluence
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
@@ -249,12 +281,14 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
}
//Calculate correcting pitch force.
val maxPitchCorrectionRate = 10.0f * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
val maxPitchCorrectionRate = if (!(vehicle.isInWater || vehicle.isUnderWater)) 15.0 else 5.0f * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat() * (1 - (currSpeed * 0.5).coerceIn(0.0, 1.0))
val maxPitchForce = maxPitchCorrectionRate / 40.0
val pitchArrivalDeg = 90.0
if(state.noInputTime.get() > rollCorrectionTimer) {
val arrivalInfluence = (min(pitchArrivalDeg,abs(controller.pitch).toDouble()) / pitchArrivalDeg)
val desiredPitchForce = (0 - controller.pitch).sign * maxPitchCorrectionRate * arrivalInfluence
val pitchArrivalDeg = 45.0
val targetPitch = if ((vehicle.isInWater || vehicle.isUnderWater)) 0.0f else (-asin(vehicle.deltaMovement.y / vehicle.deltaMovement.length()).toDegrees()).coerceIn(-45.0f, 45.0f)
if((state.noInputTime.get() > rollCorrectionTimer && driver.zza.toDouble() == 0.0) || !(vehicle.isInWater || vehicle.isUnderWater)) {
val arrivalInfluence = min(rollArrivalDeg, abs(Mth.wrapDegrees(targetPitch - controller.pitch.toDouble()))) / pitchArrivalDeg
val desiredPitchForce = (targetPitch - controller.pitch).sign * maxPitchCorrectionRate * arrivalInfluence
var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
// Ensure the 'steering' correction force doesn't exceed our maxForce
@@ -288,7 +322,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
): Vec3 {
// Begin the roll correction time counter if not enough mouse input
// is detected
if (abs(mouseX) < 1 && abs(mouseY) < 1 && (!vehicle.isInWater && !vehicle.isUnderWater)) {
if (abs(mouseX) < 1 && abs(mouseY) < 1 && (vehicle.isInWater || vehicle.isUnderWater)) {
state.noInputTime.set(state.noInputTime.get() + deltaTime)
} else {
state.noInputTime.set(0.0)
@@ -339,7 +373,8 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
// Set roll to zero if transitioning to noroll config
controller.rotateRoll(controller.roll * -1.0f)
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val boostingHandleDebuff = if (state.boosting.get()) 0.2 else 1.0
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr) * boostingHandleDebuff
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult)
//Smooth out mouse input.
@@ -384,7 +419,8 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult)
val boostingHandleDebuff = if (state.boosting.get()) 0.2 else 1.0
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult) * boostingHandleDebuff
//Smooth out mouse input.
val smoothingSpeed = 4.0
@@ -393,11 +429,11 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
val currSpeed = vehicle.deltaMovement.length()
val hoverSpeed = topSpeed * 0.5
// Yaw locally when at or below hoverSpeed
val localYaw = xInput * (1 - (RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.2, 1.0)
//else xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f)
// Yaw locally
// Dampen yaw when rolled or pitched substantially
val yawDampen = abs(cos(controller.pitch.toRadians())).pow(2) * abs(cos(controller.roll.toRadians())).pow(2)
val localYaw = xInput * (1 - (RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.4, 1.0) * yawDampen
// Pitch locally up or down depending upon a number of factors:
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
@@ -411,7 +447,6 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
// Roll less when slow and not at all when at hoverSpeed or lower.
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
// pitching globally
val pitchInfluencedRollCorrection = 0.4*(abs(yInput - localPitch) * controller.roll.sign * -1.0)
val rollForce = xInput * ((RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.2, 1.0) //+ pitchInfluencedRollCorrection
//yaw, pitch, roll
@@ -452,7 +487,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
vehicle: PokemonEntity,
regularGravity: Double
): Double {
return if (!vehicle.isInWater && !vehicle.isUnderWater) 1.0 else 0.0
return 0.0 //if (!vehicle.isInWater && !vehicle.isUnderWater) 1.0 else 0.0
}
override fun rideFovMultiplier(
@@ -461,14 +496,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
vehicle: PokemonEntity,
driver: Player
): Float {
if (state.boosting.get()) {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
val boostMod = vehicle.runtime.resolveDouble(settings.jumpExpr)
val normalizedBoostSpeed = RidingBehaviour.scaleToRange(state.speed.get(), topSpeed, topSpeed * boostMod)
return 1.0f + normalizedBoostSpeed.pow(2).toFloat() * 0.2f
} else {
return 1.0f
}
return if (state.boosting.get()) 1.2f else 1.0f
}
override fun useAngVelSmoothing(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
@@ -548,7 +576,7 @@ class DolphinSettings : RidingBehaviourSettings {
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LIQUID', 2.2, 1.2)".asExpression()
// Yaw rate in degrees per second
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LIQUID', 100.0, 45.0)".asExpression()
var horizontalPitchExpr: Expression = "q.get_ride_stats('SKILL', 'LIQUID', 0.4, 0.2)".asExpression()
var horizontalPitchExpr: Expression = "q.get_ride_stats('SKILL', 'LIQUID', 0.3, 0.2)".asExpression()
var mouseInputMult: Expression = "q.get_ride_stats('SKILL', 'LIQUID', 1.2, 0.8)".asExpression()
// Top speed in Bl/s
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LIQUID', 15.0, 5.0)".asExpression()
@@ -599,6 +627,7 @@ class DolphinState : RidingBehaviourState() {
var boosting = ridingState(false, Side.BOTH)
var boostIsToggleable = ridingState(false, Side.CLIENT)
var speed = ridingState(0.0, Side.CLIENT)
var hasBreached = ridingState(false, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
@@ -619,6 +648,7 @@ class DolphinState : RidingBehaviourState() {
boosting.set(false, forced = true)
boostIsToggleable.set(true, forced = true)
speed.set(0.0, forced = true)
hasBreached.set(false, forced = true)
}
override fun toString(): String {
@@ -635,6 +665,7 @@ class DolphinState : RidingBehaviourState() {
it.boosting.set(this.boosting.get(), forced = true)
it.boostIsToggleable.set(this.boosting.get(), forced = true)
it.speed.set(this.speed.get(), forced = true)
it.hasBreached.set(this.hasBreached.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.storage
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.DataKeys
@@ -60,6 +61,8 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
pokemon.add(Pokemon.loadFromNBT(registryAccess, pokemonNBT))
} catch(_: InvalidSpeciesException) {
handleInvalidSpeciesNBT(pokemonNBT)
} catch (e: Exception) {
LOGGER.error("Failed to read a pokémon: $pokemonNBT", e)
}
}
return this
@@ -78,6 +81,8 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
pokemon.add(Pokemon.loadFromJSON(registryAccess, pokemonJSON))
} catch (_: InvalidSpeciesException) {
handleInvalidSpeciesJSON(pokemonJSON)
} catch (e: Exception) {
LOGGER.error("Failed to read a pokémon: $pokemonJSON", e)
}
}
return this
@@ -77,6 +77,10 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
remove(pokemon)
val position = getFirstAvailablePosition() ?: return false // Couldn't fit, shrug emoji
set(position, pokemon)
// sync TM moves here
Cobblemon.playerDataManager.getTMData(uuid)?.syncTMsFromPokemon(pokemon)
return true
}
@@ -13,5 +13,5 @@ import java.util.UUID
interface MoLangDataStoreFactory {
fun markDirty(uuid: UUID)
fun load(uuid: UUID) : VariableStruct
fun load(uuid: UUID, filePath: String? = null) : VariableStruct
}
@@ -20,6 +20,8 @@ import java.nio.file.Path
import java.util.*
object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
const val DEFAULT_PATH = "playermolangdata/"
const val DEFAULT_FOLDER_STRUCTURE = "%s/%s.dat"
lateinit var savePath: Path
val cache = mutableMapOf<UUID, VariableStruct>()
val dirty = mutableListOf<UUID>()
@@ -57,7 +59,7 @@ object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
dirty.add(uuid)
}
override fun load(uuid: UUID): VariableStruct {
override fun load(uuid: UUID, filePath: String?): VariableStruct {
return if (cache.contains(uuid))
cache[uuid]!!
else {
@@ -68,7 +70,7 @@ object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
return data
}
val nbt = NbtIo.readCompressed(file(uuid), NbtAccounter.unlimitedHeap())
val nbt = NbtIo.readCompressed(file(uuid, filePath ?: DEFAULT_PATH), NbtAccounter.unlimitedHeap())
// If it's not a VariableStruct then someone's fucked around and will subsequently find out
val data = readMoValueFromNBT(nbt) as VariableStruct
@@ -77,8 +79,8 @@ object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
}
}
fun save(uuid: UUID) {
val file = file(uuid)
fun save(uuid: UUID, filePath: String? = null) {
val file = file(uuid, filePath ?: DEFAULT_PATH)
val data = cache[uuid] ?: return
val nbt = writeMoValueToNBT(data)!! as CompoundTag
file.toFile().parentFile.mkdirs()
@@ -86,5 +88,5 @@ object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
dirty -= uuid
}
private fun file(uuid: UUID) = savePath.resolve("playermolangdata/${uuid.toString().substring(0, 2)}/$uuid.dat")
private fun file(uuid: UUID, filePath: String = DEFAULT_PATH) = savePath.resolve(filePath + DEFAULT_FOLDER_STRUCTURE.format(uuid.toString().substring(0, 2), uuid))
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.storage.party
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.reactive.Observable
@@ -169,6 +170,8 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
}
} catch (_: InvalidSpeciesException) {
handleInvalidSpeciesNBT(pokemonNBT)
} catch (e: Exception) {
LOGGER.error("Failed to read a pokémon: $pokemonNBT", e)
}
}
@@ -200,6 +203,8 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
slots[slot] = Pokemon.loadFromJSON(registryAccess, pokemonJSON)
} catch (_: InvalidSpeciesException) {
handleInvalidSpeciesJSON(pokemonJSON)
} catch (e: Exception) {
LOGGER.error("Failed to read a pokémon: $pokemonJSON", e)
}
}
}
@@ -8,8 +8,8 @@
package com.cobblemon.mod.common.api.storage.pc
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.pokemon.PokemonSortMode
import com.cobblemon.mod.common.api.reactive.Observable.Companion.stopAfter
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.storage.InvalidSpeciesException
import com.cobblemon.mod.common.api.storage.StoreCoordinates
@@ -148,6 +148,8 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
pokemon[slot] = Pokemon.loadFromJSON(registryAccess, pokemonJson)
} catch (_: InvalidSpeciesException) {
pc.handleInvalidSpeciesJSON(pokemonJson)
} catch (e: Exception) {
LOGGER.error("Failed to read a pokémon: $pokemonJson", e)
}
}
}
@@ -170,6 +172,8 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
pokemon[slot] = Pokemon.loadFromNBT(registryAccess, pokemonNBT)
} catch (_: InvalidSpeciesException) {
pc.handleInvalidSpeciesNBT(pokemonNBT)
} catch (e: Exception) {
LOGGER.error("Failed to read a pokémon: $pokemonNBT", e)
}
}
}
@@ -27,6 +27,7 @@ data class GeneralPlayerData(
var starterSelected: Boolean,
var starterUUID: UUID?,
var keyItems: MutableSet<ResourceLocation>,
var tmSet: MutableSet<ResourceLocation>,
var battleTheme: ResourceLocation?,
val extraData: MutableMap<String, PlayerDataExtension>,
) : InstancedPlayerData {
@@ -44,6 +45,7 @@ data class GeneralPlayerData(
starterSelected,
starterUUID,
advancementData.totalBattleVictoryCount == 0,
tmSet,
battleTheme
)
}
@@ -8,10 +8,12 @@
package com.cobblemon.mod.common.api.storage.player
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.MODID
import com.cobblemon.mod.common.api.pokedex.PokedexManager
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.api.tms.TMMoveManager
import com.google.common.util.concurrent.ThreadFactoryBuilder
import java.util.UUID
import java.util.concurrent.Executors
@@ -66,6 +68,14 @@ open class PlayerInstancedDataStoreManager {
.infiniteIterations()
.tracker(ServerTaskTracker)
.build()
saveTasks[PlayerInstancedDataStoreTypes.TM_MOVES] = ScheduledTask.Builder()
.execute { saveAllOfOneType(PlayerInstancedDataStoreTypes.TM_MOVES) }
.delay(30f)
.interval(120f)
.infiniteIterations()
.tracker(ServerTaskTracker)
.build()
}
open fun get(playerId: UUID, dataType: PlayerInstancedDataStoreType): InstancedPlayerData {
@@ -126,4 +136,16 @@ open class PlayerInstancedDataStoreManager {
open fun getPokedexData(playerId: UUID): PokedexManager {
return get(playerId, PlayerInstancedDataStoreTypes.POKEDEX) as PokedexManager
}
fun getTMData(player: ServerPlayer): TMMoveManager {
return getTMData(player.uuid)
}
fun getTMData(playerId: UUID): TMMoveManager {
val data = get(playerId, PlayerInstancedDataStoreTypes.TM_MOVES)
if (data !is TMMoveManager) {
Cobblemon.LOGGER.warn("getTMData: No TMMoveManager found for $playerId (got ${data?.javaClass?.name})")
}
return data as TMMoveManager
}
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.storage.player
import com.cobblemon.mod.common.api.storage.player.client.ClientGeneralPlayerData
import com.cobblemon.mod.common.api.storage.player.client.ClientTMMoveManager
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.resources.ResourceLocation
@@ -27,11 +28,22 @@ object PlayerInstancedDataStoreTypes {
ClientPokedexManager::runAction,
ClientPokedexManager::runIncremental
))
val TM_MOVES = register(PlayerInstancedDataStoreType(
cobblemonResource("tm_moves"),
ClientTMMoveManager::decode,
ClientTMMoveManager::runAction,
ClientTMMoveManager::runIncremental
))
fun register(type: PlayerInstancedDataStoreType): PlayerInstancedDataStoreType {
println("Registering PlayerInstancedDataStoreType: ${type.id}")
types[type.id] = type
return type
}
fun getTypeById(id: ResourceLocation) = types[id]
fun getTypeById(id: ResourceLocation): PlayerInstancedDataStoreType? {
val type = types[id]
if (type == null) println("Unknown PlayerInstancedDataStoreType ID: $id")
return type
}
}
@@ -36,6 +36,7 @@ class PlayerDataJsonBackend: JsonBackedPlayerDataStoreBackend<GeneralPlayerData>
starterUUID = null,
keyItems = mutableSetOf(),
extraData = mutableMapOf(),
tmSet = mutableSetOf(),
battleTheme = CobblemonSounds.PVP_BATTLE.location
)}
@@ -37,6 +37,7 @@ class PlayerDataMongoBackend(mongoClient: MongoClient, databaseName: String, col
starterUUID = null,
keyItems = mutableSetOf(),
extraData = mutableMapOf(),
tmSet = mutableSetOf(),
battleTheme = CobblemonSounds.PVP_BATTLE.location
)}
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.storage.player.adapter
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.api.tms.TMMoveManager
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import java.util.UUID
class TMMoveJsonBackend :
JsonBackedPlayerDataStoreBackend<TMMoveManager>("tm_moves", PlayerInstancedDataStoreTypes.TM_MOVES) {
override val gson = GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.registerTypeAdapter(TMMoveManager::class.java, CodecBackedAdapter(TMMoveManager.CODEC))
.create()
override val classToken = TypeToken.get(TMMoveManager::class.java)
override val defaultData = defaultDataFunc
override fun initialize(store: TMMoveManager) {
store.initialize()
}
companion object {
val defaultDataFunc = { uuid: UUID -> TMMoveManager(uuid) }
}
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.storage.player.adapter
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.api.tms.TMMoveManager
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import com.mongodb.client.MongoClient
import java.util.UUID
class TMMoveMongoBackend(
mongoClient: MongoClient,
databaseName: String,
collectionName: String
) : MongoBackedPlayerDataStoreBackend<TMMoveManager>(
mongoClient,
databaseName,
collectionName,
PlayerInstancedDataStoreTypes.TM_MOVES
) {
override val gson = GsonBuilder()
.registerTypeAdapter(TMMoveManager::class.java, CodecBackedAdapter(TMMoveManager.CODEC))
.create()
override val classToken = TypeToken.get(TMMoveManager::class.java)
override val defaultData = defaultDataFunc
override fun initialize(store: TMMoveManager) {
store.initialize()
}
companion object {
val defaultDataFunc = { uuid: UUID -> TMMoveManager(uuid) }
}
}
@@ -0,0 +1,18 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.storage.player.adapter
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.api.tms.TMMoveManager
import java.util.UUID
class TMMoveNbtBackend : NbtBackedPlayerData<TMMoveManager>("tm_moves", PlayerInstancedDataStoreTypes.TM_MOVES) {
override val codec = TMMoveManager.CODEC
override val defaultData = TMMoveJsonBackend.defaultDataFunc
}
@@ -34,6 +34,7 @@ data class ClientGeneralPlayerData(
var starterSelected: Boolean = false,
var starterUUID: UUID? = null,
var showChallengeLabel: Boolean = true,
var tmSet: MutableSet<ResourceLocation> = mutableSetOf(),
val battleTheme: ResourceLocation? = null
) : ClientInstancedPlayerData {
@@ -46,6 +47,7 @@ data class ClientGeneralPlayerData(
buf.writeNullable(starterUUID) { pb, value -> pb.writeString(value.toString()) }
buf.writeNullable(resetStarters) { pb, value -> pb.writeBoolean(value) }
buf.writeNullable(battleTheme) {pb, value -> pb.writeIdentifier(value)}
buf.writeCollection(tmSet) { pb, value -> pb.writeIdentifier(value)}
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): SetClientPlayerDataPacket {
@@ -56,6 +58,7 @@ data class ClientGeneralPlayerData(
val showChallengeLabel = buffer.readBoolean()
val starterUUID = buffer.readNullable { it.readString() }?.let { UUID.fromString(it) }
val resetStarterPrompt = buffer.readNullable { it.readBoolean() }
val tmSet = buffer.readCollection({ mutableSetOf<ResourceLocation>() }) { it.readIdentifier() }
val battleTheme = buffer.readNullable { it.readIdentifier() }
val data = ClientGeneralPlayerData(
resetStarterPrompt,
@@ -64,6 +67,7 @@ data class ClientGeneralPlayerData(
starterSelected,
starterUUID,
showChallengeLabel,
tmSet,
battleTheme
)
//Weird to do this, but since the flag doesn't get passed to the decoded obj, do it here
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.storage.player.client
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeString
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
class ClientTMMoveManager(
val learnedTMs: MutableSet<ResourceLocation>
) : ClientInstancedPlayerData {
override fun encode(buf: RegistryFriendlyByteBuf) {
buf.writeCollection(learnedTMs) { _, id -> buf.writeString(id.toString()) }
}
companion object {
fun decode(buf: RegistryFriendlyByteBuf): SetClientPlayerDataPacket {
val tms = buf.readCollection(
{ mutableSetOf<ResourceLocation>() },
{ buf.readString().let(ResourceLocation::tryParse) ?: ResourceLocation("minecraft", "empty") }
)
return SetClientPlayerDataPacket(
PlayerInstancedDataStoreTypes.TM_MOVES,
ClientTMMoveManager(tms)
)
}
fun runAction(data: ClientInstancedPlayerData) {
if (data !is ClientTMMoveManager) return
CobblemonClient.clientTMMoveData = data
}
fun runIncremental(data: ClientInstancedPlayerData) {
if (data !is ClientTMMoveManager) return
CobblemonClient.clientTMMoveData.learnedTMs.addAll(data.learnedTMs)
}
}
}
@@ -91,6 +91,7 @@ object CobblemonBlockTags {
@JvmField val TUMBLESTONE_BRICKS = createTag("tumblestone_bricks")
@JvmField val TUMBLESTONES = createTag("tumblestones")
@JvmField val TUMBLESTONE_HEAT_SOURCE = createTag("tumblestone_heat_source")
@JvmField val TYPE_GEM_BLOCKS = createTag("type_gem_blocks")
@JvmField val WALL_HANGING_SIGNS = createTag("wall_hanging_signs")
@JvmField val WALL_SIGNS = createTag("wall_signs")
@JvmField val WATER_STONE_ORES = createTag("water_stone_ores")
@@ -0,0 +1,68 @@
/*
* 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.tms
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreType
import com.cobblemon.mod.common.api.storage.player.client.ClientInstancedPlayerData
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.getPlayer
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import java.util.*
abstract class AbstractTMMoveManager {
abstract val uuid: UUID
abstract val learnedTMs: MutableSet<ResourceLocation>
abstract val storeType: PlayerInstancedDataStoreType
abstract fun toClientData(): ClientInstancedPlayerData
abstract fun toClientDataFrom(set: Set<ResourceLocation>): ClientInstancedPlayerData
open fun learn(tmId: ResourceLocation): Boolean {
if (learnedTMs.add(tmId)) {
syncClient(setOf(tmId))
sendTMToast(tmId)
return true
}
return false
}
private fun sendTMToast(tmId: ResourceLocation) {
val player = uuid.getPlayer() ?: return
val tm = TechnicalMachines.tmMap[tmId] ?: return
val packet = ToastPacket(
title = Component.literal("New TM Learned"), // todp lang for this
description = tm.translatedMoveName(),
icon = tm.createItemStack(),
frameTexture = ResourceLocation.parse("minecraft:toast/advancement"),
progress = -1F,
progressColor = 0x00FF00,
uuid = UUID.nameUUIDFromBytes(("tm_toast:${tmId}").toByteArray()),
behaviour = ToastPacket.Behaviour.SHOW_OR_UPDATE,
durationMs = 4000L
)
player.sendPacket(packet)
}
protected fun syncClient(updateSet: Set<ResourceLocation> = learnedTMs) {
uuid.getPlayer()?.sendPacket(
SetClientPlayerDataPacket(
type = storeType,
playerData = toClientDataFrom(updateSet),
isIncremental = true
)
)
}
open fun markDirty() {}
}
@@ -0,0 +1,58 @@
/*
* 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.tms
import com.cobblemon.mod.common.tms.obtain.ImpossibleObtainMethod
import com.cobblemon.mod.common.tms.obtain.NoneObtainMethod
import com.cobblemon.mod.common.tms.obtain.PlayerHasAdvancementObtainMethod
import com.cobblemon.mod.common.tms.obtain.PlayerYObtainMethod
import com.cobblemon.mod.common.tms.obtain.PokemonHasMoveObtainMethod
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.server.level.ServerPlayer
interface ObtainMethod {
val passive: Boolean
/**
* Checks if this [ObtainMethod] matches the given player.
*/
fun matches(player: ServerPlayer): Boolean
/**
* Serializes this [ObtainMethod] to the buffer.
*/
fun writeToBuffer(buffer: RegistryFriendlyByteBuf)
companion object {
/**
* Deserializes an [ObtainMethod] from the buffer.
*/
fun readFromBuffer(buffer: RegistryFriendlyByteBuf): ObtainMethod {
val variant = buffer.readUtf()
return when (variant) {
"cobblemon:impossible" -> ImpossibleObtainMethod()
"cobblemon:none" -> NoneObtainMethod()
"cobblemon:advancement" -> {
val advancement = buffer.readResourceLocation()
PlayerHasAdvancementObtainMethod(advancement)
}
"cobblemon:y_level" -> {
val yLevel = buffer.readVarInt()
val operator = buffer.readUtf()
PlayerYObtainMethod(yLevel, operator)
}
"cobblemon:pokemon_knows" -> {
val moveId = buffer.readUtf()
PokemonHasMoveObtainMethod(moveId)
}
else -> throw IllegalArgumentException("Unknown ObtainMethod variant: $variant")
}
}
}
}
@@ -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.tms
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import net.minecraft.resources.ResourceLocation
import kotlin.reflect.KClass
/**
* A type adapter for [ObtainMethod]s.
* For the default implementation see [CobblemonObtainMethodAdapter].
*
* @author Apion
* @since November 22nd, 2023
*/
interface ObtainMethodAdapter : JsonDeserializer<ObtainMethod>, JsonSerializer<ObtainMethod> {
/**
* Register a [ObtainMethod] to be used by this adapter.
*
* @param type The [KClass] of the [ObtainMethod].
* @param identifier The expected [ResourceLocation] in the parsed JSON.
*/
fun register(type: KClass<out ObtainMethod>, identifier: ResourceLocation)
}
@@ -0,0 +1,64 @@
/*
* 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.tms
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.storage.player.InstancedPlayerData
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.api.storage.player.client.ClientInstancedPlayerData
import com.cobblemon.mod.common.api.tms.AbstractTMMoveManager
import com.cobblemon.mod.common.api.storage.player.client.ClientTMMoveManager
import com.cobblemon.mod.common.pokemon.Pokemon
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.resources.ResourceLocation
import java.util.UUID
class TMMoveManager(
override val uuid: UUID,
override val learnedTMs: MutableSet<ResourceLocation> = mutableSetOf()
) : AbstractTMMoveManager(), InstancedPlayerData {
override val storeType = PlayerInstancedDataStoreTypes.TM_MOVES
override fun initialize() {}
override fun toClientData(): ClientTMMoveManager {
return ClientTMMoveManager(learnedTMs.toMutableSet())
}
override fun toClientDataFrom(set: Set<ResourceLocation>): ClientTMMoveManager {
return ClientTMMoveManager(set.toMutableSet())
}
fun syncTMsFromPokemon(pokemon: Pokemon) {
val learnableTMs = TechnicalMachines.tmMap.values
.filter { tm -> pokemon.allAccessibleMoves.contains(tm.moveName) }
for (tm in learnableTMs) {
learn(tm.id)
}
}
companion object {
val CODEC: Codec<TMMoveManager> = RecordCodecBuilder.create { instance ->
instance.group(
Codec.STRING.xmap(UUID::fromString, UUID::toString)
.fieldOf("uuid")
.forGetter { it.uuid },
Codec.list(ResourceLocation.CODEC)
.xmap({ it.toMutableSet() }, { it.toList() })
.fieldOf("learnedTMs")
.forGetter { it.learnedTMs }
).apply(instance) { uuid, learnedTMs ->
TMMoveManager(uuid, learnedTMs)
}
}
}
}
@@ -0,0 +1,118 @@
/*
* 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.tms
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.item.components.TMMoveComponent
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.tms.obtain.NoneObtainMethod
import com.cobblemon.mod.common.util.lang
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.network.chat.MutableComponent
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
class TechnicalMachine(
val moveName: MoveTemplate,
val recipe: List<TechnicalMachineRecipe>?,
val obtainMethods: List<ObtainMethod> = emptyList(),
val type: String
) {
companion object {
/**
* Filters all available [TechnicalMachines] based on three optional arguments
*
* @param search A [String] to check against move names
* @param type An [ElementalType] to check for
* @param pokemon A [Pokemon] to check the [Learnset] of
* @return A [MutableSet] of [TechnicalMachine] that passed the filter
*/
fun filterTms(search: String?, type: ElementalType?, pokemon: Pokemon?, player: Player? = null): MutableSet<TechnicalMachine> {
val tms = TechnicalMachines.tmMap.values.toMutableSet()
type?.let {
val iterator = tms.iterator()
while (iterator.hasNext()) {
val tm = iterator.next()
if (ElementalTypes.get(tm.type) != type) {
iterator.remove()
}
}
}
pokemon?.let {
val iterator = tms.iterator()
while (iterator.hasNext()) {
val tm = iterator.next()
if (!pokemon.species.moves.tmLearnableMoves().contains(tm.moveName)) {
iterator.remove()
}
}
}
search?.let {
val iterator = tms.iterator()
while (iterator.hasNext()) {
val tm = iterator.next()
if (!tm.translatedMoveName().string.contains(search, ignoreCase = true)) {
iterator.remove()
}
}
}
player?.let {
val learnedTMs = CobblemonClient.clientTMMoveData?.learnedTMs ?: return@let
tms.retainAll { tm -> learnedTMs.contains(tm.id) }
}
return tms
}
}
lateinit var id: ResourceLocation
/**
* Gets the recipe list limited to the provided amount to match the slot amount of the TM Machine.
*
* @param limit The amount of recipe item slots to take.
* @return The recipe list limited to 3 ingredients.
*/
fun getClampedRecipe(limit: Int = 3): List<TechnicalMachineRecipe>? = recipe?.take(limit)
/**
* Unlocks this [TechnicalMachine] for the player.
*
* @param player The [ServerPlayer] to give this [TechnicalMachine] to.
* @return Whether the player was successfully granted the [TechnicalMachine]
*/
fun unlock(player: ServerPlayer): Boolean {
if (!obtainMethods.any { it is NoneObtainMethod }) {
player.sendSystemMessage(lang("tms.unlock_tm", moveName.displayName))
}
return true
}
/**
* Returns a [MutableComponent] of the translated move name of this [TechnicalMachine]
*
* @return This [TechnicalMachine]'s move name, translated
*/
fun translatedMoveName(): MutableComponent = moveName.displayName
fun createItemStack(): ItemStack {
val stack = ItemStack(CobblemonItems.TECHNICAL_MACHINE)
TMMoveComponent.setTMMove(stack, moveName)
return stack
}
}
@@ -0,0 +1,19 @@
/*
* 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.tms
import net.minecraft.resources.ResourceLocation
/**
* Represents the ingredients necessary to craft a [TechnicalMachine] in the [TMBlock]
*/
data class TechnicalMachineRecipe(
val item: ResourceLocation,
val count: Int
)
@@ -0,0 +1,71 @@
/*
* 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.tms
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.adapters.MoveTemplateAdapter
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.registry.ItemTagCondition
import com.cobblemon.mod.common.util.adapters.CobblemonObtainMethodAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.server.level.ServerPlayer
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.packs.PackType
object TechnicalMachines : JsonDataRegistry<TechnicalMachine> {
override val gson = GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(ObtainMethod::class.java, CobblemonObtainMethodAdapter)
.registerTypeAdapter(MoveTemplate::class.java, MoveTemplateAdapter)
.create()
override val typeToken = TypeToken.get(TechnicalMachine::class.java)
override val resourcePath = "tms"
override val id = cobblemonResource("technical_machines")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<TechnicalMachines>()
val tmMap = mutableMapOf<ResourceLocation, TechnicalMachine>()
val moveToTM = mutableMapOf<MoveTemplate, TechnicalMachine>()
val tagMap = mutableMapOf<ItemTagCondition, TechnicalMachine>()
val passiveTms = mutableMapOf<ResourceLocation, TechnicalMachine>()
override fun reload(data: Map<ResourceLocation, TechnicalMachine>) {
data.forEach { (id, tm) ->
//TODO remove the logging
Cobblemon.LOGGER.info("Processing TM: $id with moveName: ${tm.moveName}")
if (tm.moveName == null) {
Cobblemon.LOGGER.error("Failed to resolve move for TM: $id with moveName: ${tm.moveName}")
} else {
Cobblemon.LOGGER.info("Resolved Move for TM: $id -> ${tm.moveName.name}")
}
tmMap[id] = tm
tm.id = id
moveToTM[tm.moveName] = tm
// Check for passive ObtainMethods
//if (tm.obtainMethods.any { it.passive }) passiveTms[id] = tm
}
}
override fun sync(player: ServerPlayer) { }
fun checkPassives(player: ServerPlayer) {
val playerTms = Cobblemon.playerDataManager.getGenericData(player).tmSet
passiveTms.forEach { (id, tm) ->
if (tm.obtainMethods.all { it.matches(player) } && !playerTms.contains(id)) tm.unlock(player)
}
}
}
@@ -28,10 +28,12 @@ class ElementalType(
val name: String,
val displayName: MutableComponent,
val hue: Int,
val textureXMultiplier: Int,
val textureXMultiplier: Int, val primaryColor: Int,
val resourceLocation: ResourceLocation = ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "ui/types.png"),
val showdownId: String = ShowdownIdentifiable.REGEX.replace(name.lowercase(), "")
) : ShowdownIdentifiable {
val showdownId: String = ShowdownIdentifiable.REGEX.replace(name.lowercase(), ""),
val secondaryColor: Int,
val typeGem: ResourceLocation = ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, showdownId + "_gem"),
) : ShowdownIdentifiable {
override fun showdownId(): String {
return showdownId
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.types
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
@@ -21,135 +22,173 @@ object ElementalTypes {
val NORMAL = register(
name = "Normal",
displayName = Component.translatable("cobblemon.type.normal"),
hue = 0xE8E8DA,
textureXMultiplier = 0
hue = 0xDDDDCF,
textureXMultiplier = 0,
primaryColor = 0x8FA689,
secondaryColor = 0xFFFBE2
)
val FIRE = register(
name = "Fire",
displayName = Component.translatable("cobblemon.type.fire"),
hue = 0xFF6E21,
textureXMultiplier = 1
hue = 0xE55C32,
textureXMultiplier = 1,
primaryColor = 0xF92D2D,
secondaryColor = 0xF99B1D
)
val WATER = register(
name = "Water",
displayName = Component.translatable("cobblemon.type.water"),
hue = 0x3FA5FF,
textureXMultiplier = 2
hue = 0x4A9BE8,
textureXMultiplier = 2,
primaryColor = 0x377AF6,
secondaryColor = 0x50DDF9
)
val GRASS = register(
name = "Grass",
displayName = Component.translatable("cobblemon.type.grass"),
hue = 0x62D14F,
textureXMultiplier = 3
hue = 0x4DBC3C,
textureXMultiplier = 3,
primaryColor = 0x0EAE95,
secondaryColor = 0xC6F554
)
val ELECTRIC = register(
name = "Electric",
displayName = Component.translatable("cobblemon.type.electric"),
hue = 0xFFD314,
textureXMultiplier = 4
hue = 0xEFD128,
textureXMultiplier = 4,
primaryColor = 0xF7AB19,
secondaryColor = 0xFFF257
)
val ICE = register(
name = "Ice",
displayName = Component.translatable("cobblemon.type.ice"),
hue = 0x54F2F2,
textureXMultiplier = 5
hue = 0x6BC3EF,
textureXMultiplier = 5,
primaryColor = 0x41BAED,
secondaryColor = 0x91F4F9
)
val FIGHTING = register(
name = "Fighting",
displayName = Component.translatable("cobblemon.type.fighting"),
hue = 0xEF565D,
textureXMultiplier = 6
hue = 0xC44C5C,
textureXMultiplier = 6,
primaryColor = 0xC8356B,
secondaryColor = 0xF36A3F
)
val POISON = register(
name = "Poison",
displayName = Component.translatable("cobblemon.type.poison"),
hue = 0xD651FF,
textureXMultiplier = 7
hue = 0xA24BD8,
textureXMultiplier = 7,
primaryColor = 0x7B41DD,
secondaryColor = 0xE568F4
)
val GROUND = register(
name = "Ground",
displayName = Component.translatable("cobblemon.type.ground"),
hue = 0xF4A453,
textureXMultiplier = 8
hue = 0xD89950,
textureXMultiplier = 8,
primaryColor = 0xE08835,
secondaryColor = 0xFFD057
)
val FLYING = register(
name = "Flying",
displayName = Component.translatable("cobblemon.type.flying"),
hue = 0xB8B2FF,
textureXMultiplier = 9
hue = 0xBCC1FF,
textureXMultiplier = 9,
primaryColor = 0x6083F6,
secondaryColor = 0xD5CCFA
)
val PSYCHIC = register(
name = "Psychic",
displayName = Component.translatable("cobblemon.type.psychic"),
hue = 0xFF5E9E,
textureXMultiplier = 10
hue = 0xD86AD6,
textureXMultiplier = 10,
primaryColor = 0xE553CE,
secondaryColor = 0xF69DD5
)
val BUG = register(
name = "Bug",
displayName = Component.translatable("cobblemon.type.bug"),
hue = 0xD3D319,
textureXMultiplier = 11
hue = 0xA2C831,
textureXMultiplier = 11,
primaryColor = 0x9FBE1B,
secondaryColor = 0xE9FB5B
)
val ROCK = register(
name = "Rock",
displayName = Component.translatable("cobblemon.type.rock"),
hue = 0xB7A16E,
textureXMultiplier = 12
hue = 0xAA9666,
textureXMultiplier = 12,
primaryColor = 0x9F814A,
secondaryColor = 0xEECB7A
)
val GHOST = register(
name = "Ghost",
displayName = Component.translatable("cobblemon.type.ghost"),
hue = 0x9C80F7,
textureXMultiplier = 13
hue = 0x9572E5,
textureXMultiplier = 13,
primaryColor = 0x4F57DD,
secondaryColor = 0xC182F1
)
val DRAGON = register(
name = "Dragon",
displayName = Component.translatable("cobblemon.type.dragon"),
hue = 0x7580FF,
textureXMultiplier = 14
hue = 0x535DE8,
textureXMultiplier = 14,
primaryColor = 0x4E68E2,
secondaryColor = 0x47C7F7
)
val DARK = register(
name = "Dark",
displayName = Component.translatable("cobblemon.type.dark"),
hue = 0x587DA0,
textureXMultiplier = 15
hue = 0x5C6CB2,
textureXMultiplier = 1,
primaryColor = 0x465B99,
secondaryColor = 0x689EBF
)
val STEEL = register(
name = "Steel",
displayName = Component.translatable("cobblemon.type.steel"),
hue = 0xABD1F4,
textureXMultiplier = 16
hue = 0xC3CCE0,
textureXMultiplier = 16,
primaryColor = 0x628CB9,
secondaryColor = 0xD6F7F3
)
val FAIRY = register(
name = "Fairy",
displayName = Component.translatable("cobblemon.type.fairy"),
hue = 0xFF7FE5,
textureXMultiplier = 17
hue = 0xEA727E,
textureXMultiplier = 17,
primaryColor = 0xE2678C,
secondaryColor = 0xF5A7B7
)
fun register(name: String, displayName: MutableComponent, hue: Int, textureXMultiplier: Int): ElementalType {
fun register(name: String, displayName: MutableComponent, hue: Int, textureXMultiplier: Int, primaryColor: Int, secondaryColor: Int): ElementalType {
return ElementalType(
name = name,
displayName = displayName,
hue = hue,
textureXMultiplier = textureXMultiplier
textureXMultiplier = textureXMultiplier,
primaryColor = primaryColor,
secondaryColor = secondaryColor
).also {
allTypes.add(it)
}
@@ -171,4 +210,4 @@ object ElementalTypes {
fun count() = allTypes.size
fun all() = this.allTypes.toList()
}
}
@@ -21,6 +21,7 @@ import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.DirectionalBlock
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.Property
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.VoxelShape
@@ -42,6 +43,8 @@ abstract class GrowableStoneBlock(
val nextStage: Block?
) : DirectionalBlock(settings) {
abstract val growthChance: Int
private val upShape: VoxelShape = Block.box(
xzOffset.toDouble(),
0.0,
@@ -95,20 +98,26 @@ abstract class GrowableStoneBlock(
.setValue(FACING, Direction.DOWN))
}
abstract fun canGrow(pos: BlockPos, world: BlockGetter): Boolean
abstract fun canGrow(state: BlockState, pos: BlockPos, world: BlockGetter): Boolean
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(FACING)
}
override fun isRandomlyTicking(state: BlockState): Boolean = stage < MAX_STAGE
override fun randomTick(state: BlockState, world: ServerLevel, pos: BlockPos, random: RandomSource) {
if (world.random.nextInt(5) == 0 && canGrow(pos, world)) {
if (world.random.nextInt(growthChance) == 0 && canGrow(state, pos, world)) {
val block = nextStage
if (block != null) {
val newState = block.defaultBlockState().setValue(FACING, state.getValue(FACING)) as BlockState
var newState = block.defaultBlockState()
@Suppress("UNCHECKED_CAST")
for (property in state.properties) {
if (newState.hasProperty(property)) {
newState = newState.setValue(property as Property<Comparable<Any>>, state.getValue(property) as Comparable<Any>)
}
}
world.setBlockAndUpdate(pos, newState)
}
}
@@ -155,14 +164,4 @@ abstract class GrowableStoneBlock(
else -> upShape
}
}
companion object {
const val STAGE_0 = 0
const val STAGE_1 = 1
const val STAGE_2 = 2
const val STAGE_3 = 3
const val MAX_STAGE = STAGE_3
const val MIN_STAGE = STAGE_0
}
}
@@ -0,0 +1,92 @@
/*
* 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.block
import com.cobblemon.mod.common.block.entity.TMShelfBlockEntity
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult
import net.minecraft.world.ItemInteractionResult
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.*
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.block.state.properties.DirectionProperty
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.shapes.VoxelShape
class TMShelfBlock(properties: Properties) : BaseEntityBlock(properties) {
companion object {
val CODEC: MapCodec<TMShelfBlock> = simpleCodec(::TMShelfBlock)
val FACING: DirectionProperty = HorizontalDirectionalBlock.FACING
val SLOT_OCCUPIED_PROPERTIES = (0 until 14).map {
BooleanProperty.create("slot_${it}_occupied")
}
}
init {
val baseState = stateDefinition.any().setValue(FACING, Direction.NORTH)
registerDefaultState(SLOT_OCCUPIED_PROPERTIES.fold(baseState) { state, prop ->
state.setValue(prop, false)
})
}
override fun codec() = CODEC
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
return TMShelfBlockEntity(pos, state)
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(FACING)
SLOT_OCCUPIED_PROPERTIES.forEach { builder.add(it) }
}
override fun getRenderShape(state: BlockState): RenderShape = RenderShape.MODEL
override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState {
return defaultBlockState().setValue(FACING, ctx.horizontalDirection.opposite)
}
override fun useWithoutItem(
state: BlockState, level: Level, pos: BlockPos,
player: Player, hit: BlockHitResult
): InteractionResult {
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity ?: return InteractionResult.PASS
return entity.handleUseWithoutItem(state, level, pos, player, hit)
}
override fun useItemOn(
stack: ItemStack, state: BlockState, level: Level, pos: BlockPos,
player: Player, hand: InteractionHand, hit: BlockHitResult
): ItemInteractionResult {
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity ?: return ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION
return entity.handleUseItem(stack, state, level, pos, player, hit)
}
override fun hasAnalogOutputSignal(state: BlockState) = true
override fun getAnalogOutputSignal(state: BlockState, level: Level, pos: BlockPos): Int {
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity
return entity?.lastInteractedSlot?.plus(1) ?: 0
}
override fun getShape(
state: BlockState, level: BlockGetter,
pos: BlockPos, context: net.minecraft.world.phys.shapes.CollisionContext
): VoxelShape = Block.box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
}
@@ -44,15 +44,25 @@ class TumblestoneBlock(
PrimitiveCodec.INT.fieldOf("xzOffset").forGetter { it.xzOffset },
Block.CODEC.fieldOf("nextStage").forGetter { it.nextStage }
).apply(it, ::TumblestoneBlock) }
const val STAGE_0 = 0
const val STAGE_1 = 1
const val STAGE_2 = 2
const val STAGE_3 = 3
const val MAX_STAGE = STAGE_3
const val MIN_STAGE = STAGE_0
}
override val growthChance = 5
init {
registerDefaultState(stateDefinition.any()
.setValue(FACING, Direction.DOWN)
.setValue(WATERLOGGED, false))
}
override fun canGrow(pos: BlockPos, world: BlockGetter): Boolean {
override fun canGrow(state: BlockState, pos: BlockPos, world: BlockGetter): Boolean {
if (stage == MAX_STAGE) return false
val iterator: Iterator<BlockPos> =
BlockPos.betweenClosed(pos.offset(-1, -1, -1), pos.offset(1, 1, 1))
@@ -66,6 +76,8 @@ class TumblestoneBlock(
return true
}
override fun isRandomlyTicking(state: BlockState): Boolean = stage < MAX_STAGE
override fun codec(): MapCodec<out DirectionalBlock> {
return CODEC
}
@@ -0,0 +1,86 @@
/*
* 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.block
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.server.level.ServerLevel
import net.minecraft.util.RandomSource
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.DirectionalBlock
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.block.state.properties.DirectionProperty
class TypeGemBlock(
settings: Properties,
stage: Int,
height: Int,
xzOffset: Int,
nextStage: Block?
) : GrowableStoneBlock(settings, stage, height, xzOffset, nextStage) {
companion object {
val CODEC: MapCodec<TypeGemBlock> = RecordCodecBuilder.mapCodec { it.group(
propertiesCodec(),
PrimitiveCodec.INT.fieldOf("stage").forGetter { it.stage },
PrimitiveCodec.INT.fieldOf("height").forGetter { it.height },
PrimitiveCodec.INT.fieldOf("xzOffset").forGetter { it.xzOffset },
Block.CODEC.fieldOf("nextStage").forGetter { it.nextStage }
).apply(it, ::TypeGemBlock) }
const val STAGE_0 = 0
const val STAGE_1 = 1
const val MAX_STAGE = STAGE_1
val SHOULD_GROW: BooleanProperty = BooleanProperty.create("should_grow")
val FACING: DirectionProperty = DirectionalBlock.FACING
}
override val growthChance = 1
init {
registerDefaultState(stateDefinition.any()
.setValue(FACING, Direction.DOWN) // Ensure facing is properly registered
.setValue(SHOULD_GROW, true)
)
}
override fun canGrow(state: BlockState, pos: BlockPos, world: BlockGetter): Boolean {
return state.getValue(SHOULD_GROW) && stage < MAX_STAGE
}
override fun isRandomlyTicking(state: BlockState): Boolean {
return state.getValue(SHOULD_GROW) && stage < MAX_STAGE
}
override fun randomTick(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
if (canGrow(state, pos, level)) {
var nextState = nextStage?.defaultBlockState()
if (nextState != null) {
if (nextState.hasProperty(FACING)) {
nextState = nextState.setValue(FACING, state.getValue(FACING)) // Preserve FACING for core
}
level.setBlockAndUpdate(pos, nextState)
}
}
}
override fun codec(): MapCodec<out DirectionalBlock?>? = CODEC
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
super.createBlockStateDefinition(builder)
builder.add(FACING, SHOULD_GROW) // Ensure FACING is registered
}
}
@@ -0,0 +1,213 @@
/*
* 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.block
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerLevel
import net.minecraft.util.RandomSource
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.LevelReader
import net.minecraft.world.level.block.AirBlock
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.DirectionalBlock
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.block.state.properties.DirectionProperty
import net.minecraft.world.level.block.state.properties.IntegerProperty
import net.minecraft.world.level.storage.loot.LootContext
import net.minecraft.world.level.storage.loot.LootParams
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
import javax.swing.text.html.HTML.Attribute.SHAPES
class TypeGemClusterBlock(
settings: Properties,
val nextStage: Block,
val dropItemId: ResourceLocation
) : DirectionalBlock(settings) {
companion object {
val CODEC: MapCodec<TypeGemClusterBlock> = RecordCodecBuilder.mapCodec { instance ->
instance.group(
propertiesCodec(),
Block.CODEC.fieldOf("nextStage").forGetter { it.nextStage },
ResourceLocation.CODEC.fieldOf("dropItem").forGetter { it.dropItemId }
).apply(instance) { settings, nextStage, dropItemId ->
TypeGemClusterBlock(settings, nextStage, dropItemId).also {
gemToClusterMap[nextStage] = it
}
}
}
val gemToClusterMap: MutableMap<Block, TypeGemClusterBlock> = mutableMapOf()
val SHOULD_GROW: BooleanProperty = BooleanProperty.create("should_grow")
val FACING: DirectionProperty = DirectionalBlock.FACING
val STAGE: IntegerProperty = IntegerProperty.create("stage", 0, 3)
val STUNTED: BooleanProperty = BooleanProperty.create("stunted")
fun clusterFromGemBlock(gemBlock: Block): TypeGemClusterBlock? {
return gemToClusterMap[gemBlock]
}
}
init {
registerDefaultState(stateDefinition.any()
.setValue(FACING, Direction.UP)
.setValue(SHOULD_GROW, true)
.setValue(STAGE, 0)
.setValue(STUNTED, false)
)
}
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
val facing = context.clickedFace
val world = context.level
val pos = context.clickedPos.relative(facing.opposite)
val blockBelow = world.getBlockState(pos).block
val shouldGrow = blockBelow is TypeGemCoreBlock
return defaultBlockState()
.setValue(FACING, facing)
.setValue(SHOULD_GROW, shouldGrow)
.setValue(STAGE, 0)
.setValue(STUNTED, false)
}
override fun isRandomlyTicking(state: BlockState): Boolean = state.getValue(SHOULD_GROW)
override fun randomTick(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
if (!state.getValue(SHOULD_GROW)) return
val currentStage = state.getValue(STAGE)
if (currentStage < 3) {
// Progress through stages regardless of STUNTED
level.setBlockAndUpdate(pos, state.setValue(STAGE, currentStage + 1))
return
}
// At STAGE_3
val isStunted = state.getValue(STUNTED)
val facing = state.getValue(FACING)
if (isStunted) {
println("[TypeGemClusterBlock] Cluster at $pos is STUNTED at STAGE_3. Finalizing growth.")
level.setBlockAndUpdate(pos, state.setValue(SHOULD_GROW, false))
return
}
// Log type of cluster based on nextStage
val clusterType = level.registryAccess()
.registryOrThrow(BuiltInRegistries.BLOCK.key())
.getKey(nextStage)
.toString()
println("[TypeGemClusterBlock] Attempting to grow into TypeGemBlock at $pos")
println("[TypeGemClusterBlock] Cluster type: $clusterType")
// Check neighboring gem blocks
val hasConflict = Direction.entries
.filter { it != facing.opposite }
.any { dir ->
val neighborPos = pos.relative(dir)
val neighborState = level.getBlockState(neighborPos)
val neighborBlockId = BuiltInRegistries.BLOCK.getKey(neighborState.block)
println("[TypeGemClusterBlock] Neighbor at $dir -> $neighborBlockId")
isGem(neighborState)
}
if (hasConflict) {
println("[TypeGemClusterBlock] Found nearby TypeGemBlock(s). Stunting cluster at $pos.")
level.setBlockAndUpdate(
pos,
state.setValue(STUNTED, true).setValue(SHOULD_GROW, false)
)
} else {
println("[TypeGemClusterBlock] No conflicts. Converting cluster to TypeGemBlock.")
var nextState = nextStage.defaultBlockState()
if (nextState.hasProperty(FACING)) {
nextState = nextState.setValue(FACING, facing)
}
level.setBlockAndUpdate(pos, nextState)
}
}
private fun isGem(state: BlockState): Boolean {
return state.`is`(CobblemonBlockTags.TYPE_GEM_BLOCKS)
}
override fun getDrops(state: BlockState, params: LootParams.Builder): List<ItemStack> {
val item = BuiltInRegistries.ITEM.getOptional(dropItemId).orElse(Items.AIR)
val count = if (state.getValue(STAGE) >= 3) 2 else 1
return listOf(ItemStack(item, count))
}
override fun getShape(
state: BlockState,
level: net.minecraft.world.level.BlockGetter,
pos: BlockPos,
context: net.minecraft.world.phys.shapes.CollisionContext
): VoxelShape {
val stage = state.getValue(STAGE)
val facing = state.getValue(FACING)
val (min, max) = when (stage) {
0 -> 5.0 to 11.0
1 -> 4.0 to 12.0
2 -> 3.0 to 13.0
3 -> 2.0 to 14.0
else -> return Shapes.empty()
}
return when (facing) {
Direction.UP -> Shapes.box(min / 16, 0.0, min / 16, max / 16, (min + 1) / 16, max / 16)
Direction.DOWN -> Shapes.box(min / 16, (16 - (min + 1)) / 16, min / 16, max / 16, 1.0, max / 16)
Direction.NORTH -> Shapes.box(min / 16, min / 16, (16 - (min + 1)) / 16, max / 16, max / 16, 1.0)
Direction.SOUTH -> Shapes.box((16 - max) / 16, min / 16, 0.0, (16 - min) / 16, max / 16, (min + 1) / 16)
Direction.WEST -> Shapes.box((16 - (min + 1)) / 16, min / 16, min / 16, 1.0, max / 16, max / 16)
Direction.EAST -> Shapes.box(0.0, min / 16, min / 16, (min + 1) / 16, max / 16, max / 16)
}
}
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, level: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
val facing = state.getValue(FACING)
if (direction == facing.opposite && !canSurvive(state, level, pos)) {
return Blocks.AIR.defaultBlockState()
}
return super.updateShape(state, direction, neighborState, level, pos, neighborPos)
}
override fun canSurvive(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
val supportPos = pos.relative(state.getValue(FACING).opposite)
val supportBlock = level.getBlockState(supportPos).block
return supportBlock !is AirBlock
}
override fun codec(): MapCodec<out DirectionalBlock> = CODEC
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
super.createBlockStateDefinition(builder)
builder.add(FACING, SHOULD_GROW, STAGE, STUNTED)
}
}
@@ -0,0 +1,177 @@
/*
* 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.block
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.cobblemon.mod.common.block.TypeGemClusterBlock.Companion.SHOULD_GROW
import com.cobblemon.mod.common.block.TypeGemClusterBlock.Companion.STUNTED
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerLevel
import net.minecraft.util.RandomSource
import net.minecraft.world.level.WorldGenLevel
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.DirectionalBlock
import net.minecraft.world.level.block.state.BlockState
import java.util.*
class TypeGemCoreBlock(properties: Properties) : Block(properties) {
companion object {
const val MAX_CONNECTED_GEMS = 30
const val MIN_DISTANCE_BETWEEN_GEMS = 1
const val CONTINUATION_CHANCE = 0.8f
val BLOCK_TO_CLUSTER: Map<ResourceLocation, Block> by lazy {
mapOf(
ResourceLocation.parse("cobblemon:type_gem_block_normal") to CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL,
ResourceLocation.parse("cobblemon:type_gem_block_fire") to CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE,
ResourceLocation.parse("cobblemon:type_gem_block_water") to CobblemonBlocks.TYPE_GEM_CLUSTER_WATER,
ResourceLocation.parse("cobblemon:type_gem_block_electric") to CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC,
ResourceLocation.parse("cobblemon:type_gem_block_grass") to CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS,
ResourceLocation.parse("cobblemon:type_gem_block_ice") to CobblemonBlocks.TYPE_GEM_CLUSTER_ICE,
ResourceLocation.parse("cobblemon:type_gem_block_fighting") to CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING,
ResourceLocation.parse("cobblemon:type_gem_block_poison") to CobblemonBlocks.TYPE_GEM_CLUSTER_POISON,
ResourceLocation.parse("cobblemon:type_gem_block_ground") to CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND,
ResourceLocation.parse("cobblemon:type_gem_block_flying") to CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING,
ResourceLocation.parse("cobblemon:type_gem_block_psychic") to CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC,
ResourceLocation.parse("cobblemon:type_gem_block_bug") to CobblemonBlocks.TYPE_GEM_CLUSTER_BUG,
ResourceLocation.parse("cobblemon:type_gem_block_rock") to CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK,
ResourceLocation.parse("cobblemon:type_gem_block_ghost") to CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST,
ResourceLocation.parse("cobblemon:type_gem_block_dragon") to CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON,
ResourceLocation.parse("cobblemon:type_gem_block_dark") to CobblemonBlocks.TYPE_GEM_CLUSTER_DARK,
ResourceLocation.parse("cobblemon:type_gem_block_steel") to CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL,
ResourceLocation.parse("cobblemon:type_gem_block_fairy") to CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY
)
}
}
override fun randomTick(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
grow(level, pos, random)
}
fun forceGrow(level: WorldGenLevel, pos: BlockPos, random: RandomSource, percentage: Float) {
val desiredLength = MAX_CONNECTED_GEMS * percentage
while (true) {
val growInfo = grow(level, pos, random, true)
val grown = growInfo.first
val clusterSize = growInfo.second
if (!grown || clusterSize >= desiredLength) break
}
}
private fun grow(
level: WorldGenLevel,
pos: BlockPos,
random: RandomSource,
forced: Boolean = false
): Pair<Boolean, Int> {
val connectedGems = getConnectedGems(level, pos)
val gemCount = connectedGems.count { isGem(it.first) }
val isOverLimit = gemCount >= MAX_CONNECTED_GEMS
val javaRandom = java.util.Random(random.nextLong())
for ((gemState, gemPos) in connectedGems.shuffled(javaRandom)) {
val registryKey = BuiltInRegistries.BLOCK.getKey(gemState.block)
val clusterBlock = BLOCK_TO_CLUSTER[registryKey]
for (dir in Direction.entries.shuffled(javaRandom)) {
val targetPos = gemPos.relative(dir)
val targetState = level.getBlockState(targetPos)
// Allow growth into air or existing clusters
if (!(targetState.isAir || targetState.block is TypeGemClusterBlock)) continue
if (!isPositionValidForGrowth(level, targetPos)) continue
// Place gem block
val gemCopy = gemState.block.defaultBlockState()
level.setBlock(targetPos, gemCopy, UPDATE_ALL)
// if forced immediately surround with clusters
if (forced && clusterBlock != null) {
for (clusterDir in Direction.entries) {
val clusterPos = targetPos.relative(clusterDir)
if (!level.getBlockState(clusterPos).isAir) continue
var clusterState = clusterBlock.defaultBlockState()
if (clusterState.hasProperty(DirectionalBlock.FACING)) {
clusterState = clusterState.setValue(DirectionalBlock.FACING, clusterDir)
}
if (clusterState.hasProperty(SHOULD_GROW)) {
clusterState = clusterState.setValue(SHOULD_GROW, true)
}
if (clusterState.hasProperty(STUNTED)) {
clusterState = clusterState.setValue(STUNTED, isOverLimit)
}
if (clusterState.hasProperty(TypeGemClusterBlock.STAGE)) {
val randomStage = random.nextInt(0, 4)
clusterState = clusterState.setValue(TypeGemClusterBlock.STAGE, randomStage)
}
//println("[TypeGemCoreBlock] Placing ${if (isOverLimit) "STUNTED" else "normal"} cluster at $clusterPos facing ${clusterDir.opposite}")
level.setBlock(clusterPos, clusterState, UPDATE_ALL)
}
}
return Pair(true, connectedGems.size)
}
}
println("[TypeGemCoreBlock] No valid spot found for gem placement.")
return Pair(false, connectedGems.size)
}
private fun getConnectedGems(level: WorldGenLevel, pos: BlockPos): List<Pair<BlockState, BlockPos>> {
val connectedGems = mutableListOf<Pair<BlockState, BlockPos>>()
val visited = mutableSetOf<BlockPos>()
val queue: Queue<BlockPos> = LinkedList()
for (direction in Direction.entries) {
val adjacent = pos.relative(direction)
visited.add(adjacent)
queue.add(adjacent)
}
while (queue.isNotEmpty()) {
val current = queue.poll()
val state = level.getBlockState(current)
if (!isGem(state)) continue
connectedGems.add(state to current)
for (direction in Direction.entries) {
val neighbor = current.relative(direction)
if (visited.add(neighbor)) queue.add(neighbor)
}
}
return connectedGems
}
private fun isPositionValidForGrowth(level: WorldGenLevel, pos: BlockPos): Boolean {
return Direction.entries.none { dir ->
val nearby = pos.relative(dir)
isGem(level.getBlockState(nearby)) &&
pos.distManhattan(nearby) < MIN_DISTANCE_BETWEEN_GEMS
}
}
private fun isGem(state: BlockState): Boolean {
return state.`is`(CobblemonBlockTags.TYPE_GEM_BLOCKS)
}
override fun isRandomlyTicking(state: BlockState): Boolean = true
}
@@ -0,0 +1,302 @@
/*
* 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.block.entity
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.tms.TechnicalMachines
import com.cobblemon.mod.common.block.tmmachine.TMMachineBlock
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
import com.cobblemon.mod.common.client.gui.tmmachine.TMMachineScreen
import com.cobblemon.mod.common.item.components.TMMoveComponent
import com.cobblemon.mod.common.util.itemRegistry
import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup
import net.minecraft.core.NonNullList
import net.minecraft.network.chat.Component
import net.minecraft.network.protocol.Packet
import net.minecraft.network.protocol.game.ClientGamePacketListener
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
import net.minecraft.world.ContainerHelper
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.nbt.CompoundTag
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundSource
import net.minecraft.world.Container
import net.minecraft.world.inventory.ContainerData
import net.minecraft.world.level.Level
class TMMachineBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlockEntity(CobblemonBlockEntities.TM_MACHINE, pos, state) {
companion object {
const val BURN_ACTIVE_TAG = "burnActive"
const val BURN_PROGRESS_TAG = "burnProgress"
const val REPEAT_PROCESS_TAG = "repeatProcess"
const val ACTIVE_MOVE_TAG = "activeMove"
const val BURN_PROGRESS_PER_TICK = 2
const val BURN_TOTAL_TIME = 200
const val TOTAL_PROCESS_TIME = BURN_TOTAL_TIME + ((TMMachineScreen.CRAFT_TICKS + TMMachineScreen.RESET_DISC_TICKS) * BURN_PROGRESS_PER_TICK)
// Container data IDs
const val BURN_PROGRESS_INDEX = 0
const val BURN_ACTIVE_INDEX = 1
const val REPEAT_PROCESS_INDEX = 2
const val POSITION_X_INDEX = 3
const val POSITION_Y_INDEX = 4
const val POSITION_Z_INDEX = 5
fun serverTick(level: Level, pos: BlockPos, state: BlockState, blockEntity: TMMachineBlockEntity) {
if (level.isClientSide) return
val containerData = blockEntity.containerData
if (containerData.get(BURN_ACTIVE_INDEX) == 1) {
val currentProgress = containerData.get(BURN_PROGRESS_INDEX)
if (currentProgress < TOTAL_PROCESS_TIME) {
val progressPerTick = if (currentProgress >= BURN_TOTAL_TIME) 1 else BURN_PROGRESS_PER_TICK
containerData.set(BURN_PROGRESS_INDEX, currentProgress + progressPerTick)
}
}
val burnProgressValue = containerData.get(BURN_PROGRESS_INDEX)
val postCraftTicks = if (burnProgressValue >= BURN_TOTAL_TIME) (burnProgressValue - BURN_TOTAL_TIME) else 0
if (postCraftTicks > 0) {
if (postCraftTicks == TMMachineScreen.CRAFT_TICKS) {
blockEntity.craftTM()
}
if (postCraftTicks >= (TMMachineScreen.CRAFT_TICKS + TMMachineScreen.RESET_DISC_TICKS)) {
val shouldRepeatProcess = containerData.get(REPEAT_PROCESS_INDEX) == 1
if (!shouldRepeatProcess) containerData.set(BURN_ACTIVE_INDEX, 0) // Set active to false if only running once
containerData.set(BURN_PROGRESS_INDEX, 0)
blockEntity.setChanged()
}
}
}
}
var tmMachineInventory = TMMachineBlockInventory(this)
var partialTicks: Float = 0F
var burnProgress : Int = 0
var burnActive : Boolean = false
var repeatProcess: Boolean = false
var activeMove: String = ""
var containerData: ContainerData = object : ContainerData {
override fun get(index: Int): Int {
return when (index) {
BURN_PROGRESS_INDEX -> burnProgress
BURN_ACTIVE_INDEX -> if (burnActive) 1 else 0
REPEAT_PROCESS_INDEX -> if (repeatProcess) 1 else 0
POSITION_X_INDEX -> blockPos.x
POSITION_Y_INDEX -> blockPos.y
POSITION_Z_INDEX -> blockPos.z
else -> 0
}
}
override fun set(index: Int, value: Int) {
when (index) {
BURN_PROGRESS_INDEX -> burnProgress = value
BURN_ACTIVE_INDEX -> burnActive = value == 1
REPEAT_PROCESS_INDEX -> repeatProcess = value == 1
}
}
override fun getCount(): Int {
return 6
}
}
private fun craftTM() {
if (!(level?.isClientSide ?: true)) {
Moves.getByName(activeMove)?.let { move ->
TechnicalMachines.moveToTM[move]?.let { tm ->
val resultStack = getItem(TMMachineMenu.RESULT_SLOT)
if (!resultStack.isEmpty && resultStack.count >= resultStack.maxStackSize) return
// Craft the TM
val craftedTmStack = ItemStack(CobblemonItems.TECHNICAL_MACHINE)
.also { TMMoveComponent.setTMMove(it, tm.moveName) }
if (!resultStack.isEmpty && !ItemStack.isSameItemSameComponents(resultStack, craftedTmStack)) return
val blankTmStack = getItem(TMMachineMenu.BLANK_TM_SLOT)
if (blankTmStack.item != CobblemonItems.BLANK_TM) return
val recipe = tm.getClampedRecipe() ?: emptyList()
for ((index, ingredient) in recipe.withIndex()) {
val slot = TMMachineMenu.INGREDIENT_SLOTS.first + index
val provided = getItem(slot)
val expected = level?.itemRegistry?.get(ingredient.item) ?: return
if (provided.item != expected || provided.count < ingredient.count) return
}
// Consume ingredients
blankTmStack.shrink(1)
for ((index, ingredient) in recipe.withIndex()) {
getItem(TMMachineMenu.INGREDIENT_SLOTS.first + index).shrink(ingredient.count)
}
// Add crafted TM to result slot
if (resultStack.isEmpty) {
setItem(TMMachineMenu.RESULT_SLOT, craftedTmStack)
} else if (resultStack.count < resultStack.maxStackSize) {
resultStack.grow(1)
}
tmMachineInventory.setChanged()
setChanged()
}
}
}
}
override fun createMenu(containerId: Int, inventory: Inventory): AbstractContainerMenu {
return TMMachineMenu(containerId, inventory, tmMachineInventory, containerData)
}
override fun saveAdditional(compound: CompoundTag, registries: HolderLookup.Provider) {
super.saveAdditional(compound, registries)
compound.putBoolean(BURN_ACTIVE_TAG, burnActive)
compound.putBoolean(REPEAT_PROCESS_TAG, repeatProcess)
compound.putInt(BURN_PROGRESS_TAG, burnProgress)
compound.putString(ACTIVE_MOVE_TAG, activeMove)
ContainerHelper.saveAllItems(compound, tmMachineInventory.items, registries)
}
override fun loadAdditional(compound: CompoundTag, registries: HolderLookup.Provider) {
super.loadAdditional(compound, registries)
burnActive = compound.getBoolean(BURN_ACTIVE_TAG)
repeatProcess = compound.getBoolean(REPEAT_PROCESS_TAG)
burnProgress = compound.getInt(BURN_PROGRESS_TAG)
activeMove = compound.getString(ACTIVE_MOVE_TAG)
ContainerHelper.loadAllItems(compound, tmMachineInventory.items, registries)
}
override fun getDisplayName(): Component = Component.translatable("block.cobblemon.tm_machine")
override fun getDefaultName(): Component = Component.translatable("cobblemon.container.tm_machine")
override fun getItems(): NonNullList<ItemStack> = tmMachineInventory.items
override fun setItems(items: NonNullList<ItemStack>) {
for (i in items.indices) {
tmMachineInventory.setItem(i, items[i])
}
}
override fun getUpdatePacket(): Packet<ClientGamePacketListener> = ClientboundBlockEntityDataPacket.create(this)
override fun getUpdateTag(registryLookup: HolderLookup.Provider): CompoundTag = saveWithoutMetadata(registryLookup)
override fun getContainerSize(): Int = tmMachineInventory.containerSize
override fun isEmpty(): Boolean = tmMachineInventory.isEmpty
override fun getItem(slot: Int): ItemStack = tmMachineInventory.getItem(slot)
override fun removeItem(slot: Int, amount: Int): ItemStack = tmMachineInventory.removeItem(slot, amount)
override fun removeItemNoUpdate(slot: Int): ItemStack = tmMachineInventory.removeItemNoUpdate(slot)
override fun setItem(slot: Int, stack: ItemStack) = tmMachineInventory.setItem(slot, stack)
override fun setChanged() {
var currentState = blockState
level?.getBlockState(worldPosition)?.let { state ->
currentState = state
var updated = false
if (state.hasProperty(TMMachineBlock.EMPTY)) {
val isEmpty = getItem(TMMachineMenu.BLANK_TM_SLOT).isEmpty
if (currentState.getValue(TMMachineBlock.EMPTY) != isEmpty) {
if (!isEmpty) playSound(CobblemonSounds.TM_MACHINE_PLACE_DISC, 0.5F)
currentState = currentState.setValue(TMMachineBlock.EMPTY, isEmpty)
updated = true
}
}
if (state.hasProperty(TMMachineBlock.ACTIVE)) {
val isActive = containerData.get(BURN_ACTIVE_INDEX) == 1
if (currentState.getValue(TMMachineBlock.ACTIVE) != isActive) {
currentState = currentState.setValue(TMMachineBlock.ACTIVE, isActive)
updated = true
}
}
if (updated) level?.setBlockAndUpdate(worldPosition, currentState)
}
// Notify the block entity's level that this block entity has changed
level?.blockEntityChanged(worldPosition)
// Mark the chunk containing this block entity as dirty, ensuring it is saved
level?.getChunkAt(worldPosition)?.setUnsaved(true)
// Update Neighbours
level?.updateNeighborsAt(blockPos, currentState.block)
}
override fun stillValid(player: Player): Boolean = tmMachineInventory.stillValid(player)
override fun canPlaceItem(slot: Int, stack: ItemStack): Boolean = tmMachineInventory.canPlaceItem(slot, stack)
override fun canTakeItem(target: Container, slot: Int, stack: ItemStack): Boolean = tmMachineInventory.canTakeItem(target, slot, stack)
fun playSound(soundEvent: SoundEvent, volume: Float = 1F, pitch: Float = 1F) {
level?.playSound(null, worldPosition, soundEvent, SoundSource.BLOCKS, volume, pitch)
}
class TMMachineBlockInventory(val blockEntity: TMMachineBlockEntity) : SimpleContainer(6) {
override fun canTakeItem(target: Container, slot: Int, stack: ItemStack): Boolean =
if (slot == 0) true else false
override fun canPlaceItem(slot: Int, stack: ItemStack): Boolean {
if (blockEntity.containerData.get(BURN_ACTIVE_INDEX) == 1) {
Moves.getByName(blockEntity.activeMove)?.let {
val tm = TechnicalMachines.moveToTM[it] ?: return false
val item = stack.item
return when (slot) {
1 -> item == CobblemonItems.BLANK_TM
2, 3, 4 -> {
val recipe = tm.getClampedRecipe() ?: return false
val recipeIndex = slot - 2
if (recipeIndex >= recipe.size) return false
val expected = blockEntity.level?.itemRegistry?.get(recipe[recipeIndex].item) ?: return false
item == expected
}
else -> false
}
}
}
return false
}
override fun setChanged() {
blockEntity.level?.updateNeighborsAt(blockEntity.blockPos, blockEntity.blockState.block)
super.setChanged()
}
override fun startOpen(player: Player) {
blockEntity.startOpen(player)
}
override fun stopOpen(player: Player) {
blockEntity.stopOpen(player)
}
}
}
@@ -0,0 +1,156 @@
/*
* 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.block.entity
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.block.TMShelfBlock
import com.cobblemon.mod.common.item.interactive.TechnicalMachineItem
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup
import net.minecraft.core.NonNullList
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
import net.minecraft.world.ContainerHelper
import net.minecraft.world.Containers
import net.minecraft.world.InteractionResult
import net.minecraft.world.ItemInteractionResult
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.level.gameevent.GameEvent
import java.util.OptionalInt
class TMShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.TM_SHELF, pos, state) {
val items: NonNullList<ItemStack> = NonNullList.withSize(14, ItemStack.EMPTY)
var lastInteractedSlot: Int = -1
fun handleUseItem(stack: ItemStack, state: BlockState, level: Level, pos: BlockPos, player: Player, hit: BlockHitResult): ItemInteractionResult {
if (stack.isEmpty) return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
val slot = getHitSlot(hit, state).orElse(-1)
if (slot !in 0 until 14) return ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION
if (!isValidItem(stack)) return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
val oldStack = items[slot]
if (!oldStack.isEmpty) {
if (!player.addItem(oldStack)) {
Containers.dropItemStack(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, oldStack)
}
}
items[slot] = stack.copyWithCount(1)
stack.shrink(1)
lastInteractedSlot = slot
updateBlockState(level, pos)
markUpdated()
return ItemInteractionResult.sidedSuccess(level.isClientSide)
}
fun handleUseWithoutItem(state: BlockState, level: Level, pos: BlockPos, player: Player, hit: BlockHitResult): InteractionResult {
val slot = getHitSlot(hit, state).orElse(-1)
if (slot !in 0 until 14 || items[slot].isEmpty) return InteractionResult.PASS
val removed = items[slot]
items[slot] = ItemStack.EMPTY
if (!player.addItem(removed)) {
Containers.dropItemStack(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, removed)
}
lastInteractedSlot = slot
updateBlockState(level, pos)
return InteractionResult.sidedSuccess(level.isClientSide)
}
private fun updateBlockState(level: Level, pos: BlockPos) {
val newState = TMShelfBlock.SLOT_OCCUPIED_PROPERTIES.fold(blockState) { acc, prop ->
val index = TMShelfBlock.SLOT_OCCUPIED_PROPERTIES.indexOf(prop)
acc.setValue(prop, !items[index].isEmpty)
}
level.setBlock(pos, newState, 3)
level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(newState))
}
private fun getHitSlot(hit: BlockHitResult, state: BlockState): OptionalInt {
val facing = state.getValue(TMShelfBlock.FACING)
val relative = hit.location.subtract(hit.blockPos.x.toDouble(), hit.blockPos.y.toDouble(), hit.blockPos.z.toDouble())
val (x, y) = when (facing) {
Direction.NORTH -> 1.0 - relative.x to relative.y
Direction.SOUTH -> relative.x to relative.y
Direction.WEST -> 1.0 - relative.z to relative.y
Direction.EAST -> relative.z to relative.y
else -> return OptionalInt.empty()
}
val col = if (x < 0.5) 1 else 0
val row = ((1.0 - y) * 7).toInt().coerceIn(0, 6)
return OptionalInt.of(row * 2 + col)
}
fun isValidItem(stack: ItemStack): Boolean {
return stack.item is TechnicalMachineItem
|| stack.item == CobblemonItems.UPGRADE
|| stack.item == CobblemonItems.DUBIOUS_DISC
|| stack.item == Items.MUSIC_DISC_13
|| stack.item == Items.MUSIC_DISC_CAT
|| stack.item == Items.MUSIC_DISC_BLOCKS
|| stack.item == Items.MUSIC_DISC_CHIRP
|| stack.item == Items.MUSIC_DISC_FAR
|| stack.item == Items.MUSIC_DISC_MALL
|| stack.item == Items.MUSIC_DISC_MELLOHI
|| stack.item == Items.MUSIC_DISC_STAL
|| stack.item == Items.MUSIC_DISC_STRAD
|| stack.item == Items.MUSIC_DISC_WARD
|| stack.item == Items.MUSIC_DISC_11
|| stack.item == Items.MUSIC_DISC_WAIT
|| stack.item == Items.MUSIC_DISC_OTHERSIDE
|| stack.item == Items.MUSIC_DISC_5
|| stack.item == Items.MUSIC_DISC_RELIC
|| stack.item == Items.MUSIC_DISC_PRECIPICE
|| stack.item == Items.MUSIC_DISC_CREATOR
|| stack.item == Items.MUSIC_DISC_CREATOR_MUSIC_BOX
|| stack.item == Items.MUSIC_DISC_PIGSTEP
}
fun markUpdated() {
level?.setBlock(blockPos, blockState, 3)
level?.sendBlockUpdated(blockPos, blockState, blockState, 3)
setChanged()
}
override fun getUpdateTag(registries: HolderLookup.Provider): CompoundTag {
return saveWithoutMetadata(registries)
}
override fun getUpdatePacket(): ClientboundBlockEntityDataPacket {
return ClientboundBlockEntityDataPacket.create(this)
}
override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.saveAdditional(tag, registries)
ContainerHelper.saveAllItems(tag, items, true, registries)
tag.putInt("LastInteractedSlot", lastInteractedSlot)
}
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.loadAdditional(tag, registries)
items.clear()
ContainerHelper.loadAllItems(tag, items, registries)
lastInteractedSlot = tag.getInt("LastInteractedSlot")
}
}
@@ -0,0 +1,217 @@
/*
* 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.block.tmmachine
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.block.entity.TMMachineBlockEntity
import com.cobblemon.mod.common.util.playSoundServer
import com.cobblemon.mod.common.util.toVec3d
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.Containers
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.*
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityTicker
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.gameevent.GameEvent
import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.material.Fluids
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
import org.jetbrains.annotations.Nullable
class TMMachineBlock(properties: Properties) : BaseEntityBlock(properties), SimpleWaterloggedBlock {
companion object {
val EMPTY: BooleanProperty = BooleanProperty.create("empty")
val FACING = BlockStateProperties.HORIZONTAL_FACING
val WATERLOGGED = BlockStateProperties.WATERLOGGED
val ACTIVE: BooleanProperty = BooleanProperty.create("active")
val OPEN: BooleanProperty = BooleanProperty.create("open")
private val SHAPE: VoxelShape = Shapes.box(0.0, 0.0, 0.0, 1.0, 0.375, 1.0)
private val SHAPE_OPEN: VoxelShape = Shapes.box(0.0, 0.0, 0.0, 1.0, 0.3125, 1.0)
private val SHAPE_OPEN_NORTH: VoxelShape = Shapes.or(
Shapes.box(0.0, 0.0, 0.0, 1.0, 0.3125, 1.0),
Shapes.box(0.0, 0.3125, 0.8125, 1.0, 0.4375, 0.9375),
Shapes.box(0.0, 0.4375, 0.6875, 1.0, 0.5625, 0.8125),
Shapes.box(0.0, 0.5625, 0.5625, 1.0, 0.6875, 0.6875),
Shapes.box(0.0, 0.6875, 0.4375, 1.0, 0.8125, 0.5625),
Shapes.box(0.0, 0.8125, 0.3125, 1.0, 0.9375, 0.4375),
Shapes.box(0.0, 0.9375, 0.3125, 1.0, 1.0, 0.375)
)
private val SHAPE_OPEN_SOUTH: VoxelShape = Shapes.or(
Shapes.box(0.0, 0.0, 0.0, 1.0, 0.3125, 1.0),
Shapes.box(0.0, 0.3125, 0.0625, 1.0, 0.4375, 0.1875),
Shapes.box(0.0, 0.4375, 0.1875, 1.0, 0.5625, 0.3125),
Shapes.box(0.0, 0.5625, 0.3125, 1.0, 0.6875, 0.4375),
Shapes.box(0.0, 0.6875, 0.4375, 1.0, 0.8125, 0.5625),
Shapes.box(0.0, 0.8125, 0.5625, 1.0, 0.9375, 0.6875),
Shapes.box(0.0, 0.9375, 0.625, 1.0, 1.0, 0.6875)
)
private val SHAPE_OPEN_EAST: VoxelShape = Shapes.or(
Shapes.box(0.0, 0.0, 0.0, 1.0, 0.3125, 1.0),
Shapes.box(0.0625, 0.3125, 0.0, 0.1875, 0.4375, 1.0),
Shapes.box(0.1875, 0.4375, 0.0, 0.3125, 0.5625, 1.0),
Shapes.box(0.3125, 0.5625, 0.0, 0.4375, 0.6875, 1.0),
Shapes.box(0.4375, 0.6875, 0.0, 0.5625, 0.8125, 1.0),
Shapes.box(0.5625, 0.8125, 0.0, 0.6875, 0.9375, 1.0),
Shapes.box(0.625, 0.9375, 0.0, 0.6875, 1.0, 1.0)
)
private val SHAPE_OPEN_WEST: VoxelShape = Shapes.or(
Shapes.box(0.0, 0.0, 0.0, 1.0, 0.3125, 1.0),
Shapes.box(0.8125, 0.3125, 0.0, 0.9375, 0.4375, 1.0),
Shapes.box(0.6875, 0.4375, 0.0, 0.8125, 0.5625, 1.0),
Shapes.box(0.5625, 0.5625, 0.0, 0.6875, 0.6875, 1.0),
Shapes.box(0.4375, 0.6875, 0.0, 0.5625, 0.8125, 1.0),
Shapes.box(0.3125, 0.8125, 0.0, 0.4375, 0.9375, 1.0),
Shapes.box(0.3125, 0.9375, 0.0, 0.375, 1.0, 1.0)
)
val CODEC: MapCodec<TMMachineBlock> = RecordCodecBuilder.mapCodec { instance ->
instance.group(
Properties.CODEC.fieldOf("properties").forGetter { it.properties }
).apply(instance, ::TMMachineBlock)
}
}
init {
registerDefaultState(stateDefinition.any()
.setValue(FACING, Direction.NORTH)
.setValue(WATERLOGGED, false)
.setValue(ACTIVE, false)
.setValue(EMPTY, true))
}
override fun <T : BlockEntity?> getTicker(level: Level, state: BlockState, blockEntityType: BlockEntityType<T?>): BlockEntityTicker<T?>? {
return createTicker(level, blockEntityType as BlockEntityType<*>, CobblemonBlockEntities.TM_MACHINE) as BlockEntityTicker<T?>?
}
@Nullable
protected fun <T : BlockEntity> createTicker(level: Level, serverType: BlockEntityType<T>, clientType: BlockEntityType<out TMMachineBlockEntity>): BlockEntityTicker<T>? {
return if (!level.isClientSide) createTickerHelper(serverType, clientType, TMMachineBlockEntity::serverTick)
else null
}
override fun fallOn(level: Level, state: BlockState, pos: BlockPos, entity: Entity, fallDistance: Float) {
if (!level.isClientSide && (entity is LivingEntity) &&
(entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) &&
(entity.y >= (pos.y + 0.55)) &&
state.hasProperty(OPEN) && state.getValue(OPEN)
) {
level.setBlockAndUpdate(pos, state.setValue(OPEN, false))
level.playSoundServer(position = pos.toVec3d(), sound = CobblemonSounds.TM_MACHINE_CLOSE, volume = 1F, pitch = 1F)
}
}
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = TMMachineBlockEntity(pos, state)
override fun getStateForPlacement(context: BlockPlaceContext): BlockState {
return defaultBlockState()
.setValue(FACING, context.horizontalDirection.opposite)
.setValue(WATERLOGGED, context.level.getFluidState(context.clickedPos).type == Fluids.WATER)
.setValue(EMPTY, true)
.setValue(ACTIVE, false)
.setValue(OPEN, false)
}
override fun useWithoutItem(state: BlockState, level: Level, pos: BlockPos, player: Player, hit: BlockHitResult): InteractionResult {
if (!level.isClientSide) {
if (player.isCrouching) {
if (state.hasProperty(OPEN)) {
val isOpen = state.getValue(OPEN)
level.setBlockAndUpdate(pos, state.setValue(OPEN, !isOpen))
level.playSoundServer(
position = pos.toVec3d(),
sound = if (!isOpen) CobblemonSounds.TM_MACHINE_CLOSE else CobblemonSounds.TM_MACHINE_OPEN,
volume = 1F,
pitch = 1F
)
}
} else {
val blockEntity = level.getBlockEntity(pos)
if (blockEntity is TMMachineBlockEntity) {
player.openMenu(blockEntity)
level.playSoundServer(
position = pos.toVec3d(),
sound = CobblemonSounds.TM_MACHINE_ON,
volume = 0.5F,
pitch = 1F
)
level.gameEvent(player, GameEvent.BLOCK_OPEN, pos)
}
}
}
return InteractionResult.sidedSuccess(level.isClientSide)
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(FACING, WATERLOGGED, EMPTY, ACTIVE, OPEN)
}
override fun codec(): MapCodec<TMMachineBlock> = CODEC
override fun getRenderShape(state: BlockState): RenderShape = RenderShape.MODEL
override fun getFluidState(state: BlockState): FluidState =
if (state.getValue(WATERLOGGED)) Fluids.WATER.defaultFluidState() else super.getFluidState(state)
override fun getCollisionShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
return if (state.getValue(OPEN)) {
when (state.getValue(FACING)) {
Direction.NORTH -> SHAPE_OPEN_NORTH
Direction.SOUTH -> SHAPE_OPEN_SOUTH
Direction.WEST -> SHAPE_OPEN_WEST
Direction.EAST -> SHAPE_OPEN_EAST
else -> Shapes.empty()
}
} else SHAPE
}
override fun getShape(state: BlockState, getter: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape = if (state.getValue(OPEN)) SHAPE_OPEN else SHAPE
override fun onRemove(state: BlockState, level: Level, pos: BlockPos, newState: BlockState, movedByPiston: Boolean) {
Containers.dropContentsOnDestroy(state, newState, level, pos)
super.onRemove(state, level, pos, newState, movedByPiston)
}
override fun isSignalSource(state: BlockState): Boolean = true
override fun getSignal(state: BlockState, level: BlockGetter, pos: BlockPos, direction: Direction): Int {
level.getBlockEntity(pos)?.let { blockEntity ->
if (blockEntity is TMMachineBlockEntity && blockEntity.burnActive) return 15
}
return super.getSignal(state, level, pos, direction)
}
override fun hasAnalogOutputSignal(state: BlockState): Boolean = true
override fun getAnalogOutputSignal(state: BlockState, level: Level, pos: BlockPos): Int = getSignal(state, level, pos, Direction.UP)
}
@@ -0,0 +1,212 @@
/*
* 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.block.tmmachine
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonMenuType
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.tms.TechnicalMachines
import com.cobblemon.mod.common.block.entity.TMMachineBlockEntity
import com.cobblemon.mod.common.util.itemRegistry
import net.minecraft.server.level.ServerLevel
import net.minecraft.util.Mth
import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.*
import net.minecraft.world.item.ItemStack
class TMMachineMenu(menuType: MenuType<*>, syncId: Int) : AbstractContainerMenu(menuType, syncId), ContainerListener {
companion object {
const val RESULT_SLOT = 0
const val BLANK_TM_SLOT = 1
val INGREDIENT_SLOTS = 2..4
}
var playerInventory: Inventory? = null
var tmMachineEntity: TMMachineBlockEntity? = null
var inventory: Container? = null
var containerData: ContainerData? = null
constructor(syncId: Int, playerInventory: Inventory) : this(CobblemonMenuType.TM_MACHINE, syncId) {
this.playerInventory = playerInventory
this.inventory = SimpleContainer(5)
this.containerData = SimpleContainerData(6)
this.tmMachineEntity = (inventory as? TMMachineBlockEntity.TMMachineBlockInventory)?.blockEntity
this.addDataSlots(containerData)
inventory?.startOpen(playerInventory.player)
val startX = 15
val startY = 144
val slotWidth = 18
val slotHeight = 18
for (row in 0..2) {
for (col in 0..8) {
this.addSlot(Slot(playerInventory, 9 + (row * 9) + col, startX + (slotWidth * col), startY + (slotHeight * row)))
}
}
for (col in 0..8) {
this.addSlot(Slot(playerInventory, col, startX + (slotWidth * col), startY + 58))
}
val container = inventory
container?.let {
this.addSlot(TMResultSlot(container, RESULT_SLOT, startX + 37, startY - 28)) // Output slot
this.addSlot(BlankTMSlot(container, BLANK_TM_SLOT, startX + 90, startY - 28)) // Blank TM
for (index in INGREDIENT_SLOTS) {
this.addSlot(Slot(container, index, startX + 114 + (18 * (index - INGREDIENT_SLOTS.first)), startY - 28))
}
}
this.addSlotListener(this)
}
constructor(syncId: Int, playerInventory: Inventory, inventory: Container, containerData: ContainerData) : this(CobblemonMenuType.TM_MACHINE, syncId) {
this.playerInventory = playerInventory
this.inventory = inventory
this.containerData = containerData
this.tmMachineEntity = (inventory as? TMMachineBlockEntity.TMMachineBlockInventory)?.blockEntity
this.addDataSlots(containerData)
inventory.startOpen(playerInventory.player)
val startX = 15
val startY = 144
val slotWidth = 18
val slotHeight = 18
for (row in 0..2) {
for (col in 0..8) {
this.addSlot(Slot(playerInventory, 9 + (row * 9) + col, startX + (slotWidth * col), startY + (slotHeight * row)))
}
}
for (col in 0..8) {
this.addSlot(Slot(playerInventory, col, startX + (slotWidth * col), startY + 58))
}
val container = inventory
container.let {
this.addSlot(TMResultSlot(container, RESULT_SLOT, startX + 37, startY - 28)) // Output slot
this.addSlot(BlankTMSlot(container, BLANK_TM_SLOT, startX + 90, startY - 28)) // Blank TM
for (index in INGREDIENT_SLOTS) {
this.addSlot(Slot(container, index, startX + 114 + (18 * (index - INGREDIENT_SLOTS.first)), startY - 28))
}
}
this.addSlotListener(this)
}
override fun quickMoveStack(player: Player, index: Int): ItemStack {
var itemStack = ItemStack.EMPTY
val slot = this.slots[index]
val maxPlayerInventoryIndex = Inventory.INVENTORY_SIZE - 1
if (slot.hasItem()) {
val movedStack = slot.item
itemStack = movedStack.copy()
// If in container slots, move to player inventory
// 36: Output slot, 37: Blank TM slot, 38-40: Ingredient slots
if (index in 36..40) {
if (!this.moveItemStackTo(movedStack, 0, maxPlayerInventoryIndex, false)) return ItemStack.EMPTY
}
// If in player inventory, move to container slots. 'endIndex' is non-inclusive
else if (index <= maxPlayerInventoryIndex) {
if (!this.moveItemStackTo(movedStack, 37, 40 + 1, false)) return ItemStack.EMPTY
}
if (movedStack.isEmpty) {
slot.setByPlayer(ItemStack.EMPTY)
} else {
slot.setChanged()
}
}
return itemStack
}
override fun stillValid(player: Player): Boolean {
return player.let { inventory?.stillValid(it) } ?: false
}
fun getBurnProgressValue(): Int = this.containerData?.get(TMMachineBlockEntity.BURN_PROGRESS_INDEX) ?: 0
fun getBurnProgressRatio(): Float {
val amount = getBurnProgressValue()
val max = TMMachineBlockEntity.BURN_TOTAL_TIME
return if (amount != 0) {
Mth.clamp((amount.toFloat() / max.toFloat()), 0.0F, 1.0F)
} else {
0.0F
}
}
fun getPostCraftTicks(): Int {
return if (getBurnProgressValue() >= TMMachineBlockEntity.BURN_TOTAL_TIME) {
getBurnProgressValue() - TMMachineBlockEntity.BURN_TOTAL_TIME
} else 0
}
override fun removed(player: Player) {
super.removed(player)
tmMachineEntity?.setChanged()
}
class TMResultSlot(container: Container, slot: Int, x: Int, y: Int) : Slot(container, slot, x, y) {
override fun mayPlace(stack: ItemStack): Boolean = false
}
class BlankTMSlot(container: Container, slot: Int, x: Int, y: Int) : Slot(container, slot, x, y) {
override fun mayPlace(stack: ItemStack): Boolean = super.mayPlace(stack) && (stack.item == CobblemonItems.BLANK_TM)
}
override fun slotChanged(containerToSend: AbstractContainerMenu, dataSlotIndex: Int, stack: ItemStack) {
val level = playerInventory?.player?.level()
// If ingredient slots have been modified and level is server side and process is active
// 36: Output slot, 37: Blank TM slot, 38-40: Ingredient slots
if ((dataSlotIndex in 37 .. 40) && (level?.isClientSide == false) && (containerData?.get(TMMachineBlockEntity.BURN_ACTIVE_INDEX) == 1)) {
// Stop process if there are not enough ingredients after slots have been modified
var validCost = !(inventory?.getItem(BLANK_TM_SLOT)?.isEmpty ?: true)
if (validCost) {
Moves.getByName(tmMachineEntity?.activeMove ?: "")?.let { moveTemplate ->
val serverLevel = level as ServerLevel
TechnicalMachines.moveToTM[moveTemplate]?.let { tm ->
val recipe = tm.getClampedRecipe() ?: emptyList()
for ((index, ingredient) in recipe.withIndex()) {
val slot = INGREDIENT_SLOTS.first + index
val provided = inventory?.getItem(slot) ?: ItemStack.EMPTY
val expected = serverLevel.itemRegistry.get(ingredient.item) ?: ItemStack.EMPTY.item
if (provided?.item != expected || provided.count < ingredient.count) {
validCost = false
break
}
}
}
}
}
if (!validCost) {
containerData?.set(TMMachineBlockEntity.BURN_ACTIVE_INDEX, 0)
containerData?.set(TMMachineBlockEntity.BURN_PROGRESS_INDEX, 0)
}
}
tmMachineEntity?.setChanged()
inventory?.setChanged()
playerInventory?.setChanged()
broadcastChanges()
}
override fun dataChanged(containerMenu: AbstractContainerMenu, dataSlotIndex: Int, value: Int) {
broadcastChanges()
}
}
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.berry.Berries
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
import com.cobblemon.mod.common.api.storage.player.client.ClientGeneralPlayerData
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
import com.cobblemon.mod.common.api.storage.player.client.ClientTMMoveManager
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.block.entity.TintBlockEntity
import com.cobblemon.mod.common.client.battle.ClientBattle
@@ -26,11 +27,13 @@ import com.cobblemon.mod.common.client.gui.PartyOverlay
import com.cobblemon.mod.common.client.gui.RideControlsOverlay
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotScreen
import com.cobblemon.mod.common.client.gui.tmmachine.TMMachineScreen
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
import com.cobblemon.mod.common.client.render.DeferredRenderer
import com.cobblemon.mod.common.client.render.block.*
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
import com.cobblemon.mod.common.client.render.color.TechnicalMachineItemColorProvider
import com.cobblemon.mod.common.client.render.color.AprijuiceItemColorProvider
import com.cobblemon.mod.common.client.render.color.PokeBaitItemColorProvider
import com.cobblemon.mod.common.client.render.color.PokeSnackItemColorProvider
@@ -73,6 +76,7 @@ import net.minecraft.client.renderer.blockentity.HangingSignRenderer
import net.minecraft.client.renderer.blockentity.SignRenderer
import net.minecraft.client.renderer.entity.EntityRenderer
import net.minecraft.client.renderer.entity.LivingEntityRenderer
import net.minecraft.client.renderer.entity.layers.RenderLayer
import net.minecraft.client.resources.PlayerSkin
import net.minecraft.server.packs.resources.ResourceManager
import net.minecraft.world.InteractionHand
@@ -87,6 +91,7 @@ object CobblemonClient {
var battle: ClientBattle? = null
var clientPlayerData = ClientGeneralPlayerData()
var clientPokedexData = ClientPokedexManager(mutableMapOf())
var clientTMMoveData = ClientTMMoveManager(mutableSetOf())
/** If true then we won't bother them anymore about choosing a starter even if it's a thing they can do. */
var checkedStarterScreen = false
@@ -103,6 +108,7 @@ object CobblemonClient {
requests = ClientPlayerActionRequests()
teamData = ClientPlayerTeamData()
clientPokedexData = ClientPokedexManager(mutableMapOf())
clientTMMoveData = ClientTMMoveManager(mutableSetOf())
storage.onLogin()
CobblemonDataProvider.canReload = false
}
@@ -154,9 +160,9 @@ object CobblemonClient {
if (pokedexUsageContext.scanningGuiOpen &&
!(selectedItem.`is`(CobblemonItemTags.POKEDEX)) &&
!(player.offhandItem.`is`(CobblemonItemTags.POKEDEX) &&
player.isUsingItem == true &&
player.usedItemHand == InteractionHand.OFF_HAND
)
player.isUsingItem == true &&
player.usedItemHand == InteractionHand.OFF_HAND
)
) {
// Stop using Pokédex in main hand if player switches to a different slot in hotbar
pokedexUsageContext.stopUsing(PokedexUsageContext.OPEN_SCANNER_BUFFER_TICKS + 1)
@@ -165,10 +171,7 @@ object CobblemonClient {
return@subscribe
}
val nearbyPokemon = player.level().getEntities(
player,
AABB.ofSize(player.position(), 16.0, 16.0, 16.0)
) { it is PokemonEntity }
val nearbyPokemon = player.level().getEntities(player, AABB.ofSize(player.position(), 16.0, 16.0, 16.0)) { it is PokemonEntity }
nearbyPokemon?.forEach { entity ->
if (entity is PokemonEntity && !entity.isSilent) {
@@ -188,6 +191,7 @@ object CobblemonClient {
TooltipManager.registerTooltipGenerator(SeasoningTooltipGenerator)
TooltipManager.registerTooltipGenerator(FishingRodTooltipGenerator)
TooltipManager.registerTooltipGenerator(AprijuiceTooltipGenerator)
TooltipManager.registerTooltipGenerator(TechnicalMachineTooltipGenerator)
TooltipManager.registerTooltipGenerator(PokePuffTooltipGenerator)
}
@@ -268,6 +272,7 @@ object CobblemonClient {
CobblemonBlocks.POTTED_PEP_UP_FLOWER,
CobblemonBlocks.REVIVAL_HERB,
*CobblemonBlocks.berries().values.toTypedArray(),
CobblemonBlocks.POTTED_PEP_UP_FLOWER,
CobblemonBlocks.GALARICA_NUT_BUSH,
CobblemonBlocks.RESTORATION_TANK,
CobblemonBlocks.SMALL_BUDDING_TUMBLESTONE,
@@ -278,6 +283,24 @@ object CobblemonClient {
CobblemonBlocks.MEDIUM_BUDDING_BLACK_TUMBLESTONE,
CobblemonBlocks.LARGE_BUDDING_BLACK_TUMBLESTONE,
CobblemonBlocks.BLACK_TUMBLESTONE_CLUSTER,
CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL,
CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE,
CobblemonBlocks.TYPE_GEM_CLUSTER_WATER,
CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC,
CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS,
CobblemonBlocks.TYPE_GEM_CLUSTER_ICE,
CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING,
CobblemonBlocks.TYPE_GEM_CLUSTER_POISON,
CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND,
CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING,
CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC,
CobblemonBlocks.TYPE_GEM_CLUSTER_BUG,
CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK,
CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST,
CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON,
CobblemonBlocks.TYPE_GEM_CLUSTER_DARK,
CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL,
CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY,
CobblemonBlocks.SMALL_BUDDING_SKY_TUMBLESTONE,
CobblemonBlocks.MEDIUM_BUDDING_SKY_TUMBLESTONE,
CobblemonBlocks.LARGE_BUDDING_SKY_TUMBLESTONE,
@@ -302,7 +325,8 @@ object CobblemonClient {
CobblemonBlocks.PINK_CAMPFIRE_POT,
CobblemonBlocks.RED_CAMPFIRE_POT,
CobblemonBlocks.WHITE_CAMPFIRE_POT,
CobblemonBlocks.YELLOW_CAMPFIRE_POT
CobblemonBlocks.YELLOW_CAMPFIRE_POT,
CobblemonBlocks.TM_MACHINE
)
this.createBoatModelLayers()
@@ -321,8 +345,7 @@ object CobblemonClient {
@Suppress("UNCHECKED_CAST")
fun onAddLayer(skinMap: Map<PlayerSkin.Model, EntityRenderer<out Player>>?) {
var renderer: LivingEntityRenderer<Player, PlayerModel<Player>>? =
skinMap?.get(PlayerSkin.Model.WIDE) as LivingEntityRenderer<Player, PlayerModel<Player>>
var renderer: LivingEntityRenderer<Player, PlayerModel<Player>>? = skinMap?.get(PlayerSkin.Model.WIDE) as LivingEntityRenderer<Player, PlayerModel<Player>>
renderer?.addLayer(PokemonOnShoulderRenderer(renderer))
renderer = skinMap[PlayerSkin.Model.SLIM] as LivingEntityRenderer<Player, PlayerModel<Player>>?
renderer?.addLayer(PokemonOnShoulderRenderer(renderer))
@@ -330,29 +353,22 @@ object CobblemonClient {
private fun registerMenuScreens() {
MenuScreens.register(CobblemonMenuType.COOKING_POT, ::CookingPotScreen)
MenuScreens.register(CobblemonMenuType.TM_MACHINE, ::TMMachineScreen)
}
private fun registerBlockEntityRenderers() {
this.implementation.registerBlockEntityRenderer(
CobblemonBlockEntities.HEALING_MACHINE,
::HealingMachineRenderer
)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.HEALING_MACHINE, ::HealingMachineRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.BERRY, ::BerryBlockRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.SIGN, ::SignRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.HANGING_SIGN, ::HangingSignRenderer)
this.implementation.registerBlockEntityRenderer(
CobblemonBlockEntities.FOSSIL_ANALYZER,
::FossilAnalyzerRenderer
)
this.implementation.registerBlockEntityRenderer(
CobblemonBlockEntities.RESTORATION_TANK,
::RestorationTankRenderer
)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.FOSSIL_ANALYZER, ::FossilAnalyzerRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.RESTORATION_TANK, ::RestorationTankRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.GILDED_CHEST, ::GildedChestBlockRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.DISPLAY_CASE, ::DisplayCaseRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LECTERN, ::LecternBlockEntityRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.CAMPFIRE, ::CampfireBlockEntityRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.POKE_SNACK, ::PokeSnackBlockEntityRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.TM_SHELF, ::TMShelfBlockEntityRenderer)
}
private fun registerEntityRenderers() {
@@ -363,12 +379,7 @@ object CobblemonClient {
LOGGER.info("Registering Boat renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.BOAT) { ctx -> CobblemonBoatRenderer(ctx, false) }
LOGGER.info("Registering Boat with Chest renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.CHEST_BOAT) { ctx ->
CobblemonBoatRenderer(
ctx,
true
)
}
this.implementation.registerEntityRenderer(CobblemonEntities.CHEST_BOAT) { ctx -> CobblemonBoatRenderer(ctx, true) }
LOGGER.info("Registering Generic Bedrock renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.GENERIC_BEDROCK_ENTITY, ::GenericBedrockRenderer)
LOGGER.info("Registering Generic Bedrock Entity renderer")
@@ -385,6 +396,7 @@ object CobblemonClient {
implementation.registerItemColors(PokeBaitItemColorProvider, CobblemonItems.POKE_BAIT)
implementation.registerItemColors(PonigiriItemColorProvider, CobblemonItems.PONIGIRI)
implementation.registerItemColors(SinisterTeaItemColorProvider, CobblemonItems.SINISTER_TEA)
implementation.registerItemColors(TechnicalMachineItemColorProvider, CobblemonItems.TECHNICAL_MACHINE)
}
fun reloadCodedAssets(resourceManager: ResourceManager) {
@@ -411,14 +423,8 @@ object CobblemonClient {
private fun createBoatModelLayers() {
CobblemonBoatType.entries.forEach { type ->
this.implementation.registerLayer(
CobblemonBoatRenderer.createBoatModelLayer(type, false),
BoatModel::createBodyModel
)
this.implementation.registerLayer(
CobblemonBoatRenderer.createBoatModelLayer(type, true),
ChestBoatModel::createBodyModel
)
this.implementation.registerLayer(CobblemonBoatRenderer.createBoatModelLayer(type, false), BoatModel::createBodyModel)
this.implementation.registerLayer(CobblemonBoatRenderer.createBoatModelLayer(type, true), ChestBoatModel::createBodyModel)
}
}
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.gui.interact.moveselect
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.gold
import com.cobblemon.mod.common.api.text.red
@@ -21,6 +20,7 @@ import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
import com.cobblemon.mod.common.client.gui.TypeIcon
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.toRGB
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
@@ -31,96 +31,92 @@ import net.minecraft.util.Mth
class MoveSlotButton(
x: Int, y: Int,
val move: MoveTemplate,
val pp: Int,
val ppMax: Int,
val enabled: Boolean = true,
var move: MoveTemplate? = null,
var pp: Int = 0,
var ppMax: Int = 0,
var ppAsFraction: Boolean = true,
var enabled: Boolean = true,
onPress: OnPress
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Move"), onPress, CreateNarration { "".text() }), CobblemonRenderable {
) : Button(x, y, WIDTH, HEIGHT, move?.name?.text() ?: lang("ui.moves"), onPress, CreateNarration { move?.name?.text() ?: lang("ui.moves") }), CobblemonRenderable {
companion object {
private val moveResource = cobblemonResource("textures/gui/summary/summary_move.png")
private val moveDisabledResource = cobblemonResource("textures/gui/summary/summary_move_disabled.png")
private val moveOverlayResource = cobblemonResource("textures/gui/summary/summary_move_overlay.png")
const val WIDTH = 108
const val HEIGHT = 22
}
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
isHovered = pMouseX >= x && pMouseY >= y && pMouseX < x + width && pMouseY < y + height && enabled
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, pPartialTicks: Float) {
move?.let {
isHovered = isMouseOver(mouseX.toDouble(), mouseY.toDouble()) && enabled
val rgb = it.elementalType.hue.toRGB()
val alpha = if (enabled) 1.0F else 0.7F
val matrices = context.pose()
val moveTemplate = Moves.getByNameOrDummy(move.name)
val rgb = moveTemplate.elementalType.hue.toRGB()
blitk(
matrixStack = matrices,
texture = if (enabled) moveResource else moveDisabledResource,
x = x,
y = y,
width = WIDTH,
height = HEIGHT,
vOffset = if (isHovered) HEIGHT else 0,
textureHeight = HEIGHT * 2,
red = rgb.first,
green = rgb.second,
blue = rgb.third
)
val alpha = if (enabled) 1.0 else 0.5
blitk(
matrixStack = matrices,
texture = moveOverlayResource,
x = x,
y = y,
width = WIDTH,
height = HEIGHT
)
val matrices = context.pose()
blitk(
matrixStack = matrices,
texture = moveResource,
x = x,
y = y,
width = WIDTH,
height = HEIGHT,
vOffset = if (isHovered) HEIGHT else 0,
textureHeight = HEIGHT * 2,
red = rgb.first,
green = rgb.second,
blue = rgb.third,
alpha = alpha
)
if (pp != -1 && ppMax != -1) {
var movePPText = lang("ui.moves.pp", pp).bold()
if (ppAsFraction) {
movePPText = Component.literal("$pp/$ppMax").bold()
blitk(
matrixStack = matrices,
texture = moveOverlayResource,
x = x,
y = y,
width = WIDTH,
height = HEIGHT,
alpha = alpha
)
if (pp <= Mth.floor(ppMax / 2F)) {
movePPText = if (pp == 0) movePPText.red() else movePPText.gold()
}
}
if (pp != -1 && ppMax != -1) {
var movePPText = Component.literal("$pp/$ppMax").bold()
if (pp <= Mth.floor(ppMax / 2F)) {
movePPText = if (pp == 0) movePPText.red() else movePPText.gold()
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = movePPText,
x = x + 93,
y = y + 13,
centered = true,
opacity = alpha
)
}
// Type Icon
TypeIcon(x = x + 2, y = y + 2, type = it.elementalType, opacity = alpha).render(context)
// Move Category
MoveCategoryIcon(x = x + 66, y = y + 13.5, category = it.damageCategory, opacity = alpha).render(context)
// Move Name
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = movePPText,
x = x + 93,
y = y + 13,
centered = true
text = it.displayName.bold(),
x = x + 28,
y = y + 2,
shadow = true,
opacity = alpha
)
}
// Type Icon
TypeIcon(
x = x + 2,
y = y + 2,
type = moveTemplate.elementalType
).render(context)
// Move Category
MoveCategoryIcon(
x = x + 66,
y = y + 13.5,
category = move.damageCategory
).render(context)
// Move Name
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = move.displayName.bold(),
x = x + 28,
y = y + 2,
shadow = true
)
}
override fun playDownSound(soundManager: SoundManager) {}
}
}
@@ -36,9 +36,9 @@ class MoveSlotWidget(
): SoundlessWidget(pX, pY, MOVE_WIDTH, MOVE_HEIGHT, Component.literal(move?.name ?: "")) {
companion object {
private val moveResource = cobblemonResource("textures/gui/summary/summary_move.png")
private val moveOverlayResource = cobblemonResource("textures/gui/summary/summary_move_overlay.png")
private val moveSelectedOverlayResource = cobblemonResource("textures/gui/summary/summary_move_selected_overlay.png")
val moveResource = cobblemonResource("textures/gui/summary/summary_move.png")
val moveOverlayResource = cobblemonResource("textures/gui/summary/summary_move_overlay.png")
val moveSelectedOverlayResource = cobblemonResource("textures/gui/summary/summary_move_selected_overlay.png")
const val MOVE_WIDTH = 108
const val MOVE_HEIGHT = 22
@@ -50,6 +50,7 @@ class MoveSlotWidget(
}.apply {
addWidget(this)
}
private val moveDownButton = ReorderMoveButton(x, y, false) {
movesWidget.selectMove(null)
movesWidget.reorderMove(this, false)
@@ -67,46 +68,46 @@ class MoveSlotWidget(
}.apply {
addWidget(this)
}
val elementalType:ElementalType = Moves.getByNameOrDummy(move?.name ?: "").getEffectiveElementalType(pokemon)
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
val matrices = context.pose()
isHovered = pMouseX >= x && pMouseY >= y && pMouseX < x + width && pMouseY < y + height
if (move != null) {
val moveTemplate = Moves.getByNameOrDummy(move.name)
val rgb = elementalType.hue.toRGB()
if (movesWidget.selectedMove == move) {
blitk(
matrixStack = matrices,
texture = moveSelectedOverlayResource,
x = x - 1,
y = y - 1,
width = MOVE_WIDTH + 2,
height = MOVE_HEIGHT + 2
matrixStack = matrices,
texture = moveSelectedOverlayResource,
x = x - 1,
y = y - 1,
width = MOVE_WIDTH + 2,
height = MOVE_HEIGHT + 2
)
}
blitk(
matrixStack = matrices,
texture = moveResource,
x = x,
y = y,
width = MOVE_WIDTH,
height = MOVE_HEIGHT,
vOffset = if (isHovered) MOVE_HEIGHT else 0,
textureHeight = MOVE_HEIGHT * 2,
red = rgb.first,
green = rgb.second,
blue = rgb.third
matrixStack = matrices,
texture = moveResource,
x = x,
y = y,
width = MOVE_WIDTH,
height = MOVE_HEIGHT,
vOffset = if (isHovered) MOVE_HEIGHT else 0,
textureHeight = MOVE_HEIGHT * 2,
red = rgb.first,
green = rgb.second,
blue = rgb.third
)
blitk(
matrixStack = matrices,
texture = moveOverlayResource,
x = x,
y = y,
width = MOVE_WIDTH,
height = MOVE_HEIGHT
matrixStack = matrices,
texture = moveOverlayResource,
x = x,
y = y,
width = MOVE_WIDTH,
height = MOVE_HEIGHT
)
var movePPText = Component.literal("${move.currentPp}/${move.maxPp}").bold()
@@ -116,36 +117,28 @@ class MoveSlotWidget(
}
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = movePPText,
x = x + 93,
y = y + 13,
centered = true
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = movePPText,
x = x + 93,
y = y + 13,
centered = true
)
// Type Icon
TypeIcon(
x = x + 2,
y = y + 2,
type = elementalType
).render(context)
TypeIcon(x = x + 2, y = y + 2, type = elementalType).render(context)
// Move Category
MoveCategoryIcon(
x = x + 66,
y = y + 13.5,
category = move.damageCategory
).render(context)
MoveCategoryIcon(x = x + 66, y = y + 13.5, category = move.damageCategory).render(context)
// Move Name
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = move.displayName.bold(),
x = x + 28,
y = y + 2,
shadow = true
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = move.displayName.bold(),
x = x + 28,
y = y + 2,
shadow = true
)
// Reorder Buttons
@@ -158,9 +151,7 @@ class MoveSlotWidget(
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (isHovered) {
movesWidget.selectMove(move)
}
if (isHovered) movesWidget.selectMove(move)
return super.mouseClicked(mouseX, mouseY, button)
}
}
}
@@ -74,140 +74,132 @@ class MoveSwapScreen(
val rgb = elementalType.hue.toRGB()
blitk(
matrixStack = matrices,
texture = moveResource,
x = rowLeft,
y = tweakedRowTop,
height = SLOT_HEIGHT,
width = rowWidth,
vOffset = if (isHovered) SLOT_HEIGHT else 0,
textureHeight = SLOT_HEIGHT * 2,
red = rgb.first,
green = rgb.second,
blue = rgb.third
matrixStack = matrices,
texture = moveResource,
x = rowLeft,
y = tweakedRowTop,
height = SLOT_HEIGHT,
width = rowWidth,
vOffset = if (isHovered) SLOT_HEIGHT else 0,
textureHeight = SLOT_HEIGHT * 2,
red = rgb.first,
green = rgb.second,
blue = rgb.third
)
blitk(
matrixStack = matrices,
texture = moveOverlayResource,
x = rowLeft,
y = tweakedRowTop,
height = SLOT_HEIGHT,
width = rowWidth
matrixStack = matrices,
texture = moveOverlayResource,
x = rowLeft,
y = tweakedRowTop,
height = SLOT_HEIGHT,
width = rowWidth
)
// Type Icon
TypeIcon(
x = rowLeft - 9,
y = tweakedRowTop,
type = elementalType
).render(context)
TypeIcon(x = rowLeft - 9, y = tweakedRowTop, type = elementalType).render(context)
// Move Category
MoveCategoryIcon(
x = rowLeft + 77,
y = tweakedRowTop + 1.5,
category = move.damageCategory
).render(context)
MoveCategoryIcon(x = rowLeft + 77, y = tweakedRowTop + 1.5, category = move.damageCategory).render(context)
drawScaledText(
context = context,
text = move.displayName,
x = rowLeft + 14,
y = tweakedRowTop + 3.5,
scale = MovesWidget.SCALE,
shadow = true
context = context,
text = move.displayName,
x = rowLeft + 14,
y = tweakedRowTop + 3.5,
scale = MovesWidget.SCALE,
shadow = true
)
// Move icons
blitk(
matrixStack = matrices,
texture = MovesWidget.movesPowerIconResource,
x= (rowLeft + 10) / MovesWidget.SCALE,
y = (tweakedRowTop + 11) / MovesWidget.SCALE,
width = MovesWidget.MOVE_ICON_SIZE,
height = MovesWidget.MOVE_ICON_SIZE,
scale = MovesWidget.SCALE
matrixStack = matrices,
texture = MovesWidget.movesPowerIconResource,
x= (rowLeft + 10) / MovesWidget.SCALE,
y = (tweakedRowTop + 11) / MovesWidget.SCALE,
width = MovesWidget.MOVE_ICON_SIZE,
height = MovesWidget.MOVE_ICON_SIZE,
scale = MovesWidget.SCALE
)
blitk(
matrixStack = matrices,
texture = MovesWidget.movesAccuracyIconResource,
x= (rowLeft + 30) / MovesWidget.SCALE,
y = (tweakedRowTop + 11) / MovesWidget.SCALE,
width = MovesWidget.MOVE_ICON_SIZE,
height = MovesWidget.MOVE_ICON_SIZE,
scale = MovesWidget.SCALE
matrixStack = matrices,
texture = MovesWidget.movesAccuracyIconResource,
x= (rowLeft + 30) / MovesWidget.SCALE,
y = (tweakedRowTop + 11) / MovesWidget.SCALE,
width = MovesWidget.MOVE_ICON_SIZE,
height = MovesWidget.MOVE_ICON_SIZE,
scale = MovesWidget.SCALE
)
blitk(
matrixStack = matrices,
texture = MovesWidget.movesEffectIconResource,
x= (rowLeft + 53.5) / MovesWidget.SCALE,
y = (tweakedRowTop + 11) / MovesWidget.SCALE,
width = MovesWidget.MOVE_ICON_SIZE,
height = MovesWidget.MOVE_ICON_SIZE,
scale = MovesWidget.SCALE
matrixStack = matrices,
texture = MovesWidget.movesEffectIconResource,
x= (rowLeft + 53.5) / MovesWidget.SCALE,
y = (tweakedRowTop + 11) / MovesWidget.SCALE,
width = MovesWidget.MOVE_ICON_SIZE,
height = MovesWidget.MOVE_ICON_SIZE,
scale = MovesWidget.SCALE
)
val movePower = if (move.power.toInt() > 0) move.power.toInt().toString().text() else "—".text()
drawScaledText(
context = context,
text = movePower,
x = rowLeft + 16.5,
y = tweakedRowTop + 12,
scale = MovesWidget.SCALE,
shadow = true
context = context,
text = movePower,
x = rowLeft + 16.5,
y = tweakedRowTop + 12,
scale = MovesWidget.SCALE,
shadow = true
)
drawScaledText(
context = context,
text = pane.movesWidget.format(move.accuracy).text(),
x = rowLeft + 37,
y = tweakedRowTop + 12,
scale = MovesWidget.SCALE,
shadow = true
context = context,
text = MovesWidget.format(move.accuracy).text(),
x = rowLeft + 37,
y = tweakedRowTop + 12,
scale = MovesWidget.SCALE,
shadow = true
)
drawScaledText(
context = context,
text = pane.movesWidget.format(move.effectChances.firstOrNull() ?: 0.0).text(),
x = rowLeft + 60.5,
y = tweakedRowTop + 12,
scale = MovesWidget.SCALE,
shadow = true
context = context,
text = MovesWidget.format(move.effectChances.firstOrNull() ?: 0.0).text(),
x = rowLeft + 60.5,
y = tweakedRowTop + 12,
scale = MovesWidget.SCALE,
shadow = true
)
val pp = move.pp + ppRaisedStages * move.pp / 5
drawScaledText(
context = context,
text = lang("ui.moves.pp", pp),
x = rowLeft + 76,
y = tweakedRowTop + 12,
scale = MovesWidget.SCALE,
shadow = true
context = context,
text = lang("ui.moves.pp", pp),
x = rowLeft + 76,
y = tweakedRowTop + 12,
scale = MovesWidget.SCALE,
shadow = true
)
} else {
// Forget move slot
blitk(
matrixStack = matrices,
texture = forgetMoveOverlayResource,
x = rowLeft - 9,
y = tweakedRowTop,
height = SLOT_HEIGHT,
width = 100,
vOffset = if (isHovered) SLOT_HEIGHT else 0,
textureHeight = SLOT_HEIGHT * 2,
matrixStack = matrices,
texture = forgetMoveOverlayResource,
x = rowLeft - 9,
y = tweakedRowTop,
height = SLOT_HEIGHT,
width = 100,
vOffset = if (isHovered) SLOT_HEIGHT else 0,
textureHeight = SLOT_HEIGHT * 2
)
blitk(
matrixStack = matrices,
texture = forgetMoveIconResource,
x = rowLeft + rowWidth / 2 - 7,
y = tweakedRowTop + SLOT_HEIGHT / 2 - 4,
height = 7,
width = 7,
vOffset = if (isHovered) 7 else 0,
textureHeight = 14,
matrixStack = matrices,
texture = forgetMoveIconResource,
x = rowLeft + rowWidth / 2 - 7,
y = tweakedRowTop + SLOT_HEIGHT / 2 - 4,
height = 7,
width = 7,
vOffset = if (isHovered) 7 else 0,
textureHeight = 14
)
}
@@ -232,4 +224,4 @@ class MoveSwapScreen(
return false
}
}
}
}
@@ -39,6 +39,11 @@ class MovesWidget(
it.roundingMode = RoundingMode.CEILING
}
fun format(input: Double): String {
if (input <= 0) return "—"
return "${decimalFormat.format(input)}%"
}
private val movesBaseResource = cobblemonResource("textures/gui/summary/summary_moves_base.png")
val movesPowerIconResource = cobblemonResource("textures/gui/summary/summary_moves_icon_power.png")
val movesAccuracyIconResource = cobblemonResource("textures/gui/summary/summary_moves_icon_accuracy.png")
@@ -65,11 +70,7 @@ class MovesWidget(
addWidget(it)
}
private var descriptionScrollList = MoveDescriptionScrollList(
x + 69,
y + 113,
5
)
private var descriptionScrollList = MoveDescriptionScrollList(x + 69, y + 113, 5)
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
@@ -208,11 +209,6 @@ class MovesWidget(
)
}
fun format(input: Double): String {
if (input <= 0) return "—"
return "${decimalFormat.format(input)}%"
}
fun selectMove(move: Move?) {
selectedMove = if (selectedMove == move || move == null) null else move
descriptionScrollList.setMoveDescription(selectedMove?.description ?: "".text())
@@ -232,4 +228,4 @@ class MovesWidget(
if (descriptionScrollList.isHovered) descriptionScrollList.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
}
}
}
@@ -0,0 +1,60 @@
/*
* 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.client.gui.tmmachine
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.narration.NarrationElementOutput
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.client.sounds.SoundManager
import net.minecraft.resources.ResourceLocation
class IconButton(
var buttonX: Float,
var buttonY: Float,
val buttonWidth: Number,
val buttonHeight: Number,
var resource: ResourceLocation? = null,
val scale: Float = 0.5F,
val silent: Boolean = false,
val clickAction: OnPress
): Button(buttonX.toInt(), buttonY.toInt(), buttonWidth.toInt(), buttonHeight.toInt(), "".text(), clickAction, DEFAULT_NARRATION), CobblemonRenderable {
override fun mouseDragged(d: Double, e: Double, i: Int, f: Double, g: Double) = false
override fun defaultButtonNarrationText(builder: NarrationElementOutput) {}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTicks: Float) {
resource?.let {
blitk(
matrixStack = context.pose(),
texture = it,
x = buttonX / scale,
y = buttonY / scale,
width = buttonWidth,
height = buttonHeight,
vOffset = if (isMouseOver(mouseX.toDouble(), mouseY.toDouble())) buttonHeight else 0,
textureHeight = buttonHeight.toFloat() * 2,
scale = scale
)
}
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (visible && isMouseOver(mouseX, mouseY)) super.mouseClicked(mouseX, mouseY, button)
return false
}
override fun playDownSound(soundManager: SoundManager) {
if (visible && !this.silent) soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
}
}
@@ -0,0 +1,77 @@
/*
* 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.client.gui.tmmachine
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.pc.TextWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.network.chat.Component
class MoveSearchWidget(
private var pX: Int,
private var pY: Int,
text: Component = "MoveSearchWidget".text(),
update: () -> (Unit)
): TextWidget(pX, pY, text = text, update = update) {
companion object {
const val ICON_SIZE = 16
const val SCALE = 0.5F
private val iconFilterResource = cobblemonResource("textures/gui/tmmachine/icon_search.png")
private val labelFilter = lang("ui.pokedex.search")
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
blitk(
matrixStack = context.pose(),
x = (x - 10.5) / SCALE,
y = (y + 2.5) / SCALE,
texture = iconFilterResource,
width = ICON_SIZE,
height = ICON_SIZE,
vOffset = if (isFocused || !value.isEmpty() || iconHovered(mouseX, mouseY)) ICON_SIZE else 0,
textureHeight = ICON_SIZE * 2,
scale = SCALE
)
val input = applyTextCursor(value, labelFilter)
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = input.bold(),
x = pX,
y = pY + 2,
shadow = true
)
renderCursor(context, input)
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (iconHovered(mouseX.toInt(), mouseY.toInt())) {
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F, 0.25F))
// Simulate clicking on text area if icon is clicked
if (!isFocused) return super.mouseClicked(x + (width / 2.0), y + (height / 2.0), button)
}
return super.mouseClicked(mouseX, mouseY, button)
}
fun iconHovered(mouseX: Int, mouseY: Int): Boolean = mouseX >= (x - 10.5) && mouseY >= (y + 2.5)
&& mouseX <= ((x - 10.5) + (ICON_SIZE * SCALE)) && mouseY <= ((y + 2.5) + (ICON_SIZE * SCALE))
}
@@ -0,0 +1,200 @@
/*
* 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.client.gui.tmmachine
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.reactive.SettableObservable
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.tms.TechnicalMachine
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
import com.cobblemon.mod.common.client.gui.ScrollingWidget
import com.cobblemon.mod.common.client.gui.TypeIcon
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MoveSlotWidget
import com.cobblemon.mod.common.client.gui.tmmachine.MovesScrollingWidget.ScrollSlot
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.toRGB
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.FastColor
import net.minecraft.util.Mth
class MovesScrollingWidget(val pX: Int, val pY: Int, val tmList: SettableObservable<MutableList<TechnicalMachine>>, val setTM: (TechnicalMachine?, Boolean) -> (Unit)): ScrollingWidget<ScrollSlot>(
width = WIDTH,
height = HEIGHT,
left = pX,
top = pY - HEIGHT,
slotHeight = SLOT_HEIGHT + SLOT_SPACING
) {
companion object {
const val WIDTH = 115
const val HEIGHT = 82
const val SLOT_HEIGHT = 22
const val SLOT_SPACING = 4
val searchBar = cobblemonResource("textures/gui/tmmachine/search_bar.png")
}
var highlightedMoveId: ResourceLocation? = null
init {
tmList.subscribeIncludingCurrent { tmList ->
replaceEntries(tmList.map { tm -> ScrollSlot(tm, setTM) })
}
}
fun setSlotHighlighted(id: ResourceLocation?) {
highlightedMoveId = id
for (child in children()) {
if (child is ScrollSlot) {
child.highlighted = child.tm.id == highlightedMoveId
}
}
}
override fun getScrollbarPosition(): Int = rowLeft + width - 3
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
blitk(
matrixStack = context.pose(),
texture = searchBar,
x = left - 4,
y = y - 19,
width = 120,
height = 19
)
super.renderWidget(context, mouseX, mouseY, delta)
}
override fun renderScrollbar(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
val xLeft = this.scrollbarPosition
val xRight = xLeft + 3
val barHeight = this.bottom - this.y
var yBottom = ((barHeight * barHeight).toFloat() / this.maxPosition.toFloat()).toInt()
yBottom = Mth.clamp(yBottom, 32, barHeight - 8)
var yTop = scrollAmount.toInt() * (barHeight - yBottom) / this.maxScroll + this.y
if (yTop < this.y) {
yTop = this.y
}
context.fill(xLeft, this.y, xRight, this.bottom, FastColor.ARGB32.color(255, 75, 75, 75)) // background
context.fill(xLeft,yTop, xRight, yTop + yBottom, FastColor.ARGB32.color(255, 141, 141, 141)) // base
}
override fun getEntry(index: Int): ScrollSlot = children()[index] as ScrollSlot
class ScrollSlot(val tm: TechnicalMachine, val setTM : (TechnicalMachine?, clicked: Boolean) -> Unit): Slot<ScrollSlot>() {
var posX: Int = 0
var posY: Int = 0
var highlighted = false
override fun render(context: GuiGraphics, index: Int, y: Int, x: Int, entryWidth: Int, entryHeight: Int, mouseX: Int, mouseY: Int, hovered: Boolean, tickDelta: Float) {
posX = x
posY = y
val moveTemplate = tm.moveName
val rgb = moveTemplate.elementalType.hue.toRGB()
val isHovered = hovered && (mouseX < x + (108)) // Prevent hover when over scrollbar
val startPosY = y + SLOT_SPACING
val startPosX = x + 1 // Hover border for move renders at x, so render move element to the right by 1
val matrices = context.pose()
blitk(
matrixStack = matrices,
texture = MoveSlotWidget.moveResource,
x = startPosX,
y = startPosY,
width = MoveSlotWidget.MOVE_WIDTH,
height = MoveSlotWidget.MOVE_HEIGHT,
vOffset = if (isHovered) MoveSlotWidget.MOVE_HEIGHT else 0,
textureHeight = MoveSlotWidget.MOVE_HEIGHT * 2,
red = rgb.first,
green = rgb.second,
blue = rgb.third
)
blitk(
matrixStack = matrices,
texture = MoveSlotWidget.moveOverlayResource,
x = startPosX,
y = startPosY,
width = MoveSlotWidget.MOVE_WIDTH,
height = MoveSlotWidget.MOVE_HEIGHT
)
if (highlighted || isHovered) {
blitk(
matrixStack = matrices,
texture = MoveSlotWidget.moveSelectedOverlayResource,
x = startPosX - 1,
y = startPosY - 1,
width = MoveSlotWidget.MOVE_WIDTH + 2,
height = MoveSlotWidget.MOVE_HEIGHT + 2
)
}
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.moves.pp", moveTemplate.pp).bold(),
x = startPosX + 93,
y = startPosY + 13,
centered = true
)
// Type Icon
TypeIcon(
x = x + 2,
y = startPosY + 2,
type = moveTemplate.elementalType
).render(context)
// Move Category
MoveCategoryIcon(
x = x + 66,
y = startPosY + 13.5,
category = moveTemplate.damageCategory
).render(context)
// Move Name
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = moveTemplate.displayName.bold(),
x = x + 28,
y = startPosY + 2,
shadow = true
)
if (isHovered) {
setTM(tm, false)
}
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
// Prevent clicking through scroll bar
if (mouseX < posX + (108)) {
setTM(tm, true)
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
}
return super.mouseClicked(mouseX, mouseY, button)
}
override fun getNarration(): Component {
return tm.moveName.displayName
}
}
}
@@ -0,0 +1,84 @@
/*
* 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.client.gui.tmmachine
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.narration.NarrationElementOutput
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.client.sounds.SoundManager
class StartButton(
var buttonX: Float,
var buttonY: Float,
val clickAction: OnPress
): Button(buttonX.toInt(), buttonY.toInt(), WIDTH, HEIGHT, "".text(), clickAction, DEFAULT_NARRATION), CobblemonRenderable {
companion object {
const val WIDTH = 22
const val HEIGHT = 12
val base = cobblemonResource("textures/gui/tmmachine/button_start.png")
val baseStop = cobblemonResource("textures/gui/tmmachine/button_stop.png")
val baseDisabled = cobblemonResource("textures/gui/tmmachine/button_start_disabled.png")
val iconStart = cobblemonResource("textures/gui/tmmachine/button_icon_start.png")
val iconStop = cobblemonResource("textures/gui/tmmachine/button_icon_stop.png")
val iconRepeat = cobblemonResource("textures/gui/tmmachine/button_icon_repeat.png")
}
var disabled = false
var processing = false
var shouldRepeat = false
override fun mouseDragged(d: Double, e: Double, i: Int, f: Double, g: Double) = false
override fun defaultButtonNarrationText(builder: NarrationElementOutput) {}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTicks: Float) {
val resource = if (disabled) baseDisabled
else (if (processing) baseStop else base)
blitk(
matrixStack = context.pose(),
texture = resource,
x = buttonX,
y = buttonY,
width = WIDTH,
height = HEIGHT,
vOffset = if (isMouseOver(mouseX.toDouble(), mouseY.toDouble())) HEIGHT else 0,
textureHeight = HEIGHT * 2,
)
blitk(
matrixStack = context.pose(),
texture = if (processing) iconStop else (if (shouldRepeat) iconRepeat else iconStart),
x = buttonX,
y = buttonY,
width = WIDTH,
height = HEIGHT
)
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (active && isMouseOver(mouseX, mouseY)) {
super.mouseClicked(mouseX, mouseY, button)
}
return false
}
override fun playDownSound(soundManager: SoundManager) {
if (visible && !disabled) {
soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
}
}
}
@@ -0,0 +1,825 @@
/*
* 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.client.gui.tmmachine
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.ColourLibrary
import com.cobblemon.mod.common.api.gui.MultiLineLabelK
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.reactive.SettableObservable
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.api.tms.TechnicalMachine
import com.cobblemon.mod.common.api.tms.TechnicalMachines
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.block.entity.TMMachineBlockEntity
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.interact.moveselect.MoveSlotButton
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MovesWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
import com.cobblemon.mod.common.item.components.TMMoveComponent
import com.cobblemon.mod.common.net.messages.client.ui.SetActiveTMPacket
import com.cobblemon.mod.common.net.messages.client.ui.SetTMMachineContainerDataPacket
import com.cobblemon.mod.common.net.messages.server.block.TMMachineTeachMovePacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.isInventoryKeyPressed
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.toRGB
import com.mojang.blaze3d.platform.InputConstants
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.core.BlockPos
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.network.chat.Component
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.item.ItemStack
import kotlin.math.ceil
class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, title: Component) : AbstractContainerScreen<TMMachineMenu>(containerMenu, inventory, title) {
companion object {
const val HEIGHT = 226
const val WIDTH = 191
const val DISC_DIAMETER = 56
const val TYPE_SELECT_MODE = 1
const val MOVE_SELECT_MODE = 2
const val TM_BURN_MODE = 3
const val CRAFT_TICKS = 10 // Length for craft completion animation
const val RESET_DISC_TICKS = 4 // Length for disc reset animation
val baseResource = cobblemonResource("textures/gui/tmmachine/base.png")
val tmTray = cobblemonResource("textures/gui/tmmachine/tm_tray.png")
val iconArrowLeft = cobblemonResource("textures/gui/tmmachine/icon_arrow_left.png")
val iconArrowRight = cobblemonResource("textures/gui/tmmachine/icon_arrow_right.png")
val emptyDiscSlot = cobblemonResource("textures/item/tms/blank_disc_empty_slot.png")
val discBase = cobblemonResource("textures/gui/tmmachine/tm_base.png")
val discBorder = cobblemonResource("textures/gui/tmmachine/tm_border.png")
val discOverlay = cobblemonResource("textures/gui/tmmachine/tm_base_overlay.png")
val discReflective = cobblemonResource("textures/gui/tmmachine/tm_overlay_reflective.png")
val discReflectiveRotating = cobblemonResource("textures/gui/tmmachine/tm_overlay_reflective_rotating.png")
val discRotating = cobblemonResource("textures/gui/tmmachine/tm_overlay_rotating.png")
}
var selectedTM: TechnicalMachine? = null
var heldStackMove: MoveTemplate? = null
var tmList: SettableObservable<MutableList<TechnicalMachine>> = SettableObservable(mutableListOf())
var output: ItemStack? = null
var mode: Int = TYPE_SELECT_MODE
var sortType: ElementalType? = null
var selectedPokemon: Pokemon? = null
var partySlotList: MutableSet<TMPartySlotWidget> = mutableSetOf()
lateinit var backButton: IconButton
lateinit var startButton: StartButton
lateinit var selectedMoveButton: MoveSlotButton
lateinit var typesScrollList: TypesScrollingWidget
lateinit var movesScrollingList: MovesScrollingWidget
lateinit var moveSearchWidget: MoveSearchWidget
var discReflectionFrame: Int = 0
var discRotationFrame: Int = 0
var initScreen: Boolean = false
override fun init() {
imageWidth = WIDTH
imageHeight = HEIGHT
super.init()
partySlotList.clear()
CobblemonClient.storage.party.forEachIndexed { index, pokemon ->
partySlotList.add(TMPartySlotWidget(
pX = leftPos - 60,
pY = topPos + 9 + ((TMPartySlotWidget.HEIGHT + 3) * index),
pokemon = pokemon,
onPress = {
if ((it as TMPartySlotWidget).clickable) {
if (heldStackMove != null && pokemon != null) {
heldStackMove?.let { move ->
if (canLearnTMMove(move, pokemon) == TMPartySlotWidget.CAN_LEARN) {
CobblemonNetwork.sendToServer(TMMachineTeachMovePacket(pokemon.uuid, menu.carried, move))
}
}
} else {
if (mode == TYPE_SELECT_MODE && pokemon != null) {
setMoveList(pokemon = pokemon)
setScreenFromMode(MOVE_SELECT_MODE)
selectedPokemon = pokemon
playSound(CobblemonSounds.GUI_CLICK)
}
}
}
}
))
}
for (slot in partySlotList) {
if (!children().contains(slot)) addRenderableWidget(slot)
}
typesScrollList = TypesScrollingWidget(
pX = leftPos + 7,
pY = topPos + 11,
setType = { type ->
sortType = type
setMoveList()
setScreenFromMode(MOVE_SELECT_MODE)
}
)
if (!children().contains(typesScrollList)) addRenderableWidget(typesScrollList)
movesScrollingList = MovesScrollingWidget(
pX = leftPos + 4,
pY = topPos + 29,
tmList,
setTM = { tm, clicked -> setSelectedTM(tm, clicked) }
)
if (!children().contains(movesScrollingList)) addRenderableWidget(movesScrollingList)
moveSearchWidget = MoveSearchWidget(pX = leftPos + 16, pY = topPos + 13) {
if (::movesScrollingList.isInitialized && moveSearchWidget.isFocused) {
val input = moveSearchWidget.value
setMoveList(filter = if (input.isBlank()) null else input)
}
}
if (!children().contains(moveSearchWidget)) addRenderableWidget(moveSearchWidget)
backButton = IconButton(
leftPos + 110F,
topPos + 1F,
18,
18,
iconArrowLeft
) { setScreenFromMode(TYPE_SELECT_MODE) }
if (!children().contains(backButton)) addRenderableWidget(backButton)
selectedMoveButton = MoveSlotButton(
leftPos + 6,
topPos + 6,
move = selectedTM?.moveName,
pp = selectedTM?.moveName?.pp ?: 0,
ppAsFraction = false
) {
if (!isBurnActive()) {
if (::movesScrollingList.isInitialized && movesScrollingList.children().isEmpty()) setMoveList()
playSound(CobblemonSounds.GUI_CLICK)
setScreenFromMode(MOVE_SELECT_MODE)
// Clear active TM
CobblemonNetwork.sendToServer(SetActiveTMPacket(null))
}
}
if (!children().contains(selectedMoveButton)) addRenderableWidget(selectedMoveButton)
startButton = StartButton(leftPos + 5F, topPos + 35F) {
if (!(it as StartButton).disabled) {
selectedTM?.let {
val burnActive = menu.containerData?.get(TMMachineBlockEntity.BURN_ACTIVE_INDEX) ?: 0
CobblemonNetwork.sendToServer(SetTMMachineContainerDataPacket(
TMMachineBlockEntity.BURN_ACTIVE_INDEX,
burnActive xor 1 // Toggle active state, 1 ⇆ 0
))
// If button is not active, set if process should repeat or not
if (burnActive == 0) {
CobblemonNetwork.sendToServer(SetTMMachineContainerDataPacket(
TMMachineBlockEntity.REPEAT_PROCESS_INDEX,
if (hasShiftDown()) 1 else 0
))
}
CobblemonNetwork.sendToServer(SetActiveTMPacket(it))
inventory.setChanged()
menu.broadcastChanges()
}
}
}
if (!children().contains(startButton)) addRenderableWidget(startButton)
setScreenFromMode(mode)
initScreen = false
}
override fun containerTick() {
if (mode == TM_BURN_MODE) {
selectedTM?.let { tm ->
val resultStack = menu.inventory!!.getItem(TMMachineMenu.RESULT_SLOT)
val craftedTmStack = ItemStack(CobblemonItems.TECHNICAL_MACHINE)
.also { TMMoveComponent.setTMMove(it, tm.moveName) }
var validOutput = resultStack.isEmpty || (
!resultStack.isEmpty
&& (resultStack.count < resultStack.maxStackSize)
&& ItemStack.isSameItemSameComponents(resultStack, craftedTmStack)
)
var validCost = !menu.inventory!!.getItem(TMMachineMenu.BLANK_TM_SLOT).isEmpty // Blank TM slot
if (validOutput && validCost) {
val recipe = tm.getClampedRecipe() ?: listOf()
recipe.forEachIndexed { index, ingredient ->
val recipeStack = BuiltInRegistries.ITEM.get(ingredient.item).defaultInstance.also { it.count = ingredient.count }
val slotStack = menu.inventory!!.getItem(index + TMMachineMenu.INGREDIENT_SLOTS.first)
if (!(ItemStack.isSameItem(recipeStack, slotStack) && slotStack.count >= recipeStack.count)) {
recipeStack.item
validCost = false
return@forEachIndexed
}
}
}
if (::startButton.isInitialized) {
val discResetting = menu.getPostCraftTicks() > CRAFT_TICKS
startButton.disabled = !(!discResetting && (validCost && validOutput))//!(validCost && validOutput)
startButton.processing = isBurnActive()
startButton.shouldRepeat = hasShiftDown()
}
if (::selectedMoveButton.isInitialized) {
selectedMoveButton.enabled = !isBurnActive()
}
if (isBurnActive()) {
val progress = menu.getBurnProgressRatio()
if (progress > 0 && progress < 1) {
discReflectionFrame = (discReflectionFrame + 1) % 4
discRotationFrame = (discRotationFrame + 1) % 8
}
if ((progress == 1F) && (menu.getPostCraftTicks() == 0)) playSound(CobblemonSounds.TM_MACHINE_CRAFT)
}
}
} else {
// Check if screen should be switched upon opening UI
if (!initScreen) loadBurnScreenData()
}
if (mode != MOVE_SELECT_MODE) {
val heldStack = menu.carried
if (heldStack.item == CobblemonItems.TECHNICAL_MACHINE) {
// Store move of TM item picked up by player cursor
TMMoveComponent.getTMMove(heldStack)?.let { move ->
if (heldStackMove != move) heldStackMove = move
if (mode == TYPE_SELECT_MODE) updatePartySlotStatus(heldStackMove)
}
} else {
if (heldStackMove != null) {
heldStackMove = null
updatePartySlotStatus(null)
}
}
// Allow slots to be clicked if player holding TM item
if ((mode == TM_BURN_MODE)) {
for (slot in partySlotList) slot.clickable = (heldStackMove == selectedTM?.moveName)
updatePartySlotStatus(selectedTM?.moveName)
} else if (mode == MOVE_SELECT_MODE) {
for (slot in partySlotList) slot.clickable = false
}
}
super.containerTick()
}
private fun loadBurnScreenData() {
var tm: TechnicalMachine? = null
val blockPos = menu.containerData?.let {
val x = it.get(TMMachineBlockEntity.POSITION_X_INDEX)
val y = it.get(TMMachineBlockEntity.POSITION_Y_INDEX)
val z = it.get(TMMachineBlockEntity.POSITION_Z_INDEX)
BlockPos(x, y, z)
}
inventory.player.level().getBlockEntity(blockPos)?.let { blockEntity ->
if (blockEntity is TMMachineBlockEntity) {
Moves.getByName(blockEntity.activeMove)?.let {
tm = TechnicalMachines.moveToTM[it]
}
}
}
// If burn currently in progress or if active TM set, set to burn screen
if (isBurnActive() || tm != null) {
setSelectedTM(tm, true)
}
initScreen = true
}
private fun isBurnActive(): Boolean = (menu.containerData?.get(TMMachineBlockEntity.BURN_ACTIVE_INDEX) ?: 0) == 1
private fun setScreenFromMode(screen: Int) {
mode = screen
when (mode) {
TYPE_SELECT_MODE -> {
toggleTMBurn(false)
toggleMoveSelect(false)
toggleTypeSelect(true)
}
MOVE_SELECT_MODE -> {
toggleTMBurn(false)
toggleTypeSelect(false)
toggleMoveSelect(true)
}
else -> {
toggleTypeSelect(false)
toggleMoveSelect(false)
toggleTMBurn(true)
}
}
}
private fun setMoveList(filter: String? = null, pokemon: Pokemon? = null) {
val filteredList = TechnicalMachine.filterTms(filter, sortType, pokemon, player = inventory.player).toMutableList()
if (sortType == null) filteredList.sortBy { it.type }
tmList.set(filteredList)
}
private fun toggleTypeSelect(isVisible: Boolean) {
if (isVisible) {
setSelectedTM(null, false)
if (::moveSearchWidget.isInitialized) moveSearchWidget.value = ""
if (selectedPokemon != null) selectedPokemon = null
sortType = null
}
if (::typesScrollList.isInitialized) {
typesScrollList.apply {
visible = isVisible
scrollAmount = 0.0
// Resize widget to toggle input detection as screen only detects first widget added for overlapping children
if (isVisible) { setSize(TypesScrollingWidget.WIDTH, TypesScrollingWidget.HEIGHT) }
else { setSize(0, 0) }
}
}
for (slot in partySlotList) slot.clickable = isVisible
}
private fun toggleMoveSelect(isVisible: Boolean) {
if (::movesScrollingList.isInitialized) {
movesScrollingList.apply {
visible = isVisible
scrollAmount = 0.0
if (isVisible) { setSize(MovesScrollingWidget.WIDTH, MovesScrollingWidget.HEIGHT) }
else { setSize(0, 0) }
}
}
if (::moveSearchWidget.isInitialized) {
moveSearchWidget.visible = isVisible
}
if (::backButton.isInitialized) {
backButton.visible = isVisible
}
heldStackMove = null
}
private fun toggleTMBurn(isVisible: Boolean) {
if (!isVisible) setSelectedTM(null, false)
if (::selectedMoveButton.isInitialized) selectedMoveButton.visible = isVisible
if (::startButton.isInitialized) startButton.visible = isVisible
}
fun canLearnTMMove(move: MoveTemplate, pokemon: Pokemon): Int {
if (pokemon.moveSet.getMoveTemplates().contains(move) || pokemon.allAccessibleMoves.contains(move)) return TMPartySlotWidget.LEARNED
val learnableMoves = pokemon.form.moves.tmLearnableMoves()
return if (learnableMoves.contains(move)) TMPartySlotWidget.CAN_LEARN else TMPartySlotWidget.CANNOT_LEARN
}
fun setSelectedTM(tm: TechnicalMachine?, clicked: Boolean) {
if (selectedTM != tm) {
selectedTM = tm
updatePartySlotStatus(tm?.moveName)
}
if (::movesScrollingList.isInitialized) {
movesScrollingList.setSlotHighlighted(if (clicked) null else selectedTM?.id)
}
if (clicked) {
selectedTM?.let {
if (::selectedMoveButton.isInitialized) {
selectedMoveButton.apply {
move = it.moveName
pp = it.moveName.pp
}
setScreenFromMode(TM_BURN_MODE)
}
}
}
}
fun updatePartySlotStatus(moveTemplate: MoveTemplate?) {
for (slot in partySlotList) {
if (slot.pokemon != null) {
slot.teachable = if (moveTemplate != null) canLearnTMMove(moveTemplate, slot.pokemon) else null
}
}
}
fun renderDiscTypeOverlay(poseStack: PoseStack, isDiscRotating: Boolean, alpha: Float = 1F) {
selectedTM?.let {
val primaryRgb = it.moveName.elementalType.primaryColor.toRGB()
val secondaryRgb = it.moveName.elementalType.secondaryColor.toRGB()
blitk(
matrixStack = poseStack,
texture = discBase,
x = leftPos + 32,
y = topPos + 50,
width = DISC_DIAMETER,
height = DISC_DIAMETER,
red = primaryRgb.first,
green = primaryRgb.second,
blue = primaryRgb.third,
alpha = alpha
)
if (isDiscRotating) {
// Reflective rotation
blitk(
matrixStack = poseStack,
texture = discReflectiveRotating,
x = leftPos + 32,
y = topPos + 50,
width = DISC_DIAMETER,
height = DISC_DIAMETER,
vOffset = DISC_DIAMETER * discReflectionFrame,
textureHeight = DISC_DIAMETER * 4,
red = secondaryRgb.first,
green = secondaryRgb.second,
blue = secondaryRgb.third,
alpha = alpha
)
// Groove rotation
blitk(
matrixStack = poseStack,
texture = discRotating,
x = leftPos + 32,
y = topPos + 50,
width = DISC_DIAMETER,
height = DISC_DIAMETER,
vOffset = DISC_DIAMETER * discRotationFrame,
textureHeight = DISC_DIAMETER * 8,
red = secondaryRgb.first,
green = secondaryRgb.second,
blue = secondaryRgb.third,
alpha = alpha
)
} else {
blitk(
matrixStack = poseStack,
texture = discReflective,
x = leftPos + 32,
y = topPos + 50,
width = DISC_DIAMETER,
height = DISC_DIAMETER,
red = secondaryRgb.first,
green = secondaryRgb.second,
blue = secondaryRgb.third,
alpha = alpha
)
}
val postCraftTicks = menu.getPostCraftTicks()
if (postCraftTicks > 0 && postCraftTicks < 8) {
val opacity = when {
postCraftTicks <= 5 -> postCraftTicks * 0.25F
postCraftTicks > 5 -> (CRAFT_TICKS - postCraftTicks) * 0.25F // decreases by 0.25 each tick after 5
else -> 0F
}
blitk(
matrixStack = poseStack,
texture = discOverlay,
x = leftPos + 32,
y = topPos + 50,
width = DISC_DIAMETER,
height = DISC_DIAMETER,
alpha = opacity
)
}
}
}
fun renderDisc(poseStack: PoseStack, isDiscRotating: Boolean, baseAlpha: Float = 1F, overlayAlpha: Float = 1F) {
blitk(
matrixStack = poseStack,
texture = discBase,
x = leftPos + 32,
y = topPos + 50,
width = DISC_DIAMETER,
height = DISC_DIAMETER,
alpha = baseAlpha
)
if (isDiscRotating) {
// Reflective rotation
blitk(
matrixStack = poseStack,
texture = discReflectiveRotating,
x = leftPos + 32,
y = topPos + 50,
width = DISC_DIAMETER,
height = DISC_DIAMETER,
vOffset = DISC_DIAMETER * discReflectionFrame,
textureHeight = DISC_DIAMETER * 4,
alpha = baseAlpha
)
// Groove rotation
blitk(
matrixStack = poseStack,
texture = discRotating,
x = leftPos + 32,
y = topPos + 50,
width = DISC_DIAMETER,
height = DISC_DIAMETER,
vOffset = DISC_DIAMETER * discRotationFrame,
textureHeight = DISC_DIAMETER * 8,
alpha = baseAlpha
)
} else {
blitk(
matrixStack = poseStack,
texture = discReflective,
x = leftPos + 32,
y = topPos + 50,
width = DISC_DIAMETER,
height = DISC_DIAMETER,
alpha = baseAlpha
)
}
if (menu.getPostCraftTicks() < CRAFT_TICKS) {
renderDiscTypeOverlay(poseStack, isDiscRotating, overlayAlpha)
}
blitk(
matrixStack = poseStack,
texture = discBorder,
x = leftPos + 32,
y = topPos + 50,
width = DISC_DIAMETER,
height = DISC_DIAMETER,
alpha = baseAlpha
)
}
fun renderMoveInfo(context: GuiGraphics, mouseX: Int, mouseY: Int) {
val currentTm = selectedTM
val moveTemplate = currentTm?.moveName
val scale = 0.5F
blitk(
matrixStack = context.pose(),
texture = MovesWidget.movesPowerIconResource,
x = leftPos + 127.5,
y = topPos + 7.5,
width = 5,
height = 5
)
blitk(
matrixStack = context.pose(),
texture = MovesWidget.movesAccuracyIconResource,
x = leftPos + 127.5,
y = topPos + 19.5,
width = 5,
height = 5
)
blitk(
matrixStack = context.pose(),
texture = MovesWidget.movesEffectIconResource,
x = leftPos + 127.5,
y = topPos + 31.5,
width = 5,
height = 5
)
drawScaledText(
context = context,
text = lang("ui.power"),
x = leftPos + 134.5,
y = topPos + 8,
shadow = true,
scale = scale
)
drawScaledText(
context = context,
text = lang("ui.accuracy"),
x = leftPos + 134.5,
y = topPos + 20,
shadow = true,
scale = scale
)
drawScaledText(
context = context,
text = lang("ui.effect"),
x = leftPos + 134.5,
y = topPos + 32,
shadow = true,
scale = scale
)
val movePower = if (moveTemplate != null && moveTemplate.power.toInt() > 0) moveTemplate.power.toInt().toString().text() else "—".text()
drawScaledTextJustifiedRight(
context = context,
text = movePower,
x = leftPos + 183,
y = topPos + 8,
shadow = true,
scale = scale
)
val moveAccuracy = if (moveTemplate != null) MovesWidget.format(moveTemplate.accuracy).text() else "—".text()
drawScaledTextJustifiedRight(
context = context,
text = moveAccuracy,
x = leftPos + 183,
y = topPos + 20,
shadow = true,
scale = scale
)
val moveEffect = if (moveTemplate != null) MovesWidget.format(moveTemplate.effectChances.firstOrNull() ?: 0.0).text() else "—".text()
drawScaledTextJustifiedRight(
context = context,
text = moveEffect,
x = leftPos + 183,
y = topPos + 32,
shadow = true,
scale = scale
)
if (moveTemplate != null) {
context.pose().pushPose()
context.pose().scale(scale, scale, 1F)
MultiLineLabelK.create(
component = moveTemplate.description,
width = 55 / scale,
maxLines = 5
).renderLeftAligned(
context = context,
x = (leftPos + 127.5) / scale,
y = (topPos + 44.5) / scale,
ySpacing = 6 / scale,
colour = ColourLibrary.WHITE,
shadow = true
)
context.pose().popPose()
val recipe = selectedTM?.getClampedRecipe() ?: listOf()
// Render material cost
recipe.forEachIndexed { index, ingredient ->
val itemX = (leftPos + 129 + (index * 18))
val itemY = (topPos + 90)
val itemStack = BuiltInRegistries.ITEM.get(ingredient.item).defaultInstance.also { it.count = ingredient.count }
context.renderItem(itemStack, itemX, itemY)
context.renderItemDecorations(Minecraft.getInstance().font, itemStack, itemX, itemY)
if (mouseX > itemX && mouseX < (itemX + 16) && mouseY > itemY && mouseY < (itemY + 16)) {
context.renderTooltip(Minecraft.getInstance().font, itemStack, mouseX, mouseY)
}
}
}
}
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
val searchFocused = this::moveSearchWidget.isInitialized && moveSearchWidget.isFocused
if (isInventoryKeyPressed(minecraft, keyCode, scanCode)) {
if (searchFocused) { return true }
else {
playSound(CobblemonSounds.TM_MACHINE_OFF)
Minecraft.getInstance().setScreen(null)
return true
}
}
if (keyCode == InputConstants.KEY_ESCAPE) {
if (searchFocused) {
// Escape from text box
this.focused = null
return true
} else {
playSound(CobblemonSounds.TM_MACHINE_OFF)
Minecraft.getInstance().setScreen(null)
return true
}
}
return super.keyPressed(keyCode, scanCode, modifiers)
}
override fun renderBackground(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {}
override fun renderBg(guiGraphics: GuiGraphics, partialTick: Float, mouseX: Int, mouseY: Int) {}
override fun render(graphics: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
super.renderTransparentBackground(graphics)
val matrices = graphics.pose()
blitk(
matrixStack = matrices,
texture = baseResource,
x = leftPos,
y = topPos,
width = WIDTH,
height = HEIGHT
)
blitk(
matrixStack = matrices,
texture = emptyDiscSlot,
x = leftPos + 105,
y = topPos + 116,
width = 16,
height = 16
)
var label = if (mode != MOVE_SELECT_MODE) "block.cobblemon.tm_machine".asTranslated()
else ( if (selectedPokemon != null) selectedPokemon!!.getDisplayName() else (if (sortType != null) lang("type.suffix", sortType!!.displayName) else lang("ui.moves")))
drawScaledText(
context = graphics,
font = CobblemonResources.DEFAULT_LARGE,
text = label.bold(),
x = leftPos + 5,
y = topPos + 1
)
if (mode == TM_BURN_MODE) {
blitk(
matrixStack = matrices,
texture = tmTray,
x = leftPos,
y = topPos,
width = 120,
height = 112
)
val burnProgressWidth = ceil(menu.getBurnProgressRatio() * 81).toInt()
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = leftPos + 33,
y = topPos + 36,
width = burnProgressWidth,
height = 2
)
if (!menu.inventory!!.getItem(TMMachineMenu.BLANK_TM_SLOT).isEmpty) {
val postCraftTicks = menu.getPostCraftTicks()
val baseOpacity = when {
((postCraftTicks >= (CRAFT_TICKS + 1)) && (postCraftTicks <= (CRAFT_TICKS + RESET_DISC_TICKS))) -> (postCraftTicks - (CRAFT_TICKS + 1)) * 0.25F
postCraftTicks == (CRAFT_TICKS) -> 0F
else -> 1F
}
val overlayOpacity = if (postCraftTicks < CRAFT_TICKS) menu.getBurnProgressRatio() else 0F
renderDisc(matrices, (menu.getBurnProgressRatio() < 1F) && isBurnActive(), baseOpacity, overlayOpacity)
}
}
super.render(graphics, mouseX, mouseY, delta)
renderMoveInfo(graphics, mouseX, mouseY)
this.renderTooltip(graphics, mouseX, mouseY)
}
override fun renderBlurredBackground(partialTick: Float) {}
override fun renderLabels(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int) {}
fun playSound(soundEvent: SoundEvent) {
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
}
}
@@ -0,0 +1,187 @@
/*
* 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.client.gui.tmmachine
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
import net.minecraft.client.gui.components.Button
import net.minecraft.client.sounds.SoundManager
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
import org.joml.Quaternionf
import org.joml.Vector3f
class TMPartySlotWidget(
pX: Number,
pY: Number,
val pokemon: Pokemon?,
onPress: OnPress
) : Button(pX.toInt(), pY.toInt(), WIDTH, HEIGHT, Component.literal("PartyMember"), onPress, DEFAULT_NARRATION) {
val state = FloatingState()
var teachable: Int? = null
var clickable: Boolean = true
companion object {
const val WIDTH = 57
const val HEIGHT = 31
private const val PORTRAIT_DIAMETER = 30
const val HALF_SCALE = 0.5F
const val CAN_LEARN = 0
const val CANNOT_LEARN = 1
const val LEARNED = 2
private val slotResource = cobblemonResource("textures/gui/tmmachine/party_slot.png")
private val slotLearnableResource = cobblemonResource("textures/gui/tmmachine/party_slot_learnable.png")
private val slotLearnableDisabledResource = cobblemonResource("textures/gui/tmmachine/party_slot_learnable_disabled.png")
private val slotDisabledResource = cobblemonResource("textures/gui/tmmachine/party_slot_disabled.png")
private val slotLearnedResource = cobblemonResource("textures/gui/tmmachine/party_slot_learned.png")
val genderIconMale = cobblemonResource("textures/gui/pc/gender_icon_male.png")
val genderIconFemale = cobblemonResource("textures/gui/pc/gender_icon_female.png")
private val canLearnLabel = lang("ui.tm_machine.can_learn")
private val cannotLearnLabel = lang("ui.tm_machine.cannot_learn")
private val learnedLabel = lang("ui.tm_machine.learned")
}
private fun getResource(): ResourceLocation = when (teachable) {
CAN_LEARN -> if (clickable) slotLearnableResource else slotLearnableDisabledResource
CANNOT_LEARN -> slotDisabledResource
LEARNED -> slotLearnedResource
else -> slotResource
}
private fun getLabel(): MutableComponent? = when (teachable) {
CAN_LEARN -> canLearnLabel
CANNOT_LEARN -> cannotLearnLabel
LEARNED -> learnedLabel
else -> null
}
override fun renderWidget(context: net.minecraft.client.gui.GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
state.currentAspects = pokemon?.aspects ?: emptySet()
val matrices = context.pose()
if (pokemon != null) {
blitk(
matrixStack = matrices,
texture = getResource(),
x = x,
y = y,
width = width,
height = height,
vOffset = if (clickable && isMouseOver(mouseX.toDouble(), mouseY.toDouble())) height else 0,
textureHeight = height * 2,
)
val ballIcon = cobblemonResource("textures/gui/ball/" + pokemon.caughtBall.name.path + ".png")
val ballHeight = 22
blitk(
matrixStack = matrices,
texture = ballIcon,
x = (x - 2) / HALF_SCALE,
y = (y - 3) / HALF_SCALE,
height = ballHeight,
width = 18,
textureHeight = ballHeight * 2,
scale = HALF_SCALE
)
// Render Pokémon
matrices.pushPose()
matrices.translate(x + (PORTRAIT_DIAMETER / 2.0) - 2, y - 1.0, 0.0)
matrices.scale(2.5F, 2.5F, 1F)
drawProfilePokemon(
species = pokemon.species.resourceIdentifier,
matrixStack = matrices,
rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(13F, 35F, 0F)),
state = state,
scale = 4.5F,
partialTicks = delta
)
matrices.popPose()
drawScaledTextJustifiedRight(
context = context,
text = lang("ui.lv.number", pokemon.level),
x = x + 54,
y = y + 13,
shadow = true,
scale = HALF_SCALE
)
if (pokemon.gender != Gender.GENDERLESS) {
blitk(
matrixStack = matrices,
texture = if (pokemon.gender == Gender.MALE) genderIconMale else genderIconFemale,
x = (x + 51) / HALF_SCALE,
y = (y + 19) / HALF_SCALE,
height = 8,
width = 6,
scale = HALF_SCALE
)
}
drawScaledTextJustifiedRight(
context = context,
text = pokemon.getDisplayName(),
x = x + ( if (pokemon.gender != Gender.GENDERLESS) 50 else 54),
y = y + 19,
shadow = true,
scale = HALF_SCALE
)
getLabel()?.let { label ->
drawScaledText(
context = context,
text = label,
x = x + (width / 2F),
y = y + 26,
centered = true,
scale = HALF_SCALE
)
}
// Held Item
val heldItem = pokemon.heldItemNoCopy()
if (!heldItem.isEmpty) {
renderScaledGuiItemIcon(
itemStack = heldItem,
x = x + 15.0,
y = y + 13.5,
scale = HALF_SCALE.toDouble(),
matrixStack = matrices
)
}
} else {
blitk(
matrixStack = matrices,
texture = slotDisabledResource,
x = x + 55,
y = y,
width = 5,
height = height,
textureWidth = width,
textureHeight = height * 2
)
}
}
override fun playDownSound(soundManager: SoundManager) {}
}
@@ -0,0 +1,151 @@
/*
* 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.client.gui.tmmachine
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.client.gui.ScrollingWidget
import com.cobblemon.mod.common.client.gui.TypeIcon
import com.cobblemon.mod.common.client.gui.tmmachine.TypesScrollingWidget.ScrollSlotRow
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.network.chat.Component
import net.minecraft.util.FastColor
import net.minecraft.util.Mth
class TypesScrollingWidget(val pX: Int, val pY: Int, val setType: (ElementalType?) -> (Unit)): ScrollingWidget<ScrollSlotRow>(
width = WIDTH,
height = HEIGHT,
left = pX,
top = pY - HEIGHT,
slotHeight = SLOT_SIZE + SLOT_SPACING
) {
companion object {
const val WIDTH = 112
const val HEIGHT = 100
const val SLOT_SIZE = 22
const val SLOT_SPACING = 5
}
init {
(listOf(null) + ElementalTypes.all()).chunked(4).forEachIndexed { index, listChunk ->
addEntry(ScrollSlotRow(listChunk, setType))
}
}
override fun getScrollbarPosition(): Int = rowLeft + width - 3
override fun renderScrollbar(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
val xLeft = this.scrollbarPosition
val xRight = xLeft + 3
val barHeight = this.bottom - this.y
var yBottom = ((barHeight * barHeight).toFloat() / this.maxPosition.toFloat()).toInt()
yBottom = Mth.clamp(yBottom, 32, barHeight - 8)
var yTop = scrollAmount.toInt() * (barHeight - yBottom) / this.maxScroll + this.y
if (yTop < this.y) {
yTop = this.y
}
context.fill(xLeft, this.y, xRight, this.bottom, FastColor.ARGB32.color(255, 75, 75, 75)) // background
context.fill(xLeft,yTop, xRight, yTop + yBottom, FastColor.ARGB32.color(255, 141, 141, 141)) // base
}
fun renderEntry(context: GuiGraphics, mouseX: Int, mouseY: Int, index: Int, x: Int, y: Int) {
val entry = this.getEntry(index)
entry.x = x
entry.y = y
entry.renderRow( context, y, x, mouseX, mouseY)
}
override fun getEntry(index: Int): ScrollSlotRow = children()[index] as ScrollSlotRow
override fun renderListItems(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
val rowX = rowLeft
for (index in 0 until this.itemCount) {
val rowY = this.getRowTop(index)
val rowBottom = this.getRowBottom(index)
if (rowBottom >= this.y && rowY <= this.bottom) {
this.renderEntry(context!!, mouseX, mouseY, index, rowX, rowY)
}
}
}
class ScrollSlotRow(val types: List<ElementalType?>, val setType : (ElementalType?) -> Unit): Slot<ScrollSlotRow>() {
companion object {
private val allSlotResource = cobblemonResource("textures/gui/tmmachine/type_slot_all.png")
private val slotResource = cobblemonResource("textures/gui/tmmachine/type_slot.png")
}
var x: Int = 0
var y: Int = 0
fun renderRow(context: GuiGraphics, y: Int, x: Int, mouseX: Int, mouseY: Int) {
types.forEachIndexed { index, type ->
val matrices = context.pose()
val startPosX = x + ((SLOT_SPACING + SLOT_SIZE) * index)
val startPosY = y + SLOT_SPACING
val slotHovered = getHoveredSlotIndex(mouseX, mouseY) == index
blitk(
matrixStack = matrices,
texture = if (type != null) slotResource else allSlotResource,
x = startPosX,
y = startPosY,
width = SLOT_SIZE,
height = SLOT_SIZE,
vOffset = if (slotHovered) SLOT_SIZE else 0,
textureHeight = SLOT_SIZE * 2
)
if (type != null) {
TypeIcon(startPosX + 2, startPosY + 2, type).render(context)
}
}
}
override fun render(context: GuiGraphics, index: Int, y: Int, x: Int, entryWidth: Int, entryHeight: Int, mouseX: Int, mouseY: Int, hovered: Boolean, tickDelta: Float) {}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
val hoverIndex = getHoveredSlotIndex(mouseX.toInt(), mouseY.toInt())
if (hoverIndex > -1 && hoverIndex < types.size) {
setType.invoke(types[hoverIndex])
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
}
return true
}
private fun getHoveredSlotIndex(mouseX: Int, mouseY: Int): Int {
types.forEachIndexed { index, _ ->
val startPosX = x + ((SLOT_SPACING + SLOT_SIZE) * index)
val startPosY = y + SLOT_SPACING + 1
if (mouseX in startPosX..(startPosX + SLOT_SIZE) && mouseY in startPosY..(startPosY + SLOT_SIZE)) {
return index
}
}
return -1
}
override fun getNarration(): Component {
if (types.isNotEmpty()) {
return "${types[0]}-${types[types.size - 1]}".text()
}
return "".text()
}
}
}
@@ -29,27 +29,38 @@ class CobblemonToast(
var description: Component,
var frameTexture: ResourceLocation,
var progress: Float,
var progressColor: Int
var progressColor: Int,
var durationMs: Long? = null
) : Toast {
constructor(packet: ToastPacket) : this(packet.uuid, packet.icon, packet.title, packet.description, packet.frameTexture, packet.progress, packet.progressColor)
constructor(packet: ToastPacket) : this(packet.uuid, packet.icon, packet.title, packet.description, packet.frameTexture, packet.progress, packet.progressColor, packet.durationMs)
private var startTime: Long = -1
private var lastProgress = 0F
private var lastTime = 0L
internal var nextVisibility: Toast.Visibility = Toast.Visibility.SHOW
override fun render(context: GuiGraphics, manager: ToastComponent, startTime: Long): Toast.Visibility {
if (this.startTime == -1L) this.startTime = startTime
val localDuration = this.durationMs
if (localDuration != null && startTime - this.startTime >= localDuration) {
return Toast.Visibility.HIDE
}
context.blitSprite(this.frameTexture, 0, 0, this.width(), this.height())
val textRenderer = manager.minecraft.font
context.drawString(textRenderer, this.title, 30, 7, this.title.style.color?.value ?: -1, false)
context.drawString(textRenderer, this.description, 30, 18, this.description.style.color?.value ?: -1, false)
context.renderFakeItem(this.icon, 8, 8)
if (this.hasProgressBar()) {
context.fill(3, 28, 157, 29, -1)
val f = Mth.clampedLerp(this.lastProgress, this.progress, (startTime - this.lastTime).toFloat() / 100F)
context.fill(3, 28, (3F + 154F * f).toInt(), 29, this.progressColor)
this.lastProgress = f
}
this.lastTime = startTime
return this.nextVisibility
}
@@ -0,0 +1,19 @@
/*
* 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.client.net.gui
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.net.messages.client.ui.OpenTMMPacket
import net.minecraft.client.Minecraft
object OpenTMMPacketHandler : ClientNetworkPacketHandler<OpenTMMPacket> {
override fun handle(packet: OpenTMMPacket, client: Minecraft) {
//client.setScreen()
}
}
@@ -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.client.net.gui
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
import com.cobblemon.mod.common.net.messages.client.ui.SetActiveTMPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.level.block.Block
object SetActiveTMPacketHandler : ServerNetworkPacketHandler<SetActiveTMPacket> {
override fun handle(packet: SetActiveTMPacket, server: MinecraftServer, player: ServerPlayer) {
val menu = player.containerMenu as? TMMachineMenu ?: return
val inventory = menu.inventory ?: return
menu.tmMachineEntity?.let { blockEntity ->
blockEntity.activeMove = packet.tm?.moveName?.name ?: ""
blockEntity.setChanged()
player.level().sendBlockUpdated(blockEntity.blockPos, blockEntity.blockState, blockEntity.blockState, Block.UPDATE_CLIENTS)
}
inventory.setChanged()
menu.broadcastChanges()
}
}
@@ -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.client.net.gui
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.block.entity.TMMachineBlockEntity
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
import com.cobblemon.mod.common.net.messages.client.ui.SetTMMachineContainerDataPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
object SetTMMachineContainerDataPacketHandler : ServerNetworkPacketHandler<SetTMMachineContainerDataPacket> {
override fun handle(packet: SetTMMachineContainerDataPacket, server: MinecraftServer, player: ServerPlayer) {
val menu = player.containerMenu as? TMMachineMenu ?: return
val slotIndex = packet.dataSlotIndex
val slotValue = packet.value
menu.containerData?.set(slotIndex, slotValue)
// If setting to not active, also reset progress
if (slotIndex == TMMachineBlockEntity.BURN_ACTIVE_INDEX && slotValue == 0) {
menu.containerData?.set(TMMachineBlockEntity.BURN_PROGRESS_INDEX, 0)
}
menu.broadcastChanges()
}
}
@@ -0,0 +1,143 @@
/*
* 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.client.render.block
import com.cobblemon.mod.common.block.entity.TMShelfBlockEntity
import com.cobblemon.mod.common.item.interactive.TechnicalMachineItem
import com.cobblemon.mod.common.item.components.TMMoveComponent
import com.cobblemon.mod.common.util.cobblemonResource
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.blaze3d.vertex.VertexConsumer
import com.mojang.math.Axis
import net.minecraft.client.renderer.LevelRenderer
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.core.Direction
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.FastColor
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.BlockAndTintGetter
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import org.joml.Matrix4f
class TMShelfBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) :
BlockEntityRenderer<TMShelfBlockEntity> {
private val slotWidthPixels = 6
private val slotHeightPixels = 2
private val textureSize = 16f
private val leftMargin = 1f
private val rightMargin = 1f
private val topMargin = 1f
private val bottomMargin = 1f
private val interRowSpacing = 0f
override fun render(
entity: TMShelfBlockEntity,
partialTicks: Float,
poseStack: PoseStack,
buffer: MultiBufferSource,
light: Int,
overlay: Int
) {
val facing = entity.blockState.getValue(HorizontalDirectionalBlock.FACING)
val rotation = when (facing) {
Direction.NORTH -> 0f
Direction.SOUTH -> 180f
Direction.WEST -> 90f
Direction.EAST -> -90f
else -> 0f
}
val lightGetter = entity.level as? BlockAndTintGetter
val packedLight = lightGetter?.let { LevelRenderer.getLightColor(it, entity.blockPos) } ?: 0xF000F0
poseStack.pushPose()
poseStack.translate(0.5, 0.5, 0.5)
poseStack.mulPose(Axis.YP.rotationDegrees(rotation))
poseStack.translate(-0.5, -0.5, -0.5)
val slotWidth = slotWidthPixels / textureSize
val slotHeight = slotHeightPixels / textureSize
val verticalSpacing = (slotHeightPixels + interRowSpacing) / textureSize
val totalHeight = slotHeight * 7 + interRowSpacing * 6 / textureSize
val startY = topMargin / textureSize
for ((index, item) in entity.items.withIndex()) {
if (!item.isEmpty) {
val row = index / 2
val col = index % 2
val isLeftColumn = col == 0
val posX = if (isLeftColumn)
leftMargin / textureSize
else
(16f - rightMargin - slotWidthPixels) / textureSize
val posY = startY + (6 - row) * verticalSpacing
poseStack.pushPose()
poseStack.translate(posX, posY, -0.001f)
val tex = getItemTexture(item)
val colorize = item.item is TechnicalMachineItem
val color = if (colorize) {
TMMoveComponent.getTMMove(item)?.elementalType?.primaryColor ?: 0xAAAAAA
} else 0xFFFFFF
renderSlotQuad(poseStack, buffer, tex, color, packedLight, slotWidth, slotHeight, tint = colorize)
poseStack.popPose()
}
}
poseStack.popPose()
}
private fun renderSlotQuad(
poseStack: PoseStack,
buffer: MultiBufferSource,
texture: ResourceLocation,
color: Int,
light: Int,
width: Float,
height: Float,
tint: Boolean
) {
val consumer: VertexConsumer = buffer.getBuffer(RenderType.text(texture))
val matrix: Matrix4f = poseStack.last().pose()
val (r, g, b, a) = if (tint) {
listOf(
FastColor.ARGB32.red(color) / 255f,
FastColor.ARGB32.green(color) / 255f,
FastColor.ARGB32.blue(color) / 255f,
1.0f
)
} else listOf(1f, 1f, 1f, 1f)
consumer.addVertex(matrix, 0f, 0f, 0f).setColor(r, g, b, a).setUv(0f, 0f)
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
consumer.addVertex(matrix, 0f, height, 0f).setColor(r, g, b, a).setUv(0f, 1f)
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
consumer.addVertex(matrix, width, height, 0f).setColor(r, g, b, a).setUv(1f, 1f)
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
consumer.addVertex(matrix, width, 0f, 0f).setColor(r, g, b, a).setUv(1f, 0f)
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
}
private fun getItemTexture(itemStack: ItemStack): ResourceLocation {
val id = BuiltInRegistries.ITEM.getKey(itemStack.item)
return cobblemonResource("textures/block/tm_shelf/${id.path}.png")
}
}
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.render.camera
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.Seat
import com.cobblemon.mod.common.client.MountedPokemonAnimationRenderController.setup
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
@@ -18,6 +17,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository.getPoser
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.mixin.accessor.CameraAccessor
import com.mojang.math.Axis
import net.minecraft.client.Camera
import net.minecraft.util.Mth
import net.minecraft.world.entity.Entity
@@ -73,18 +73,31 @@ object MountedCameraRenderer
val currEyeHeight: Double = Mth.lerp(instance.partialTickTime.toDouble(), eyeHeight, eyeHeightOld)
var offset = Vector3f(0f, (currEyeHeight - (entity.bbHeight / 2)).toFloat(), 0f)
// Get additional offset from poser and add to the eyeHeight offset when first person
// Get additional offset from poser and add to the eyeHeight offset
if (!instance.isDetached) {
offset.add(getFirstPersonOffset(model, locatorName))
offset.add(
getFirstPersonOffset(model, locatorName)
)
} else {
offset.add(
getThirdPersonOffset(thirdPersonReverse, model.thirdPersonCameraOffset, locatorName)
)
}
// Rotate Offset if needed
if (controller.isActive()) {
offset = controller.getRenderOrientation(instance.partialTickTime).transform(offset)
} else {
offset = Axis.YP.rotationDegrees(180f - instance.yRot).transform(offset)
}
val position = locatorOffset.add(entityPos).add(Vec3(offset))
return position
val pos = locatorOffset.add(entityPos).toVector3f()
val offsetDistance = offset.length()
val offsetDirection = offset.mul(1 / offsetDistance)
// Use getMaxZoom to calculate clipping
val maxZoom: Float = getMaxZoom(offsetDistance, offsetDirection, pos, instance)
return Vec3(offsetDirection.mul(maxZoom).add(pos))
} else {
val xRot = (-1 * Math.toRadians(instance.xRot.toDouble())).toFloat()
@@ -113,9 +126,8 @@ object MountedCameraRenderer
val pos = entityPos.toVector3f().add(pivot)
// Get offset from poser
val offset = getThirdPersonOffset(thirdPersonReverse, model, locatorName)
val offset = getThirdPersonOffset(thirdPersonReverse, model.thirdPersonCameraOffsetNoViewBobbing, locatorName)
if (thirdPersonReverse) offset.z *= -1f
val rotatedOffset = orientation.transform(offset)
val offsetDistance = rotatedOffset.length()
val offsetDirection = rotatedOffset.mul(1 / offsetDistance)
@@ -130,18 +142,18 @@ object MountedCameraRenderer
private fun getThirdPersonOffset(
thirdPersonReverse: Boolean,
model: PosableModel,
cameraOffsets: Map<String, Vec3>,
locatorName: String
): Vector3f {
val cameraOffsets = model.thirdPersonCameraOffset
return if (thirdPersonReverse && cameraOffsets.containsKey(locatorName + "_reverse")) {
val offset = if (thirdPersonReverse && cameraOffsets.containsKey(locatorName + "_reverse")) {
cameraOffsets[locatorName + "_reverse"]!!.toVector3f()
} else if (cameraOffsets.containsKey(locatorName)) {
cameraOffsets[locatorName]!!.toVector3f()
} else {
Vector3f(0f, 2f, 4f)
}
if (thirdPersonReverse) offset.z *= -1
return offset
}
private fun getFirstPersonOffset(model: PosableModel, locatorName: String): Vector3f {
@@ -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.client.render.color
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.item.components.TMMoveComponent.Companion.getTMMove
import net.minecraft.client.color.item.ItemColor
import net.minecraft.util.FastColor
import net.minecraft.world.item.ItemStack
object TechnicalMachineItemColorProvider : ItemColor {
override fun getColor(itemStack: ItemStack, layer: Int): Int {
val moveType = getTMMove(itemStack)?.elementalType ?: ElementalTypes.NORMAL
val color = if (layer == 0) moveType.primaryColor else moveType.secondaryColor
return FastColor.ARGB32.opaque(color)
}
}
@@ -92,12 +92,15 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
open var profileScale = 1F
/** Used for third person riding camera */
open var thirdPersonCameraOffset = mutableMapOf<String, Vec3>()
/** Used for third person riding camera with no view bobbing */
open var thirdPersonCameraOffsetNoViewBobbing = mutableMapOf<String, Vec3>()
/** Used for third person riding camera */
/** Used for third person riding camera with no view bobbing */
open var thirdPersonPivotOffset = mutableMapOf<String, Vec3>()
/** Used for third person riding camera with view bobbing */
open var thirdPersonCameraOffset = mutableMapOf<String, Vec3>()
/** Used for first person riding camera */
open var firstPersonCameraOffset = mutableMapOf<String, Vec3>()
@@ -201,8 +204,6 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
/**
* Animation group : animation name [: primary]
* e.g. "particle_dummy:animation.particle_dummy.dragon_claw_target:primary"
* e.g. "particle_dummy:animation.particle.dummy.stat_up
*/
fun extractAnimation(string: String): ActiveAnimation? {
val group = string.substringBefore(":")
@@ -11,7 +11,7 @@ package com.cobblemon.mod.common.client.render.models.blockbench
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.client.render.models.blockbench.animation.ActiveAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.isBattling
import com.cobblemon.mod.common.util.isDusk
@@ -49,6 +49,15 @@ class PoseAdapter(
}
}
if (json.has("isRideStyle")) {
val styles = json.get("isRideStyle").asJsonArray.map { it.asString }
conditionsList.add {
val pokemon = it.getEntity() as? PokemonEntity ?: return@add false
val rideStyle = pokemon.ridingController?.context?.style ?: return@add false
return@add styles.any { it == rideStyle.name }
}
}
addCondition("isTouchingWater") { entity, expectedValue -> entity.isInWater == expectedValue }
addCondition("isInWaterOrRain") { entity, expectedValue -> entity.isInWaterOrRain == expectedValue }
addCondition("isUnderWater") { entity, expectedValue -> entity.isUnderWater == expectedValue }
@@ -1,98 +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.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.WingFlapIdleAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BiWingedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class ArticunoModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BiWingedFrame {
override val rootPart = root.registerChildWithAllChildren("articuno")
override val head = getPart("head")
override val leftLeg = getPart("leftleg")
override val rightLeg = getPart("rightleg")
override val leftWing = getPart("leftwing")
override val rightWing = getPart("rightwing")
override var portraitScale = 2.8F
override var portraitTranslation = Vec3(-0.35, 0.6, 0.0)
override var profileScale = 0.85F
override var profileTranslation = Vec3(0.0, 0.5, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var hover: Pose
lateinit var fly: Pose
override fun registerPoses() {
standing = registerPose(
poseName = "standing",
poseTypes = UI_POSES + PoseType.STAND,
animations = arrayOf(
singleBoneLook(),
bedrock("articuno", "ground_idle")
)
)
standing = registerPose(
poseName = "walk",
poseType = PoseType.WALK,
animations = arrayOf(
singleBoneLook(),
bedrock("articuno", "ground_idle"),
BipedWalkAnimation(this, periodMultiplier = 0.7F, amplitudeMultiplier = 0.85F)
)
)
hover = registerPose(
poseName = "hover",
poseTypes = setOf(PoseType.HOVER, PoseType.FLOAT),
animations = arrayOf(
singleBoneLook(),
bedrock("articuno", "air_idle"),
WingFlapIdleAnimation(this,
flapFunction = sineFunction(verticalShift = -10F.toRadians(), period = 0.9F, amplitude = 0.6F),
timeVariable = { state, _, _ -> state.animationSeconds },
axis = ModelPartTransformation.Y_AXIS
)
)
)
fly = registerPose(
poseName = "fly",
poseTypes = setOf(PoseType.FLY, PoseType.SWIM),
animations = arrayOf(
singleBoneLook(),
bedrock("articuno", "air_fly"),
WingFlapIdleAnimation(this,
flapFunction = sineFunction(verticalShift = -14F.toRadians(), period = 0.9F, amplitude = 0.6F),
timeVariable = { state, _, _ -> state.animationSeconds },
axis = ModelPartTransformation.Y_AXIS
)
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("articuno", "faint") else null
}
@@ -1,91 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BimanualSwingAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class MewtwoModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BimanualFrame, BipedFrame {
override val rootPart = root.registerChildWithAllChildren("mewtwo")
override val head = getPart("head")
override val leftLeg = getPart("leg_left")
override val rightLeg = getPart("leg_right")
override val leftArm = getPart("arm_left")
override val rightArm = getPart("arm_right")
override var portraitScale = 2.8F
override var portraitTranslation = Vec3(-0.15, 1.5, 0.0)
override var profileScale = 0.7F
override var profileTranslation = Vec3(0.0, 0.61, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var hover: Pose
lateinit var fly: Pose
override fun registerPoses() {
val blink = quirk { bedrockStateful("mewtwo", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = UI_POSES + PoseType.STAND,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("mewtwo", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseType = PoseType.WALK,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("mewtwo", "ground_idle"),
BipedWalkAnimation(this, periodMultiplier = 0.6F, amplitudeMultiplier = 0.9F),
BimanualSwingAnimation(this, swingPeriodMultiplier = 0.6F, amplitudeMultiplier = 0.9F)
)
)
hover = registerPose(
poseName = "hover",
poseTypes = setOf(PoseType.FLOAT, PoseType.HOVER),
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("mewtwo", "air_idle")
)
)
fly = registerPose(
poseName = "fly",
poseTypes = setOf(PoseType.FLY, PoseType.SWIM),
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("mewtwo", "air_fly")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("mewtwo", "faint") else null
}
@@ -1,98 +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.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.WingFlapIdleAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BiWingedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class MoltresModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BiWingedFrame {
override val rootPart = root.registerChildWithAllChildren("moltres")
override val head = getPart("head")
override val leftLeg = getPart("leftleg")
override val rightLeg = getPart("rightleg")
override val leftWing = getPart("leftwing")
override val rightWing = getPart("rightwing")
override var portraitScale = 2.8F
override var portraitTranslation = Vec3(-0.9, 0.45, 0.0)
override var profileScale = 0.85F
override var profileTranslation = Vec3(0.0, 0.5, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var hover: Pose
lateinit var fly: Pose
override fun registerPoses() {
standing = registerPose(
poseName = "standing",
poseTypes = UI_POSES + PoseType.STAND,
animations = arrayOf(
singleBoneLook(),
bedrock("moltres", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseType = PoseType.WALK,
animations = arrayOf(
singleBoneLook(),
bedrock("moltres", "ground_idle"),
BipedWalkAnimation(this, periodMultiplier = 0.7F, amplitudeMultiplier = 0.85F)
)
)
hover = registerPose(
poseName = "hover",
poseTypes = setOf(PoseType.FLOAT, PoseType.HOVER),
animations = arrayOf(
singleBoneLook(),
bedrock("moltres", "air_idle"),
WingFlapIdleAnimation(this,
flapFunction = sineFunction(verticalShift = -10F.toRadians(), period = 0.9F, amplitude = 0.6F),
timeVariable = { state, _, _ -> state.animationSeconds },
axis = ModelPartTransformation.Y_AXIS
)
)
)
fly = registerPose(
poseName = "fly",
poseTypes = setOf(PoseType.FLY, PoseType.SWIM),
animations = arrayOf(
singleBoneLook(),
bedrock("moltres", "air_fly"),
WingFlapIdleAnimation(this,
flapFunction = sineFunction(verticalShift = -14F.toRadians(), period = 0.9F, amplitude = 0.6F),
timeVariable = { state, _, _ -> state.animationSeconds },
axis = ModelPartTransformation.Y_AXIS
)
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("moltres", "faint") else null
}
@@ -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.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class MrmimeModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame {
override val rootPart = root.registerChildWithAllChildren("mr_mime")
override val head = getPart("head")
override val leftLeg = getPart("leg_left1")
override val rightLeg = getPart("leg_right1")
override var portraitScale = 2.1F
override var portraitTranslation = Vec3(-0.15, 1.25, 0.0)
override var profileScale = 0.7F
override var profileTranslation = Vec3(0.0, 0.7, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
override fun registerPoses() {
val blink = quirk { bedrockStateful("mr_mime", "blink")}
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("mr_mime", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("mr_mime", "ground_idle"),
BipedWalkAnimation(this, periodMultiplier = 0.6F, amplitudeMultiplier = 0.9F)
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("mrmime", "faint") else null
}
@@ -1,63 +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.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class ParasectModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("parasect")
override val head = getPart("head")
override var portraitScale = 1.8F
override var portraitTranslation = Vec3(-0.35, -0.8, 0.0)
override var profileScale = 0.8F
override var profileTranslation = Vec3(-0.05, 0.45, 0.0)
lateinit var sleep: Pose
lateinit var standing: Pose
lateinit var walk: Pose
override fun registerPoses() {
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
transformTicks = 10,
animations = arrayOf(
bedrock("parasect", "ground_idle"),
singleBoneLook()
)
)
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("parasect", "sleep"))
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
transformTicks = 10,
animations = arrayOf(
bedrock("parasect", "ground_walk"),
singleBoneLook()
)
)
}
override fun getFaintAnimation(state: PosableState) = bedrockStateful("parasect", "faint")
}
@@ -29,7 +29,7 @@ class SquirtleModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override var portraitScale = 2.02F
override var portraitTranslation = Vec3(-0.12, -0.21, 0.0)
override var portraitTranslation = Vec3(-0.26, -0.21, 0.0)
override var profileScale = 0.78F
override var profileTranslation = Vec3(-0.04, 0.57, 0.0)
@@ -27,7 +27,7 @@ class WartortleModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val head = getPart("head_ai")
override var portraitScale = 1.57F
override var portraitTranslation = Vec3(-0.05, 0.54, 0.0)
override var portraitTranslation = Vec3(-0.17, 0.54, 0.0)
override var profileScale = 0.69F
override var profileTranslation = Vec3(-0.04, 0.69, 0.0)
@@ -1,99 +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.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.WingFlapIdleAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BiWingedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class ZapdosModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BiWingedFrame, BipedFrame {
override val rootPart = root.registerChildWithAllChildren("zapdos")
override val head = getPart("head")
override val leftLeg = getPart("leftleg")
override val rightLeg = getPart("rightleg")
override val leftWing = getPart("left_wing")
override val rightWing = getPart("right_wing")
override var portraitScale = 2.7F
override var portraitTranslation = Vec3(-0.9, -0.65, 0.0)
override var profileScale = 0.85F
override var profileTranslation = Vec3(0.0, 0.5, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var hover: Pose
lateinit var fly: Pose
override fun registerPoses() {
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + UI_POSES - PoseType.HOVER,
animations = arrayOf(
singleBoneLook(),
bedrock("zapdos", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES - PoseType.FLY,
animations = arrayOf(
singleBoneLook(),
BipedWalkAnimation(this, periodMultiplier = 0.7F, amplitudeMultiplier = 0.85F),
bedrock("zapdos", "ground_idle")
)
)
hover = registerPose(
poseName = "hover",
poseTypes = setOf(PoseType.HOVER, PoseType.FLOAT),
animations = arrayOf(
singleBoneLook(),
bedrock("zapdos", "air_idle"),
WingFlapIdleAnimation(this,
flapFunction = sineFunction(verticalShift = -10F.toRadians(), period = 0.9F, amplitude = 0.6F),
timeVariable = { state, _, _ -> state.animationSeconds },
axis = ModelPartTransformation.Y_AXIS
)
)
)
fly = registerPose(
poseName = "fly",
poseTypes = setOf(PoseType.FLY, PoseType.SWIM),
animations = arrayOf(
singleBoneLook(),
bedrock("zapdos", "air_fly"),
WingFlapIdleAnimation(this,
flapFunction = sineFunction(verticalShift = -14F.toRadians(), period = 0.9F, amplitude = 0.6F),
timeVariable = { state, _, _ -> state.animationSeconds },
axis = ModelPartTransformation.Y_AXIS
)
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("zapdos", "faint") else null
}
@@ -1,100 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen2
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.isBattling
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class CrobatModel(root: ModelPart) : PokemonPosableModel(root) {
override val rootPart = root.registerChildWithAllChildren("crobat")
override var portraitScale = 1.2F
override var portraitTranslation = Vec3(-0.1, 1.8, 0.0)
override var profileScale = 0.5F
override var profileTranslation = Vec3(0.0, 1.2, 0.0)
lateinit var sleep: Pose
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var hover: Pose
lateinit var fly: Pose
lateinit var battleidle: Pose
override val cryAnimation = CryProvider { bedrockStateful("crobat", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("crobat", "blink") }
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("crobat", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES - PoseType.HOVER,
condition = { !it.isBattling },
transformTicks = 10,
animations = arrayOf(
bedrock("crobat", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES - PoseType.FLY,
transformTicks = 10,
animations = arrayOf(
bedrock("crobat", "ground_walk")
)
)
hover = registerPose(
poseName = "hover",
poseType = PoseType.HOVER,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("crobat", "air_idle")
)
)
fly = registerPose(
poseName = "fly",
poseType = PoseType.FLY,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("crobat", "air_fly")
)
)
battleidle = registerPose(
poseName = "battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
condition = { it.isBattling },
animations = arrayOf(
bedrock("crobat", "battle_idle")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("crobat", "faint") else null
}
@@ -1,69 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen2
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BimanualSwingAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class SneaselHisuianModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BimanualFrame {
override val rootPart = root.registerChildWithAllChildren("sneasel_hisuian")
override val head = getPart("head")
override val leftArm = getPart("arm_left")
override val rightArm = getPart("arm_right")
override val leftLeg = getPart("leg_left")
override val rightLeg = getPart("leg_right")
override var portraitScale = 2.16F
override var portraitTranslation = Vec3(-0.18, -0.11, 0.0)
override var profileScale = 0.9F
override var profileTranslation = Vec3(0.0, 0.4, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
override val cryAnimation = CryProvider { bedrockStateful("sneasel_hisuian", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("sneasel_hisuian", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("sneasel_hisuian", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("sneasel_hisuian", "ground_idle"),
BipedWalkAnimation(this, periodMultiplier = 0.6F, amplitudeMultiplier = 0.9F),
BimanualSwingAnimation(this, swingPeriodMultiplier = 0.6F, amplitudeMultiplier = 0.9F)
//bedrock("sneasel_hisuian", "ground_walk")
)
)
}
}
@@ -1,68 +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.client.render.models.blockbench.pokemon.gen2
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BimanualSwingAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class SneaselModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BimanualFrame {
override val rootPart = root.registerChildWithAllChildren("sneasel")
override val head = getPart("head")
override val leftArm = getPart("arm_left")
override val rightArm = getPart("arm_right")
override val leftLeg = getPart("leg_left")
override val rightLeg = getPart("leg_right")
override var portraitScale = 2.16F
override var portraitTranslation = Vec3(-0.18, -0.11, 0.0)
override var profileScale = 0.9F
override var profileTranslation = Vec3(0.0, 0.4, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
override val cryAnimation = CryProvider { bedrockStateful("sneasel", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("sneasel", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("sneasel", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("sneasel", "ground_idle"),
BipedWalkAnimation(this, periodMultiplier = 0.6F, amplitudeMultiplier = 0.9F),
BimanualSwingAnimation(this, swingPeriodMultiplier = 0.6F, amplitudeMultiplier = 0.9F)
//bedrock("sneasel", "ground_walk")
)
)
}
}

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