Compare commits

...
Author SHA1 Message Date
JazzMcNade c5cc57a298 Fixed issues with pokemon drops 2024-06-02 03:18:22 -05:00
Apion fad10da89f Merge branch 'forcespawn_cmd_fix' into 'main'
Fixes #877

Closes #877

See merge request cable-mc/cobblemon!1056
2024-06-01 15:19:06 +00:00
notepadccino 055ce8168c Fixes #877 2024-06-01 15:42:21 +02:00
Hiroku e4da05fd50 we are snapshotting thx 2024-06-01 00:24:30 +00:00
Paul 3a17a88ea2 Changelog stuff [skip-ci] 2024-06-01 09:38:50 +10:00
Hiroku 09abf30421 Merge branch 'no-middle-stage-ha-fix' into 'main'
Hidden Ability slot for cocoons.

See merge request cable-mc/cobblemon!1020
2024-05-31 23:38:18 +00:00
Hiroku 06ea4b4cec Merge branch 'berry-changes' into 'main'
Berry changes

See merge request cable-mc/cobblemon!1030
2024-05-31 23:35:59 +00:00
Hiroku 8799308800 Merge branch 'Frank's-Coding-Farm' into 'main'
Enabled Seafloor spawns & Disabled spawning in slabs

See merge request cable-mc/cobblemon!1040
2024-05-31 23:35:24 +00:00
Frank The FarmerandHiroku d727fde5b3 Enabled Seafloor spawns & Disabled spawning in slabs 2024-05-31 23:35:24 +00:00
Hiroku ce5e93b552 Merge branch 'main' into 'main'
Fixes #878

Closes #878

See merge request cable-mc/cobblemon!1052
2024-05-31 23:34:39 +00:00
Hiroku 77c0138c09 Merge branch 'get-sticked' into 'main'
Leeks are Sticks (visually)

See merge request cable-mc/cobblemon!1054
2024-05-31 23:34:25 +00:00
dhi_holo dc727fe56d Leeks are Sticks (visually)
Medicinal and Roasted Leeks now reference a new display setting, making them held like sticks/rods.

That's it, that's the entire change, it's a small parity thing, this is what I like doing, okay.
2024-05-31 10:19:06 +10:00
Apion 4db36ef6fa Merge branch 'tmetcalfe89/abstract-shedding' into 'main'
abstracted shedinja logic for datapackability

See merge request cable-mc/cobblemon!1014
2024-05-30 04:02:21 +00:00
Timothy MetcalfeandApion e350d920b5 abstracted shedinja logic for datapackability 2024-05-30 04:02:21 +00:00
Apion 2a0ae1a466 Merge branch 'tumbled-bricks' into 'main'
Tumbled Blocks and Bricks

See merge request cable-mc/cobblemon!1051
2024-05-30 02:58:57 +00:00
dhi_holoandApion 3b8f7f23a3 Tumbled Blocks and Bricks 2024-05-30 02:58:57 +00:00
apion 72cf161f0d Merge branch 'refs/heads/main' into berry-changes
# Conflicts:
#	CHANGELOG.md
2024-05-29 22:24:12 -04:00
apion fe3d8d3185 Changed CHANGELOG.md merge strategy 2024-05-29 22:06:53 -04:00
apion d04ed7a519 Add empty headers to CHANGELOG.md 2024-05-29 21:51:57 -04:00
Apion e6e9137cb5 Merge branch 'AmauraFetusClippingFix' into 'main'
Amaura fetus clipping fix

Closes #873

See merge request cable-mc/cobblemon!1053
2024-05-30 00:49:20 +00:00
JazzMcNade 78f25b3f8b Corrected typo in changelog 2024-05-28 17:25:08 -05:00
JazzMcNade 631293e0cd UpdatedChangelog 2024-05-28 17:10:20 -05:00
JazzMcNade 2865b8140a Scaled down Amaura's fetus model to avoid clipping through the tank while animating 2024-05-28 17:07:46 -05:00
notepadccino be34bc4c74 Fixes #878 2024-05-28 19:12:21 +02:00
Paul df8f078d13 Fixed Wooloo Dubwool issues and fixed sync issues with Gimmighoul 2024-05-27 15:39:21 +10:00
Paul 309b223da9 Changelog update 4 [skip-ci] 2024-05-27 11:53:03 +10:00
Paul 75f688fa30 Changelog update 3 [skip-ci] 2024-05-27 11:48:22 +10:00
Paul d7fadd58ed Changelog update 2 [skip-ci] 2024-05-27 11:42:56 +10:00
Paul 996c7189b6 Changelog update [skip-ci] 2024-05-27 11:31:38 +10:00
Paul e1d5f07aed Fixed root part animations on JSON posers, clean up changelog, no snapshot, remove bubblebeam effect for now 2024-05-27 11:14:05 +10:00
Paul 6afe77ef0c Fixed confusion particle timing 2024-05-27 10:23:00 +10:00
Hiroku bf54f28b58 Merge branch 'move-effects' into 'main'
A bunch of particles, now with sound!

See merge request cable-mc/cobblemon!1049
2024-05-27 00:08:36 +00:00
Paul aac10e967f Reverted protect change to camera mode 2024-05-27 10:06:39 +10:00
Sterrezo 0cd9da80d7 Update sounds.json 2024-05-27 02:06:27 +02:00
Sterrezo d33f1150d2 Adjusted assets for powder moves, protect, swords dance and thunder wave. Rebalanced volumes of all moves. 2024-05-27 01:39:41 +02:00
Hiroku 216ed85da1 Merge branch 'IllusionThrownPokeballRevealFix' into 'main'
IllusionThrownPokeballRevealFix

See merge request cable-mc/cobblemon!1046
2024-05-26 23:35:07 +00:00
Paul 4b70b20ab7 Fixed using confusion breaking battles lol 2024-05-27 09:26:40 +10:00
Segfault Guy 125e193b81 Broadcast messages with CLIENT species on pokeball hit. 2024-05-26 19:17:16 -04:00
Paul 1ebd249351 Fixed sleeppowder effect, added redundancy to action effects in case they run into an exception 2024-05-27 09:08:22 +10:00
Segfault Guy 74727cda48 Merge branch 'main' into IllusionThrownPokeballRevealFix 2024-05-26 18:47:53 -04:00
Paul 6a8a2489d1 Fixed structure reference to cave_support not working 2024-05-27 08:27:59 +10:00
Paul 3524931596 Fixed dyeable Pokemon not being dyeable away from white unless spawned via command, fixed creative mode players still losing the dye 2024-05-27 07:06:57 +10:00
Paul 43d9347c9c Updated zh and es_es translations 2024-05-27 06:47:56 +10:00
Tyzillion 60303504cd Changelog update. 2024-05-26 11:52:30 -04:00
Tyzillion 63db0066d3 more sounds! 2024-05-26 11:49:50 -04:00
apion 6e0270ac80 Fully moved mulch into block entity 2024-05-26 11:31:21 -04:00
apion 30070d35f6 Fully moved mulch into block entity 2024-05-26 11:23:47 -04:00
apion 8e3435479a Start trying to move mulch into block entity 2024-05-26 09:39:57 -04:00
Tyzillion 333e1fe9ae Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into move-effects 2024-05-26 09:35:42 -04:00
Tyzillion c0c8c103f4 Updated changelog 2024-05-26 09:11:47 -04:00
Tyzillion a414784cca Added sounds to sand attack and the spore mushrooms, implemented confusion cuz I forgot to last night. 2024-05-26 09:10:41 -04:00
Apion b4b01ef0b6 Merge branch 'sound' into 'main'
Updated all generic impact assets

See merge request cable-mc/cobblemon!1048
2024-05-26 12:45:16 +00:00
SterrezoandApion 04f725d72e Updated all generic impact assets 2024-05-26 12:45:16 +00:00
Hiroku 0257f591de Merge branch 'implement_pokemon' into 'main'
Shedinja T Pose & Little guys sliding

See merge request cable-mc/cobblemon!1047
2024-05-26 11:40:35 +00:00
Frank The FarmerandHiroku dfeeed8796 Shedinja T Pose & Little guys sliding 2024-05-26 11:40:35 +00:00
Paul 0e304a81e8 Changelog, deleted unneeded caves template pool, licenses 2024-05-26 21:33:57 +10:00
Hiroku 03acc21af1 Merge branch 'move-effects' into 'main'
Implemented Action Effects

Closes #814, #773, #840, and #841

See merge request cable-mc/cobblemon!1045
2024-05-26 11:31:20 +00:00
Hiroku 2233e6903b Merge branch '1.5-structure-fixes' into 'main'
added loot to the trapped chest in unstable caves so you can't tell which one...

See merge request cable-mc/cobblemon!1042
2024-05-26 11:25:36 +00:00
Hiroku ac281faa3b Merge branch 'main' into 'move-effects'
# Conflicts:
#   CHANGELOG.md
2024-05-26 11:07:24 +00:00
JazzMcNade 6891272516 Throwing a ball at a player owned pokemon no longer reveals Illusion 2024-05-26 03:02:49 -05:00
Hiroku eba784071a Merge branch 'Primary-Quirks-for-JSON' into 'main'
Primary quirks for json

See merge request cable-mc/cobblemon!1044
2024-05-26 04:10:33 +00:00
Hiroku a4435af9c0 Merge branch 'GildedChestLockAnimFix' into 'main'
GildedChestLockAnimFix

Closes #811

See merge request cable-mc/cobblemon!1043
2024-05-26 04:08:22 +00:00
JazzMcNade 4b0b0f813e Merge branch 'main' into GildedChestLockAnimFix 2024-05-25 22:59:01 -05:00
JazzMcNade c32da0825c Updated Changelog 2024-05-25 22:47:39 -05:00
Tyzillion 803703b6a6 Updated changelog 2024-05-25 23:43:50 -04:00
Tyzillion 668914275e Added sounds to all the powder attacks. Tweaks and fixes. 2024-05-25 23:42:02 -04:00
FrankTheFarmer af90ff6733 Added excludedLabels 2024-05-26 13:07:16 +10:00
FrankTheFarmer c657c2ea68 CHANGELOG with example. 2024-05-26 11:27:58 +10:00
FrankTheFarmer 2f567830d4 Temporary function for bedrock quirks intended for 1.5.1 2024-05-26 11:07:14 +10:00
Tyzillion 569f3c624e Implemented Cotton guard. 2024-05-25 20:38:31 -04:00
Tyzillion f30be418f9 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into move-effects
# Conflicts:
#	CHANGELOG.md
2024-05-25 20:38:13 -04:00
Tyzillion 2c50dc2d0c Implemented Ember, Powder moves, and updated other particles. Implemented Infatuation 2024-05-25 20:34:07 -04:00
Hiroku dde2e9f52a Merge branch 'fix/form-sync' into 'main'
Form Sync on Form Change Fix

See merge request cable-mc/cobblemon!1041
2024-05-26 00:02:40 +00:00
NickandHiroku 50a21d91e8 Form Sync on Form Change Fix 2024-05-26 00:02:40 +00:00
Tyzillion 58f3de13fa Implemented Ember, Powder moves, and updated other particles. 2024-05-25 13:55:44 -04:00
Tyzillion 8d67a3a909 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into move-effects
# Conflicts:
#	CHANGELOG.md
2024-05-25 10:54:51 -04:00
JazzMcNade ad4cb8a290 Gilded Chest no longer animates to the open state on the client when the client fails to open a locked chest 2024-05-24 21:30:54 -05:00
Hiroku d9e5de5f97 Fixed pluralize usage for pose conditions 2024-05-24 22:35:48 +00:00
Hiroku e4a47af06d Merge branch 'sendout-bug' into 'main'
Fixed tiny bug, fixes #867

Closes #867

See merge request cable-mc/cobblemon!1037
2024-05-24 22:03:50 +00:00
Apion cc741c0478 Merge branch 'remulched' into 'main'
Cheap Mulch!

See merge request cable-mc/cobblemon!1039
2024-05-24 12:28:03 +00:00
dhi_holo 6887887d6b Cheap Mulch!
Made Mulch cheaper, as outlined by #866
2024-05-24 20:28:45 +10:00
Hiroku cf1100a20e Merge branch 'implement_pokemon' into 'main'
Wailord, Seedot line, and apostrophes in the changelog

See merge request cable-mc/cobblemon!1038
2024-05-24 09:46:27 +00:00
Frank The FarmerandHiroku ebc7b97e4f Wailord, Seedot line, and apostrophes in the changelog 2024-05-24 09:46:26 +00:00
apion d65ce9222a Fixed tiny bug, fixes #867 2024-05-24 05:04:36 -04:00
Apion 706221aead Merge branch 'LangFixes' into 'main'
A Couple of Lang Fixes

See merge request cable-mc/cobblemon!1035
2024-05-24 08:15:24 +00:00
Apion 26bfca893b Merge branch 'main' into 'LangFixes'
# Conflicts:
#   CHANGELOG.md
2024-05-24 07:01:46 +00:00
JazzMcNade 5f65f3c20c Updated Changelog 2024-05-24 01:47:23 -05:00
JazzMcNade 1e5239da10 Added English language keys for Rough Skin and Iron Barbs 2024-05-24 01:26:30 -05:00
JazzMcNade 7d8ec3470b Fix for Hyper Cutter / Big Pecks incorrectly stating they prevented accuracy from being lowered 2024-05-24 01:25:58 -05:00
Apion 389d2a0adc Merge branch '1.5-fixes-112313' into 'main'
1.5 fixes 112313

See merge request cable-mc/cobblemon!1034
2024-05-24 04:45:10 +00:00
apion 37433aa0c5 CHANGELOG.md 2024-05-24 00:34:04 -04:00
Paul 7fa0022095 Fixed species sync failing when madmen give pokemon more than 255 level up moves on the same level 2024-05-24 14:21:03 +10:00
Paul 9fde7a7618 Merge remote-tracking branch 'origin/main' into 1.5-fixes-112313 2024-05-24 14:18:04 +10:00
Paul 6c1f7ddd70 Putted the AttackDefenceRatioRequirement back for backcompat 2024-05-24 14:15:45 +10:00
apion 1a1cedb3aa Regenned berry data, added bone meal chance 2024-05-23 23:53:42 -04:00
Hiroku 241b83cec9 Merge branch 'implement_pokemon' into 'main'
Implementation Stuff

See merge request cable-mc/cobblemon!1027
2024-05-23 21:57:39 +00:00
Frank The FarmerandHiroku f0feb81a16 Implementation Stuff 2024-05-23 21:57:38 +00:00
apion b82287f258 Vera berry changes 2024-05-23 09:02:24 -04:00
Hiroku 00f4052b75 Merge branch 'MoreFossilMachineFixes' into 'main'
Many Fossil Machine Fixes

See merge request cable-mc/cobblemon!1033
2024-05-23 11:02:07 +00:00
Hiroku 407c24b7c5 Merge branch 'asset-updates' into 'main'
Updated wooloo/dubwool textures and passho berry stuff

Closes #854

See merge request cable-mc/cobblemon!1031
2024-05-23 10:56:56 +00:00
JazzMcNade c8904c3e38 Removed commented code, updated changelog 2024-05-23 00:24:22 -05:00
JazzMcNade 6bfc711129 Update FossilMultiblockStructure.kt 2024-05-22 23:25:46 -05:00
JazzMcNade 3d914f47ee Merge branch 'main' into MoreFossilMachineFixes 2024-05-22 23:25:26 -05:00
JazzMcNade 61891d8fe4 Refactored blockState check to for the blockstate property (credit to Charzard4261) , fixed typo 2024-05-22 21:26:49 -05:00
apion 1b66ee8ce3 Updated wooloo/dubwool textures and passho berry stuff 2024-05-22 03:57:34 -04:00
apion c884c556cb Remove binding soil 2024-05-22 03:37:04 -04:00
Apion 5e6091c782 Merge branch 'tmetcalfe89/revive-event' into 'main'
added fossil revived event

See merge request cable-mc/cobblemon!1029
2024-05-22 07:26:52 +00:00
Timothy MetcalfeandApion 5a2e5479e0 added fossil revived event 2024-05-22 07:26:52 +00:00
apion 9a8b481fa2 Changed berry grow timers per Vera request 2024-05-22 03:26:09 -04:00
apion e2fb31fad3 Merge branch 'refs/heads/main' into berry-changes 2024-05-22 02:39:51 -04:00
Tyzillion fc03682140 Implemented quick attack 2024-05-21 22:30:41 -04:00
JazzMcNade 07fbffaa71 Fixed refactor mistake in previous commit. 2024-05-21 20:45:54 -05:00
Tyzillion 5e2f5dadb4 Fixed some errors I ran into. 2024-05-21 19:36:11 -04:00
Tyzillion fc682d1cd1 Implemented action effects for Sand Attack, Protect, Minimize, and Swords Dance. 2024-05-21 19:13:06 -04:00
Tyzillion 7759c91f3b Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into move-effects
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/evolution/Evolution.kt
2024-05-21 19:11:04 -04:00
JazzMcNade c13654f60c FossilMultiblockStructure no longer stores a created pokemon, instead the pokemon is created when the pokemon is retrieved from the machine 2024-05-21 02:36:39 -05:00
JazzMcNade 289e9df52b Fixed Restoration Tank over consuming items when interacting with Create blocks 2024-05-20 21:23:13 -05:00
Tyzillion 5dca5a1579 Merge remote-tracking branch 'origin/move-effects' into move-effects 2024-05-20 15:57:38 -04:00
JazzMcNade 3ebad9dc9d Fixed a Create crash when a chute attempts to access the inventory of the top half of the Restoration Tank when the bottom half has been destoryed 2024-05-19 21:27:03 -05:00
apion c93fa02616 Fixed dispatch timeline 2024-05-19 21:13:58 -04:00
apion 5897fff954 Added boost/unboost general particles, removed the particle from growl 2024-05-19 18:05:11 -04:00
apion 2fd2137d78 Added poison (this particle is BROKEN) 2024-05-19 17:22:09 -04:00
apion 42c4d6625e Added poison (this particle is BROKEN) 2024-05-19 17:21:52 -04:00
apion b0381d1685 We do a little refactoring 2024-05-19 16:23:41 -04:00
apion 3b00043b13 Got confusion kinda working, needs tweaks 2024-05-19 02:44:05 -04:00
JazzMcNade 2b403df6ef Removed client side mutations in FossilMultiblockStructure's onUse and enabled the client to enter the function 2024-05-19 01:37:04 -05:00
apion f0d5622bab Added volatile status stuff 2024-05-19 02:20:12 -04:00
apion ec28ca0c45 Added action effect for paralysis + action effect triggers for the cant command 2024-05-19 01:11:41 -04:00
Tyzillion 0d68eedc74 Merge remote-tracking branch 'origin/move-effects' into move-effects 2024-05-18 22:24:56 -04:00
Paul c969e1731b Merge remote-tracking branch 'origin/move-effects' into move-effects 2024-05-19 09:13:29 +10:00
Paul bb77c6d4a9 Fixed particle velocity not respecting drag as much as it should oops sorry 2024-05-19 09:13:17 +10:00
Tyzillion 5dfc90c0bd Merge remote-tracking branch 'origin/move-effects' into move-effects 2024-05-18 18:52:14 -04:00
apion 26a72fe6a9 I hope this works (pt. 2) 2024-05-18 18:46:58 -04:00
apion 4bb9547822 I hope this works 2024-05-18 17:08:09 -04:00
Tyzillion ffe88d75b5 Merge remote-tracking branch 'origin/move-effects' into move-effects 2024-05-18 16:08:32 -04:00
apion d8980fabbe Fix entity_scale var 2024-05-18 14:56:47 -04:00
apion 7598413e93 Merge branch 'refs/heads/particle-rave' into move-effects 2024-05-18 13:55:24 -04:00
apion b0cc4714fc Merge branch 'refs/heads/main' into move-effects
# Conflicts:
#	CHANGELOG.md
2024-05-18 13:53:45 -04:00
JazzMcNade 5aa3f34880 Added block type checks before attempting to update the blockstate from the fossilmultiblockstructure 2024-05-18 12:15:50 -05:00
Slippy 21363405fc console log spam fix for real this time 2024-05-18 12:00:11 -05:00
JazzMcNade 340be0b2a1 Fixed an issue where the lights on the restoration tank would not animate if it was facing east 2024-05-18 11:57:16 -05:00
JazzMcNade 933e29b134 Fix for TankEntityRenderer desync 2024-05-18 11:52:54 -05:00
Tyzillion 41b27e5d75 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into move-effects
# Conflicts:
#	CHANGELOG.md
2024-05-18 10:55:29 -04:00
Tyzillion e80bf0e331 Merge remote-tracking branch 'origin/move-effects' into move-effects 2024-05-18 10:54:32 -04:00
Paul 683aa354ed Fixed particle event triggers spawning particles on the emitter, fixes #814 2024-05-18 11:10:48 +10:00
apion aa19ff2c95 Added entity scale as a particle param 2024-05-17 20:57:10 -04:00
Paul 7d5e790f75 Fixed particle rotations being inverted 2024-05-18 10:55:28 +10:00
Tyzillion 76f17c9760 Merge remote-tracking branch 'origin/move-effects' into move-effects 2024-05-17 18:33:17 -04:00
Paul 3b0d2cd980 Added parametric particle support (motion and rotation), closes #773 2024-05-17 16:43:35 +10:00
Ludichat 7e43a7424e Updated CHANGELOG.md. 2024-05-17 08:26:51 +02:00
Tyzillion f5a3c93039 Merge remote-tracking branch 'origin/move-effects' into move-effects 2024-05-16 21:35:16 -04:00
Paul 00dc501df7 Fixed timed particle ages being a tick too long, fixes #840 2024-05-17 11:28:49 +10:00
Paul 39fee0bcea Fixed particle effects generated from molang not having access to entity_radius and stuff. Fixes #841 2024-05-17 10:26:18 +10:00
Tyzillion 1162689172 Merge remote-tracking branch 'origin/move-effects' into move-effects 2024-05-16 20:08:03 -04:00
Paul b295644070 Cleaned imports 2024-05-17 09:37:29 +10:00
Paul 21df0085e2 Merge remote-tracking branch 'origin/main' into move-effects 2024-05-17 09:08:19 +10:00
Ludichat b718daf0f1 Gives a Hidden Ability slot to middle stage evolution and Ferroseed with a single ability. 2024-05-16 21:49:34 +02:00
Slippy 08c4f4a0ff added loot to the trapped chest in unstable caves so you can't tell which one is trapped if you're using lootr muwahahahaha 2024-05-16 01:57:41 -05:00
Slippy c4a1704a8b unstable cave loot table fixes 2024-05-15 20:35:24 -05:00
Tyzillion 51af37b37a Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into move-effects 2024-05-15 18:09:53 -04:00
apion a3122812e4 Added color to all sprouts 2024-05-15 07:47:43 -04:00
Slippy 837aa5acf5 missing loot table in deserted tower 2024-05-14 16:58:55 -05:00
apion 19698fc0ff Merge branch 'refs/heads/main' into berry-changes 2024-05-14 16:36:34 -04:00
Slippy 969d6ac7fc console spam fix 2024-05-12 09:23:39 -05:00
Slippy 46bb9e1aec console fix 2024-05-11 21:58:50 -05:00
Slippy 9792ff3392 console spam fix 2024-05-11 21:36:29 -05:00
Tyzillion 978d9e76a1 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into move-effects 2024-05-04 17:48:57 -04:00
Tyzillion 1e067cb7e4 Implemented simple elemental punches. Implemented sweetscent effects, and stat increase and decrease particles. 2024-04-27 22:00:56 -04:00
Tyzillion 2f464f82b1 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into move-effects 2024-04-26 21:01:38 -04:00
Tyzillion 28c0e17a48 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into move-effects 2024-04-25 18:21:18 -04:00
Paul 31136435fe Fixed autogeneration of target and special_attack locators 2024-04-25 10:20:19 +10:00
Paul bdeb83c62c Fixed entity_particles parsing of the effect ID when it's a simple string 2024-04-25 10:20:01 +10:00
Paul 15bf726421 Merge remote-tracking branch 'origin/main' into move-effects 2024-04-24 15:13:23 +10:00
DoctorWafflePhD 7f1889db7b bubblebeam VFX uploaded for review by hiroku (thanks oomfie) 2024-04-23 21:42:16 -05:00
DoctorWafflePhD 548c18bea4 hit particle fix(?) 2024-04-23 21:41:46 -05:00
DoctorWafflePhD d9203bef2a hit particle fix(?) 2024-04-23 21:06:57 -05:00
Tyzillion 5e24799295 move effects are in the works now. :) 2024-04-21 23:05:41 -04:00
Tyzillion a7243102d5 Evolution particle tweaks and Pokemon now stop moving if evolution is started. 2024-04-21 21:06:47 -04:00
Tyzillion de6a84a75b Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into evolution-effect 2024-04-21 19:37:16 -04:00
Tyzillion be3c35eb43 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into evolution-effect 2024-04-21 11:21:57 -04:00
Tyzillion b4508bf87e More tweaks to evolution particles. 2024-03-10 06:29:27 -04:00
Tyzillion dcb4f52cdb Added sounds to evolution. 2024-03-10 00:32:08 -05:00
Tyzillion 1e9b390f38 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into evolution-effect 2024-03-09 23:34:36 -05:00
Tyzillion 7edd47f995 Evolution tweaks. 2024-03-08 19:12:46 -05:00
Tyzillion dd220c5c85 Merge remote-tracking branch 'origin/evolution-effect' into evolution-effect 2024-03-07 22:02:04 -05:00
Paul bea84a37d4 Fixed a bunch of evolution animation locator issues 2024-03-08 13:01:42 +10:00
Tyzillion ac30da3c15 Merge remote-tracking branch 'origin/evolution-effect' into evolution-effect 2024-03-07 20:58:08 -05:00
Paul 3861835d47 Test command spawns bedrock entity for evo 2024-03-06 21:06:38 +10:00
Tyzillion d62fb5c4c7 Merge remote-tracking branch 'origin/evolution-effect' into evolution-effect 2024-03-03 20:58:44 -05:00
Paul ca9dde92cd Merge remote-tracking branch 'origin/main' into evolution-effect 2024-03-04 11:32:33 +10:00
Tyzillion 6ba264ddcb Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into evolution-effect 2024-03-03 20:26:59 -05:00
Tyzillion ab6c088ddf Evolution particle edits. 2024-03-03 17:56:50 -05:00
Tyzillion 18105a8b3f Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into evolution-effect 2024-03-03 17:56:44 -05:00
Tyzillion acb5984a9e Evolution particles electric boogaloo. 2024-03-03 03:50:33 -05:00
Paul 0a3ba8d4fa Fixed generic bedrock animations not being loaded 2024-03-03 17:37:43 +10:00
Paul 15259f0a95 Merge remote-tracking branch 'origin/main' into evolution-effect 2024-03-03 17:32:04 +10:00
Tyzillion 9f66c053e1 Evolution Particles BB. 2024-03-03 02:25:10 -05:00
apion 743da2817b Added rooted soil 2024-02-27 06:07:24 -05:00
apion ebdf69f496 Rebuilt blockstate jsons 2024-02-27 05:57:41 -05:00
apion 995364c960 Texture updates 2024-02-23 19:53:27 -05:00
apion 289b2ddc7b Berries now stick out of the ground when initially planted 2024-02-23 19:08:03 -05:00
1237 changed files with 38043 additions and 3182 deletions
+1
View File
@@ -3,3 +3,4 @@
# are changed when only line endings change.
src/generated/**/.cache/cache text eol=lf
src/generated/**/*.json text eol=lf
CHANGELOG.md merge=union
+112 -19
View File
@@ -1,43 +1,136 @@
# Changelog
## [1.5.1](#1-5-1)
## [1.5.3](#1-5-3)
### Fixes
- Scaled down Amaura's fetus model to avoid clipping through the tank while animating
- Added Polished Tumblestone and Tumblestone Brick block sets from Tumblestone, Black Tumblestone, and Sky Tumblestone.
- Fixed the Seafloor spawning context not being a usable context.
- Fixed Pokemon spawning in non-full blocks like slabs.
- Fixed Gilded Chests not dropping the chest itself when broken, only the contents.
- Fixed Pokémon losing their Hidden Ability through evolution if the middle stage did not have a Hidden Ability.
### Additions
- Added extra visuals to early berry growth stages
### Changes
- Altered the item model for Medicinal Leeks and Roasted Leeks to be held like sticks and other rod items.
- Adjusted some berry balance values like yield and growth times
- Changes to mulch buffs/durations
### Developer
### Data Pack & Resource Pack Creators
- Added support for "shedders" similar to Shedinja's evolution logic.
## [1.5.2 (May 27th, 2024)](#1-5-2)
### Fixes
- Fixed Wooloo and Dubwool dyeing making your game start dying. Only if you do it on a server.
- Fixed Gimmighoul causing PCs and parties to look glitched out, also only in a server.
- Fixed players being able to dye other people's sheep Pokémon.
## [1.5.1 (May 27th, 2024)](#1-5-1)
### Additions
- Added unique send out particles for Cherish, Dream, Beast, and Ancient Origin balls.
- Made Wooloo and Dubwool dye-able like sheep
- Made Wooloo and Dubwool dye-able like sheep. So cute!
- Added stat up and down particles.
- Most status effects now have particles! These include: Paralysis, Poison, Sleep, Confusion, Infatuation.
#### Move Particle Effects
- Confusion
- Cotton Guard
- Growl
- Ice Punch
- Fire Punch
- Thunder Punch
- Minimize
- Quick Attack
- Protect
- Swords Dance
- Sand Attack
- Poison Powder
- Sleep Powder
- Stun Spore
- Powder
- Rage Powder
- Spore
- Thunder Wave
### Changes
- Sounds for Relic Coin Sacks have been correctly renamed. Relic Coin Pouches received new sounds for breaking and placing.
- Readjusted Petilil portraits
- Changed "dimensions" spawn condition to check with dimension IDs instead of effects, so custom dimension IDs can be used.
- Readjusted Petilil portraits so they fit a bit better.
- Improved handling of Pokémon taken from the Restoration Tank block to be a bit more stable.
- Made Mulch cheaper to craft.
### Fixes
- Fixed a bug in which adding organic material to the fossil tank via right click was adding the full count of the stack currently in hand
- Fixed a bug in which adding organic material to the restoration tank via right click was adding the full count of the stack currently in hand - but only taking 1 of the item.
- Fixed a niche issue where some properties of entities were not initialized correctly, causing Pokémon that appeared to be level 1 until you battle them.
- Fixed Fossilized Drake being missing from the Fossils item tag.
- Fixed Gilded Chest block entity not being cleared on block break
- Fixed sherd brokenness on Forge/Supplementaries incompat
- Fixed Fossil Compartment crash with Jade / WAILA forks
- Fixed PCStore#resize not allowing PC boxes size reduction.
- Fixed Gilded Chest block entity not being cleared on block break, creating spooky ghost blocks. Old ones can be fixed by placing something like a furnace where it was, then breaking the furnace.
- Fixed sherd brokenness on Forge.
- Fixed Supplementaries incompatibility.
- Fixed Fossil Compartment crash with Jade / WAILA forks.
- Fixed pasture block PC lookups when the player is offline.
- Fixed an untranslated battle message that occurs when using a move that just ran out of PP (e.g. Fire Blast that just got spited mid-turn).
- Fixed held items being eaten even when the held item evolutions are already unlocked.
- Fixed Hisuian Decidueye not being Grass/Fighting
- Fixed both Decidueye forms learning both Triple Arrows and Spirit Shackle
- Fixed Pineco being unable to evolve into Shulker Forretress
- Fixed Kabutops T-posing when underwater. It still does not have proper swimming animations yet though.
- Fixed Hisuian Decidueye not being Grass/Fighting.
- Fixed both Decidueye forms learning both Triple Arrows and Spirit Shackle.
- Fixed Pineco being unable to evolve into Shulker Forretress.
- Fixed Kabutops T-posing when underwater. It doesn't have proper swimming animations yet, though.
- Fixed Pidgey's missing walk animation.
- Fixed recoil evo condition.
- Fixed issue where poser debug tools didnt work on JSON posers
- Fixed issue where gilded chests dont close when going far away
- Fixed Cyndaquil's hidden flames clipping if it was swimming.
- Fixed Chimecho and Chingling being unable to spawn near bells. They are meant to!
- Fixed Tyrantrum and Wailord Party Overlay models peeking through the chat box. It was kinda funny though.
- Fixed hitbox sizes for Seedot, Nuzleaf, and Shiftry.
- Fixed Budew and Lechonk sliding if they walked for too long.
- Fixed Shedinja T-posing in battle.
- Fixed recoil evolution condition not working, making things like Basculegion unobtainable.
- Fixed issue where poser debug tools didn't work on JSON posers.
- Fixed issue where gilded chests don't close when going far away.
- Fixed issue where the restoration tank's renderer was reading old data, making it appear wrong.
- Fixed issue where the lights on the restoration tank would not animate if it was facing east. Very specific.
- Fixed client crash with the fossil machine when updating block state on a chunk that is unloaded in the client. I don't understand this but the devs are sure that all of those are real words.
- Fixed Restoration Tank crash with Create upon the tank block's destruction.
- Fixed Restoration Tank over consuming items when interacting with Create blocks.
- Fixed addons that add very many moves to a learn-set causing disappearing Pokémon (visually) issues on servers.
- Fixed Hyper Cutter and Big Pecks incorrectly stating that it prevented accuracy from being lowered in battle.
- Fixed missing messages for Rough Skin and Iron Barbs in battle.
- Fixed a bug where sometimes Pokémon sendouts wouldn't create an entity, or the entity would spawn at 0 0 0 which is not a good place for a Pokémon to be. Or any of us, really.
- Fixed issue in which a locked gilded chest would animate to the open state when the client fails to open it, such as when it is locked.
- Fixed a bug where aspects of a form would not be properly reflected on form changes (eg. Normal -> Hisui).
- Fixed generic battle effect sounds not sounding the way they were intended to.
- Fixed particle effects often not having access to some specific entity functions from MoLang.
- Fixed particles sometimes lasting a single tick too long, causing (very quick) visual glitches.
- Fixed particle rotations being inverted.
- Fixed particle events not spawning at the instigating particle's location.
- Fixed a bunch of spam during world generation.
- Fixed a bug in which throwing a Poké Ball at a player owned Pokémon with the ability Illusion would reveal its true species. Hilarious meta strategy.
- Fixed root-part animations not working for JSON posed Pokémon. You didn't notice this but if we didn't fix this in this update then if you use Quick Attack a lot you'd have seen a whole lot of [this](https://cdn.discordapp.com/attachments/1076993968803434627/1242660506783715369/Minecraft__1.20.1_-_Singleplayer_2024-05-21_22-08-17.mp4?ex=66549408&is=66534288&hm=ff95ee293eb15634fd63e6546534ea279540a1c892605e8d561593ca2c5600c5&) which is damn funny but very unintended.
### Developer
- Changed SpawnAction#complete to return a nullable generic R (the spawn action result) instead of a boolean. More information this way.
- Changed SpawnAction#complete to return a nullable generic R (the spawn action result) instead of a boolean. Provides more information this way.
- Added an event that fires when a fossil is revived, with or without a player.
- Added IVS and EVS property extractors.
- Fixed PCStore#resize not allowing PC boxes size reduction.
### Data Pack & Resource Pack Creators
- Added support for MoLang conditions for quirks and poses.
- Changed the AttackDefenceRatio requirement to StatCompare and StatEqual.
- Changed the AttackDefenceRatio requirement to StatCompare and StatEqual. There is some backwards compatibility for AttackDefenceRatio, though.
- Changed "dimensions" spawn condition to check with dimension IDs instead of effects, so custom dimension IDs can be used.
- Added parametric motion and rotation support to particle effects.
- Added entity_scale as a molang var for particles (likely only applicable to Pokemon)
- Added support for primary quirk animations using the following format:
```JSON
{
"quirks": [
"q.bedrock_primary_quirk('<pokemon>', '<animation>', <minSeconds>, <maxSeconds>, <loopTimes>, '<excludedLabels>', q.curve('<waveFunction>'))"
]
}
```
### Localization
- Updated translations for:
- Simplified and Traditional Chinese.
- Spanish.
## [1.5.0 - The Ruins and Revival Update (May 12th, 2024)](#1-5-0)
#### "You're telling me that Mojang has added archaeology to the game? Hmm... that gives me an idea."
@@ -371,36 +371,6 @@ object Cobblemon {
BATTLE_VICTORY.subscribe { AdvancementHandler.onWinBattle(it) }
EVOLUTION_COMPLETE.subscribe(Priority.LOWEST) { event ->
AdvancementHandler.onEvolve(event)
val pokemon = event.pokemon
val ninjaskIdentifier = cobblemonResource("ninjask")
// Ensure the config option is enabled and that the result was a ninjask and that shedinja exists
if (this.config.ninjaskCreatesShedinja && pokemon.species.resourceIdentifier == ninjaskIdentifier && PokemonSpecies.getByIdentifier(Pokemon.SHEDINJA) != null) {
val player = pokemon.getOwnerPlayer() ?: return@subscribe
if (player.isCreative || player.inventory.containsAny { it.item is PokeBallItem }) {
var pokeball = Items.AIR
player.inventory.combinedInventory.forEach {
it.forEach {
itemStack -> if (itemStack.item is PokeBallItem && pokeball == Items.AIR) {
pokeball = itemStack.item as PokeBallItem
}
}
}
if (!player.isCreative) {
player.inventory.removeAmountIf(1) { it.item is PokeBallItem }
}
if (pokeball == Items.AIR) {
pokeball = CobblemonItems.POKE_BALL
}
val properties = event.evolution.result.copy()
properties.species = Pokemon.SHEDINJA.toString()
val product = pokemon.clone()
product.removeHeldItem()
properties.apply(product)
product.caughtBall = (pokeball as PokeBallItem).pokeBall
pokemon.storeCoordinates.get()?.store?.add(product)
CobblemonCriteria.EVOLVE_POKEMON.trigger(player, EvolvePokemonContext(event.pokemon.preEvolution!!.species.resourceIdentifier, product.species.resourceIdentifier, playerData.get(player).advancementData.totalEvolvedCount))
}
}
}
LEVEL_UP_EVENT.subscribe { AdvancementHandler.onLevelUp(it) }
TRADE_COMPLETED.subscribe { AdvancementHandler.onTradeCompleted(it) }
@@ -214,6 +214,213 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_TUMBLESTONE = this.create("polished_tumblestone", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_TUMBLESTONE_STAIRS = this.create("polished_tumblestone_stairs", StairsBlockInvoker.`cobblemon$create`(POLISHED_TUMBLESTONE.defaultState, AbstractBlock.Settings.copy(POLISHED_TUMBLESTONE)))
@JvmField
val POLISHED_TUMBLESTONE_SLAB = this.create("polished_tumblestone_slab", SlabBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_TUMBLESTONE_WALL = this.create("polished_tumblestone_wall", WallBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val CHISELED_POLISHED_TUMBLESTONE = this.create("chiseled_polished_tumblestone", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val TUMBLESTONE_BRICKS = this.create("tumblestone_bricks", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val TUMBLESTONE_BRICK_STAIRS = this.create("tumblestone_brick_stairs", StairsBlockInvoker.`cobblemon$create`(TUMBLESTONE_BRICKS.defaultState, AbstractBlock.Settings.copy(TUMBLESTONE_BRICKS)))
@JvmField
val TUMBLESTONE_BRICK_SLAB = this.create("tumblestone_brick_slab", SlabBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val TUMBLESTONE_BRICK_WALL = this.create("tumblestone_brick_wall", WallBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val CHISELED_TUMBLESTONE_BRICKS = this.create("chiseled_tumblestone_bricks", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_SKY_TUMBLESTONE = this.create("polished_sky_tumblestone", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_SKY_TUMBLESTONE_STAIRS = this.create("polished_sky_tumblestone_stairs", StairsBlockInvoker.`cobblemon$create`(POLISHED_SKY_TUMBLESTONE.defaultState, AbstractBlock.Settings.copy(POLISHED_SKY_TUMBLESTONE)))
@JvmField
val POLISHED_SKY_TUMBLESTONE_SLAB = this.create("polished_sky_tumblestone_slab", SlabBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_SKY_TUMBLESTONE_WALL = this.create("polished_sky_tumblestone_wall", WallBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val CHISELED_POLISHED_SKY_TUMBLESTONE = this.create("chiseled_polished_sky_tumblestone", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val SKY_TUMBLESTONE_BRICKS = this.create("sky_tumblestone_bricks", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val SKY_TUMBLESTONE_BRICK_STAIRS = this.create("sky_tumblestone_brick_stairs", StairsBlockInvoker.`cobblemon$create`(
SKY_TUMBLESTONE_BRICKS.defaultState, AbstractBlock.Settings.copy(SKY_TUMBLESTONE_BRICKS)))
@JvmField
val SKY_TUMBLESTONE_BRICK_SLAB = this.create("sky_tumblestone_brick_slab", SlabBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val SKY_TUMBLESTONE_BRICK_WALL = this.create("sky_tumblestone_brick_wall", WallBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val CHISELED_SKY_TUMBLESTONE_BRICKS = this.create("chiseled_sky_tumblestone_bricks", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_BLACK_TUMBLESTONE = this.create("polished_black_tumblestone", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_BLACK_TUMBLESTONE_STAIRS = this.create("polished_black_tumblestone_stairs", StairsBlockInvoker.`cobblemon$create`(POLISHED_BLACK_TUMBLESTONE.defaultState, AbstractBlock.Settings.copy(POLISHED_BLACK_TUMBLESTONE)))
@JvmField
val POLISHED_BLACK_TUMBLESTONE_SLAB = this.create("polished_black_tumblestone_slab", SlabBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_BLACK_TUMBLESTONE_WALL = this.create("polished_black_tumblestone_wall", WallBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val CHISELED_POLISHED_BLACK_TUMBLESTONE = this.create("chiseled_polished_black_tumblestone", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val BLACK_TUMBLESTONE_BRICKS = this.create("black_tumblestone_bricks", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val BLACK_TUMBLESTONE_BRICK_STAIRS = this.create("black_tumblestone_brick_stairs", StairsBlockInvoker.`cobblemon$create`(
BLACK_TUMBLESTONE_BRICKS.defaultState, AbstractBlock.Settings.copy(BLACK_TUMBLESTONE_BRICKS)))
@JvmField
val BLACK_TUMBLESTONE_BRICK_SLAB = this.create("black_tumblestone_brick_slab", SlabBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val BLACK_TUMBLESTONE_BRICK_WALL = this.create("black_tumblestone_brick_wall", WallBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val CHISELED_BLACK_TUMBLESTONE_BRICKS = this.create("chiseled_black_tumblestone_bricks", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val BLACK_APRICORN = apricornBlock("black_apricorn", Apricorn.BLACK)
@JvmField
@@ -528,6 +735,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
val WEPEAR_BERRY = this.berryBlock("wepear")
val WIKI_BERRY = this.berryBlock("wiki")
val YACHE_BERRY = this.berryBlock("yache")
//val BINDING_SOIL = this.create("binding_soil", BindingSoilBlock(AbstractBlock.Settings.create()))
init {
/**
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
import com.cobblemon.mod.common.item.battle.DireHitItem
import com.cobblemon.mod.common.item.battle.GuardSpecItem
import com.cobblemon.mod.common.item.battle.XStatItem
import com.cobblemon.mod.common.item.berry.BerryItem
import com.cobblemon.mod.common.item.berry.FriendshipRaisingBerryItem
import com.cobblemon.mod.common.item.berry.HealingBerryItem
import com.cobblemon.mod.common.item.berry.PPRestoringBerryItem
@@ -932,6 +933,67 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val BLACK_TUMBLESTONE_BLOCK = blockItem("black_tumblestone_block", CobblemonBlocks.BLACK_TUMBLESTONE_BLOCK)
@JvmField
val POLISHED_TUMBLESTONE = blockItem("polished_tumblestone", CobblemonBlocks.POLISHED_TUMBLESTONE)
@JvmField
val POLISHED_TUMBLESTONE_STAIRS = blockItem("polished_tumblestone_stairs", CobblemonBlocks.POLISHED_TUMBLESTONE_STAIRS)
@JvmField
val POLISHED_TUMBLESTONE_SLAB = blockItem("polished_tumblestone_slab", CobblemonBlocks.POLISHED_TUMBLESTONE_SLAB)
@JvmField
val POLISHED_TUMBLESTONE_WALL = blockItem("polished_tumblestone_wall", CobblemonBlocks.POLISHED_TUMBLESTONE_WALL)
@JvmField
val CHISELED_POLISHED_TUMBLESTONE = blockItem("chiseled_polished_tumblestone", CobblemonBlocks.CHISELED_POLISHED_TUMBLESTONE)
@JvmField
val TUMBLESTONE_BRICKS = blockItem("tumblestone_bricks", CobblemonBlocks.TUMBLESTONE_BRICKS)
@JvmField
val TUMBLESTONE_BRICK_STAIRS = blockItem("tumblestone_brick_stairs", CobblemonBlocks.TUMBLESTONE_BRICK_STAIRS)
@JvmField
val TUMBLESTONE_BRICK_SLAB = blockItem("tumblestone_brick_slab", CobblemonBlocks.TUMBLESTONE_BRICK_SLAB)
@JvmField
val TUMBLESTONE_BRICK_WALL = blockItem("tumblestone_brick_wall", CobblemonBlocks.TUMBLESTONE_BRICK_WALL)
@JvmField
val CHISELED_TUMBLESTONE_BRICKS = blockItem("chiseled_tumblestone_bricks", CobblemonBlocks.CHISELED_TUMBLESTONE_BRICKS)
@JvmField
val POLISHED_SKY_TUMBLESTONE = blockItem("polished_sky_tumblestone", CobblemonBlocks.POLISHED_SKY_TUMBLESTONE)
@JvmField
val POLISHED_SKY_TUMBLESTONE_STAIRS = blockItem("polished_sky_tumblestone_stairs", CobblemonBlocks.POLISHED_SKY_TUMBLESTONE_STAIRS)
@JvmField
val POLISHED_SKY_TUMBLESTONE_SLAB = blockItem("polished_sky_tumblestone_slab", CobblemonBlocks.POLISHED_SKY_TUMBLESTONE_SLAB)
@JvmField
val POLISHED_SKY_TUMBLESTONE_WALL = blockItem("polished_sky_tumblestone_wall", CobblemonBlocks.POLISHED_SKY_TUMBLESTONE_WALL)
@JvmField
val CHISELED_POLISHED_SKY_TUMBLESTONE = blockItem("chiseled_polished_sky_tumblestone", CobblemonBlocks.CHISELED_POLISHED_SKY_TUMBLESTONE)
@JvmField
val SKY_TUMBLESTONE_BRICKS = blockItem("sky_tumblestone_bricks", CobblemonBlocks.SKY_TUMBLESTONE_BRICKS)
@JvmField
val SKY_TUMBLESTONE_BRICK_STAIRS = blockItem("sky_tumblestone_brick_stairs", CobblemonBlocks.SKY_TUMBLESTONE_BRICK_STAIRS)
@JvmField
val SKY_TUMBLESTONE_BRICK_SLAB = blockItem("sky_tumblestone_brick_slab", CobblemonBlocks.SKY_TUMBLESTONE_BRICK_SLAB)
@JvmField
val SKY_TUMBLESTONE_BRICK_WALL = blockItem("sky_tumblestone_brick_wall", CobblemonBlocks.SKY_TUMBLESTONE_BRICK_WALL)
@JvmField
val CHISELED_SKY_TUMBLESTONE_BRICKS = blockItem("chiseled_sky_tumblestone_bricks", CobblemonBlocks.CHISELED_SKY_TUMBLESTONE_BRICKS)
@JvmField
val POLISHED_BLACK_TUMBLESTONE = blockItem("polished_black_tumblestone", CobblemonBlocks.POLISHED_BLACK_TUMBLESTONE)
@JvmField
val POLISHED_BLACK_TUMBLESTONE_STAIRS = blockItem("polished_black_tumblestone_stairs", CobblemonBlocks.POLISHED_BLACK_TUMBLESTONE_STAIRS)
@JvmField
val POLISHED_BLACK_TUMBLESTONE_SLAB = blockItem("polished_black_tumblestone_slab", CobblemonBlocks.POLISHED_BLACK_TUMBLESTONE_SLAB)
@JvmField
val POLISHED_BLACK_TUMBLESTONE_WALL = blockItem("polished_black_tumblestone_wall", CobblemonBlocks.POLISHED_BLACK_TUMBLESTONE_WALL)
@JvmField
val CHISELED_POLISHED_BLACK_TUMBLESTONE = blockItem("chiseled_polished_black_tumblestone", CobblemonBlocks.CHISELED_POLISHED_BLACK_TUMBLESTONE)
@JvmField
val BLACK_TUMBLESTONE_BRICKS = blockItem("black_tumblestone_bricks", CobblemonBlocks.BLACK_TUMBLESTONE_BRICKS)
@JvmField
val BLACK_TUMBLESTONE_BRICK_STAIRS = blockItem("black_tumblestone_brick_stairs", CobblemonBlocks.BLACK_TUMBLESTONE_BRICK_STAIRS)
@JvmField
val BLACK_TUMBLESTONE_BRICK_SLAB = blockItem("black_tumblestone_brick_slab", CobblemonBlocks.BLACK_TUMBLESTONE_BRICK_SLAB)
@JvmField
val BLACK_TUMBLESTONE_BRICK_WALL = blockItem("black_tumblestone_brick_wall", CobblemonBlocks.BLACK_TUMBLESTONE_BRICK_WALL)
@JvmField
val CHISELED_BLACK_TUMBLESTONE_BRICKS = blockItem("chiseled_black_tumblestone_bricks", CobblemonBlocks.CHISELED_BLACK_TUMBLESTONE_BRICKS)
@JvmField
val AUTOMATON_ARMOR_TRIM_SMITHING_TEMPLATE: SmithingTemplateItem = this.create(
"automaton_armor_trim_smithing_template",
@@ -985,6 +1047,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val STEEL_GEM = noSettingsItem("steel_gem")
@JvmField
val FAIRY_GEM = noSettingsItem("fairy_gem")
//@JvmField
//val BINDING_SOIL = blockItem("binding_soil", CobblemonBlocks.BINDING_SOIL)
private fun blockItem(name: String, block: Block): BlockItem = this.create(name, BlockItem(block, Item.Settings()))
@@ -235,6 +235,7 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(TradeableUpdatePacket.ID, TradeableUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(SpeciesFeatureUpdatePacket.ID, SpeciesFeatureUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(OriginalTrainerUpdatePacket.ID, OriginalTrainerUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(FormUpdatePacket.ID, FormUpdatePacket::decode, PokemonUpdatePacketHandler())
// Evolution start
this.createClientBound(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler())
@@ -16,7 +16,7 @@ import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.item.berry.BerryItem
import com.cobblemon.mod.common.pokemon.Nature
import com.cobblemon.mod.common.util.readBox
import com.cobblemon.mod.common.util.writeBox
@@ -45,9 +45,8 @@ import net.minecraft.world.biome.Biome
* @property identifier The [Identifier] of this berry.
* @property baseYield The [IntRange] possible for the berry tree before [bonusYield] is calculated.
* @property preferredBiomeTags The [TagKey]s of the berries preffered biomes. Determines spawning and yield
* @property growthTime The [IntRange] possible in minutes for how long the berry tree takes to grow.
* @property refreshRate The [IntRange] possible in minutes for how long the berry tree takes to regrow its berries once it has been harvested.
* @depreciated @property lifeCycles The [IntRange] possible for the berry to live for between harvests.
* @property growthTime The [IntRange] possible in minutes for how long the berry tree takes to grow frome age 0 to age 3
* @property refreshRate The [IntRange] possible in minutes for how long the berry tree takes to grow from age 3 to age 5
* @property favoriteMulches The types of mulches that benefit this berries yield
* @property growthFactors An array of [GrowthFactor]s that will affect this berry. The client is not aware of these.
* @property mutations A map of the partner berry as the key and the value as the resulting mutation with this berry.
@@ -60,6 +59,7 @@ import net.minecraft.world.biome.Biome
* @property flowerTexture The [Identifier] for the texture of the berry in flower form. This is resolved into a [ModelPart] on the client.
* @property fruitModelIdentifier The [Identifier] for the model of the berry in flower form. This is resolved into a [ModelPart] on the client.
* @property fruitTexture The [Identifier] for the texture of the berry in flower form.
* @property stageOnePositioning Transformation of the berry model for age 0
*
* @throws IllegalArgumentException if the any yield range argument is not a positive range.
*/
@@ -69,7 +69,6 @@ class Berry(
val preferredBiomeTags: List<TagKey<Biome>>,
val growthTime: IntRange,
val refreshRate: IntRange,
//val lifeCycles: IntRange,
val favoriteMulches: EnumSet<MulchVariant>,
val growthFactors: Collection<GrowthFactor>,
val spawnConditions: List<BerrySpawnCondition>,
@@ -88,7 +87,9 @@ class Berry(
@SerializedName("fruitModel")
val fruitModelIdentifier: Identifier,
val fruitTexture: Identifier,
val weight: Float
val stageOnePositioning: GrowthPoint,
val weight: Float,
val boneMealChance: Float
) {
@Transient
@@ -161,8 +162,9 @@ class Berry(
val bonus = this.bonusYield(world, state, pos)
var yield = base + bonus.first
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
if (BerryBlock.getMulch(state) == MulchVariant.RICH) {
yield = min(yield + 1, maxYield())
if (BerryBlock.getMulch(treeEntity) == MulchVariant.RICH) {
//I hope the upper bound isnt exclusive, especially when there's a method called nextBetweenExclusive
yield += world.random.nextBetween(1, 3)
treeEntity.decrementMulchDuration(world, pos, state)
}
val event = BerryYieldCalculationEvent(this, world, state, pos, placer, yield, bonus.second)
@@ -279,6 +281,13 @@ class Berry(
buffer.writeIdentifier(this.flowerTexture)
buffer.writeIdentifier(this.fruitModelIdentifier)
buffer.writeIdentifier(this.fruitTexture)
buffer.writeDouble(stageOnePositioning.position.x)
buffer.writeDouble(stageOnePositioning.position.y)
buffer.writeDouble(stageOnePositioning.position.z)
buffer.writeDouble(stageOnePositioning.rotation.x)
buffer.writeDouble(stageOnePositioning.rotation.y)
buffer.writeDouble(stageOnePositioning.rotation.z)
buffer.writeFloat(boneMealChance)
}
/**
@@ -292,7 +301,8 @@ class Berry(
private fun bonusYield(world: World, state: BlockState, pos: BlockPos): Pair<Int, Collection<GrowthFactor>> {
var bonus = 0
val passed = arrayListOf<GrowthFactor>()
val mulchVariant = BerryBlock.getMulch(state)
val treeEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return 0 to passed
val mulchVariant = BerryBlock.getMulch(treeEntity)
val hasBiomeMulch = mulchVariant in favoriteMulches
this.growthFactors.forEach { factor ->
if (factor.isValid(world, state, pos)) {
@@ -339,7 +349,9 @@ class Berry(
val flowerTexture = buffer.readIdentifier()
val fruitModelIdentifier = buffer.readIdentifier()
val fruitTexture = buffer.readIdentifier()
return Berry(identifier, baseYield, emptyList(), growthTime, refreshRate, favMulchs, emptySet(), emptyList(), growthPoints, randomizedGrowthPoints, mutations, sproutShapeBoxes, matureShapeBoxes, flavors, tintIndexes, flowerModelIdentifier, flowerTexture, fruitModelIdentifier, fruitTexture, 0F)
val stageOneYPos = GrowthPoint(Vec3d(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()), Vec3d(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()))
val boneMealChance = buffer.readFloat()
return Berry(identifier, baseYield, emptyList(), growthTime, refreshRate, favMulchs, emptySet(), emptyList(), growthPoints, randomizedGrowthPoints, mutations, sproutShapeBoxes, matureShapeBoxes, flavors, tintIndexes, flowerModelIdentifier, flowerTexture, fruitModelIdentifier, fruitTexture, stageOneYPos, 0F, boneMealChance)
}
}
@@ -78,6 +78,8 @@ object CobblemonEvents {
val POKE_BALL_CAPTURE_CALCULATED = EventObservable<PokeBallCaptureCalculatedEvent>()
@JvmField
val POKEMON_CAPTURED = EventObservable<PokemonCapturedEvent>()
@JvmField
val FOSSIL_REVIVED = EventObservable<FossilRevivedEvent>()
// @JvmField
// val EGG_HATCH = EventObservable<HatchEggEvent>()
@JvmField
@@ -0,0 +1,17 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.network.ServerPlayerEntity
data class FossilRevivedEvent (
val pokemon: Pokemon,
val player: ServerPlayerEntity?
)
@@ -10,9 +10,7 @@ package com.cobblemon.mod.common.api.moves.animations
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.cobblemon.mod.common.api.moves.animations.keyframes.ActionEffectKeyframe
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
import java.util.concurrent.CompletableFuture
@@ -43,6 +41,9 @@ class ActionEffectTimeline(
// .toList copy because I'm paranoid about iterators being trying to share between identical effects playing
chainKeyframes(context, timeline.toList().iterator(), finalFuture)
finalFuture
}.exceptionallyCompose {
it.printStackTrace()
CompletableFuture.completedFuture(Unit)
}
}
@@ -55,6 +56,7 @@ class ActionEffectTimeline(
keyframe.play(context)
.thenRun { context.currentKeyframes.remove(keyframe) }
.thenApply { chainKeyframes(context, iterator, finalFuture) }
.exceptionally { finalFuture.completeExceptionally(it) }
}
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.EntityProvider
@@ -40,7 +39,7 @@ class EntityParticlesActionEffectKeyframe : ConditionalActionEffectKeyframe(), E
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
val effectIdentifier = try {
effect?.asExpressionLike()?.resolveString(context.runtime)
effect?.asExpressionLike()?.resolveString(context.runtime)?.takeIf { it != "0.0" } ?: effect
} catch (e: Exception) {
effect
}?.asIdentifierDefaultingNamespace() ?: return skip()
@@ -8,24 +8,30 @@
package com.cobblemon.mod.common.api.mulch
import com.cobblemon.mod.common.BakingOverride
import com.cobblemon.mod.common.client.CobblemonBakingOverrides
import net.minecraft.util.StringIdentifiable
/**
* Represents the different types of Mulch implemented in the mod.
*
*/
enum class MulchVariant(val duration: Int = -1) : StringIdentifiable {
COARSE,
GROWTH(3),
HUMID,
LOAMY,
PEAT,
RICH(3),
SANDY,
SURPRISE(3),
NONE;
enum class MulchVariant(val model: BakingOverride?, val duration: Int = -1) : StringIdentifiable {
COARSE(CobblemonBakingOverrides.COARSE_MULCH),
GROWTH(CobblemonBakingOverrides.GROWTH_MULCH, 5),
HUMID(CobblemonBakingOverrides.HUMID_MULCH),
LOAMY(CobblemonBakingOverrides.LOAMY_MULCH),
PEAT(CobblemonBakingOverrides.PEAT_MULCH),
RICH(CobblemonBakingOverrides.RICH_MULCH, 5),
SANDY(CobblemonBakingOverrides.SANDY_MULCH),
SURPRISE(CobblemonBakingOverrides.SURPRISE_MULCH, 3),
NONE(null);
override fun asString(): String {
return name.lowercase()
}
companion object {
const val GROWTH_TIME_MULTIPLIER = 0.5
}
}
@@ -52,11 +52,9 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
if(hand == Hand.OFF_HAND) {
return ActionResult.SUCCESS
}
if (!world.isClient) {
val entity = world.getBlockEntity(pos) as MultiblockEntity?
if (entity?.multiblockStructure != null) {
return entity.multiblockStructure!!.onUse(state, world, pos, player, hand, hit)
}
val entity = world.getBlockEntity(pos) as MultiblockEntity?
if (entity?.multiblockStructure != null) {
return entity.multiblockStructure!!.onUse(state, world, pos, player, hand, hit)
}
return super.onUse(state, world, pos, player, hand, hit)
}
@@ -47,6 +47,10 @@ fun interface PokemonPropertyExtractor {
val NICKNAME = add { pokemon, properties -> properties.nickname = pokemon.nickname }
@JvmField
val STATUS = add { pokemon, properties -> properties.status = pokemon.status?.status?.showdownName }
@JvmField
val IVS = add { pokemon, properties -> properties.ivs = pokemon.ivs }
@JvmField
val EVS = add { pokemon, properties -> properties.evs = pokemon.evs }
@JvmField
val ILLUSION = mutableListOf(SPECIES, FORM, ASPECTS, GENDER, NICKNAME, SHINY)
@@ -8,7 +8,11 @@
package com.cobblemon.mod.common.api.pokemon.evolution
import com.cobblemon.mod.common.Cobblemon.playerData
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.EvolvePokemonContext
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEvent
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionTestedEvent
@@ -17,7 +21,9 @@ import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
import com.cobblemon.mod.common.api.scheduling.afterOnServer
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
@@ -25,6 +31,7 @@ import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.party
import net.minecraft.item.ItemStack
import net.minecraft.sound.SoundCategory
@@ -44,6 +51,11 @@ interface Evolution : EvolutionLike {
*/
val result: PokemonProperties
/**
* The shed result of this evolution.
*/
val shedder: PokemonProperties?
/**
* If this evolution allows the user to choose when to start it or not.
*/
@@ -95,6 +107,36 @@ interface Evolution : EvolutionLike {
return true
}
fun shed(pokemon: Pokemon): Boolean {
val innerShedder = shedder ?: return false
val owner = pokemon.getOwnerPlayer() ?: return false
// If the player has at least one Pokeball in their inventory.
var pokeballStack: ItemStack? = null
if (!owner.isCreative) {
for (i in 0 until owner.inventory.size()) {
val stackI = owner.inventory.getStack(i)
if (stackI.isIn(CobblemonItemTags.POKE_BALLS)) {
pokeballStack = stackI
}
}
if (pokeballStack == null) return false
}
// If the player has at least one empty spot in their party.
val emptySlot = owner.party().getFirstAvailablePosition() ?: return false
// Add shed Pokemon to player's party
val shedPokemon = pokemon.clone()
shedPokemon.removeHeldItem()
innerShedder.apply(shedPokemon)
shedPokemon.caughtBall = ((pokeballStack?.item ?: CobblemonItems.POKE_BALL) as PokeBallItem).pokeBall
pokemon.storeCoordinates.get()?.store?.add(shedPokemon)
CobblemonCriteria.EVOLVE_POKEMON.trigger(owner, EvolvePokemonContext(pokemon.preEvolution!!.species.resourceIdentifier, shedPokemon.species.resourceIdentifier, playerData.get(owner).advancementData.totalEvolvedCount))
// Consume one of the balls
pokeballStack?.decrement(1)
return true
}
/**
* Starts this evolution as soon as possible.
@@ -155,6 +197,7 @@ interface Evolution : EvolutionLike {
// we want to instantly tick for example you might only evolve your Bulbasaur at level 34 so Venusaur should be immediately available
pokemon.lockedEvolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
pokemon.getOwnerPlayer()?.playSound(CobblemonSounds.EVOLVING, SoundCategory.NEUTRAL, 1F, 1F)
this.shed(pokemon)
CobblemonEvents.EVOLUTION_COMPLETE.post(EvolutionCompleteEvent(pokemon, this))
}
@@ -101,7 +101,7 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
val level = buffer.readSizedInt(IntSize.U_SHORT)
val moves = mutableListOf<MoveTemplate>()
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
repeat(times = buffer.readSizedInt(IntSize.U_SHORT)) {
Moves.getByNumericalId(buffer.readInt())?.let(moves::add)
}
levelUpMoves[level] = moves
@@ -112,7 +112,7 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
buffer.writeSizedInt(IntSize.U_BYTE, levelUpMoves.size)
for ((level, moves) in levelUpMoves) {
buffer.writeSizedInt(IntSize.U_SHORT, level)
buffer.writeSizedInt(IntSize.U_BYTE, moves.size)
buffer.writeSizedInt(IntSize.U_SHORT, moves.size)
for (move in moves) {
buffer.writeInt(move.num)
}
@@ -51,8 +51,8 @@ enum class Stats(override val identifier: Identifier, override val displayName:
/** Gets the [Stat] from the respective Showdown id. */
fun getStat(statKey: String) = when(statKey) {
"atk" -> ATTACK
"def" -> DEFENCE
"atk", "Attack" -> ATTACK // Hyper Cutter states the full stat name "Attack"
"def", "Defense" -> DEFENCE // Big Pecks states the full stat name "Defense"
"spa" -> SPECIAL_ATTACK
"spd" -> SPECIAL_DEFENCE
"spe" -> SPEED
@@ -8,6 +8,9 @@
package com.cobblemon.mod.common.api.pokemon.status
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import net.minecraft.util.Identifier
/**
@@ -20,4 +23,6 @@ open class Status(
val showdownName: String = "",
val applyMessage: String,
val removeMessage: String
)
) {
fun getActionEffect() = ActionEffects.actionEffects[name]
}
@@ -9,21 +9,29 @@
package com.cobblemon.mod.common.api.pokemon.status
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import com.cobblemon.mod.common.pokemon.status.VolatileStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.nonpersistent.ConfuseStatus
import com.cobblemon.mod.common.pokemon.status.statuses.nonpersistent.AttractStatus
import net.minecraft.util.Identifier
/**
* Main API point for Statuses
* Get or register Statuses
*
* NOTE: May seem weird to have so many things called volatile statuses but the package is called nonpersistent.
* Its because volatile is a reserved keyword in Java. Cant use it in a package name
*
* @author Deltric
*/
object Statuses {
private val persistentStatuses = mutableListOf<Status>()
private val volatileStatuses = mutableListOf<Status>()
private val allStatuses = mutableListOf<Status>()
val POISON = registerStatus(PoisonStatus())
@@ -32,13 +40,23 @@ object Statuses {
val SLEEP = registerStatus(SleepStatus())
val FROZEN = registerStatus(FrozenStatus())
val BURN = registerStatus(BurnStatus())
val ATTRACT = registerStatus(AttractStatus())
val CONFUSE = registerStatus(ConfuseStatus())
fun <T: Status> registerStatus(status: T) : T {
if (status is PersistentStatus) {
persistentStatuses.add(status)
}
else if (status is VolatileStatus) {
volatileStatuses.add(status)
}
allStatuses.add(status)
return status
}
fun getStatus(name: Identifier) = allStatuses.find { status -> status.name == name }
fun getStatus(showdownName: String) = allStatuses.find { it.showdownName == showdownName }
fun getPersistentStatuses() = allStatuses.filterIsInstance<PersistentStatus>()
fun getPersistentStatuses() = persistentStatuses
}
@@ -79,15 +79,15 @@ class ParticleEvent(
particleEffect?.let { effect ->
val bedrockParticleEffect = BedrockParticleEffectRepository.getEffect(effect.effect) ?: return@let
val rootMatrix = when (effect.type) {
EventParticleEffect.EventParticleType.EMITTER -> MatrixWrapper().updatePosition(storm.matrixWrapper.getOrigin())
EventParticleEffect.EventParticleType.EMITTER_BOUND -> storm.matrixWrapper
EventParticleEffect.EventParticleType.EMITTER,// -> MatrixWrapper().updatePosition(storm.matrixWrapper.getOrigin())
EventParticleEffect.EventParticleType.EMITTER_BOUND,// -> storm.matrixWrapper
EventParticleEffect.EventParticleType.PARTICLE,
EventParticleEffect.EventParticleType.PARTICLE_WITH_VELOCITY -> (particle?.let { Vec3d(it.getX(), it.getY(), it.getZ()) } ?: Vec3d(storm.getX(), storm.getY(), storm.getZ())).let { MatrixWrapper().updatePosition(it) }
}
val sourceVelocity = when (effect.type) {
EventParticleEffect.EventParticleType.EMITTER -> storm.sourceVelocity().let { { it } }
EventParticleEffect.EventParticleType.EMITTER_BOUND -> storm.sourceVelocity
EventParticleEffect.EventParticleType.EMITTER,// -> storm.sourceVelocity().let { { it } }
EventParticleEffect.EventParticleType.EMITTER_BOUND,// -> storm.sourceVelocity
EventParticleEffect.EventParticleType.PARTICLE -> { { Vec3d.ZERO } }
EventParticleEffect.EventParticleType.PARTICLE_WITH_VELOCITY -> (particle?.let { Vec3d(it.getVelocityX(), it.getVelocityY(), it.getVelocityZ()) } ?: Vec3d.ZERO).let { { it } }
}
@@ -15,10 +15,12 @@ import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.codec.CodecMapped
import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.client.render.SnowstormParticle
import com.cobblemon.mod.common.util.codec.EXPRESSION_CODEC
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.math.geometry.transformDirection
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.resolveVec3d
import com.mojang.serialization.Codec
import com.mojang.serialization.DynamicOps
import com.mojang.serialization.codecs.PrimitiveCodec
@@ -35,20 +37,79 @@ interface ParticleMotion : CodecMapped {
init {
registerSubtype(ParticleMotionType.DYNAMIC, DynamicParticleMotion::class.java, DynamicParticleMotion.CODEC)
registerSubtype(ParticleMotionType.STATIC, StaticParticleMotion::class.java, StaticParticleMotion.CODEC)
registerSubtype(ParticleMotionType.PARAMETRIC, ParametricParticleMotion::class.java, ParametricParticleMotion.CODEC)
}
}
val type: ParticleMotionType
fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3d, emitterPos: Vec3d): Vec3d
fun getAcceleration(runtime: MoLangRuntime, velocity: Vec3d): Vec3d
fun getVelocity(runtime: MoLangRuntime, particle: SnowstormParticle, velocity: Vec3d): Vec3d
fun getParticleDirection(runtime: MoLangRuntime, storm: ParticleStorm, velocity: Vec3d, minSpeed: Float): Vec3d
}
enum class ParticleMotionType {
DYNAMIC,
PARAMETRIC, // TODO figure out how that even works
PARAMETRIC,
STATIC
}
class ParametricParticleMotion(
var offset: Triple<Expression, Expression, Expression> = Triple(
NumberExpression(0.0),
NumberExpression(0.0),
NumberExpression(0.0)
),
var direction: Triple<Expression, Expression, Expression> = Triple(
NumberExpression(0.0),
NumberExpression(0.0),
NumberExpression(0.0)
)
) : ParticleMotion {
companion object {
val CODEC: Codec<ParametricParticleMotion> = RecordCodecBuilder.create { instance ->
instance.group(
PrimitiveCodec.STRING.fieldOf("type").forGetter { it.type.name },
EXPRESSION_CODEC.listOf().fieldOf("offset").forGetter { listOf(it.offset.first, it.offset.second, it.offset.third) },
EXPRESSION_CODEC.listOf().fieldOf("direction").forGetter { listOf(it.direction.first, it.direction.second, it.direction.third) }
).apply(instance) { _, offset, direction -> ParametricParticleMotion(Triple(offset[0], offset[1], offset[2]), Triple(direction[0], direction[1], direction[2])) }
}
}
override val type = ParticleMotionType.PARAMETRIC
override fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3d, emitterPos: Vec3d) = Vec3d.ZERO
override fun getVelocity(runtime: MoLangRuntime, particle: SnowstormParticle, velocity: Vec3d): Vec3d {
val stormPosition = Vec3d(particle.storm.getX(), particle.storm.getY(), particle.storm.getZ())
val offset = runtime.resolveVec3d(this.offset)
val particlePosition = Vec3d(particle.getX(), particle.getY(), particle.getZ())
val desiredPosition = stormPosition.add(offset)
return desiredPosition.subtract(particlePosition)
}
override fun getParticleDirection(runtime: MoLangRuntime, storm: ParticleStorm, velocity: Vec3d, minSpeed: Float) = runtime.resolveVec3d(direction).normalize()
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun readFromBuffer(buffer: PacketByteBuf) {
offset = Triple(
MoLang.createParser(buffer.readString()).parseExpression(),
MoLang.createParser(buffer.readString()).parseExpression(),
MoLang.createParser(buffer.readString()).parseExpression()
)
direction = Triple(
MoLang.createParser(buffer.readString()).parseExpression(),
MoLang.createParser(buffer.readString()).parseExpression(),
MoLang.createParser(buffer.readString()).parseExpression()
)
}
override fun writeToBuffer(buffer: PacketByteBuf) {
buffer.writeString(offset.first.getString())
buffer.writeString(offset.second.getString())
buffer.writeString(offset.third.getString())
buffer.writeString(direction.first.getString())
buffer.writeString(direction.second.getString())
buffer.writeString(direction.third.getString())
}
}
class DynamicParticleMotion(
var direction: ParticleMotionDirection = InwardsMotionDirection(),
var speed: Expression = NumberExpression(0.0),
@@ -79,14 +140,23 @@ class DynamicParticleMotion(
return direction.getDirectionVector(runtime, storm, emitterPos, particlePos).normalize().multiply(runtime.resolveDouble(speed))
}
override fun getAcceleration(runtime: MoLangRuntime, velocity: Vec3d): Vec3d {
return Vec3d(
override fun getVelocity(runtime: MoLangRuntime, particle: SnowstormParticle, velocity: Vec3d): Vec3d {
val acceleration = Vec3d(
runtime.resolveDouble(acceleration.first),
runtime.resolveDouble(acceleration.second),
runtime.resolveDouble(acceleration.third)
).subtract(velocity.multiply(runtime.resolveDouble(drag)))
)
.subtract(velocity.multiply(20 * runtime.resolveDouble(drag)))
.multiply(1 / 20.0).multiply(1 / 20.0) // blocks per second per second -> blocks per tick per tick
return Vec3d(
velocity.x + acceleration.x,
velocity.y + acceleration.y,
velocity.z + acceleration.z
)
}
override fun getParticleDirection(runtime: MoLangRuntime, storm: ParticleStorm, velocity: Vec3d, minSpeed: Float) = velocity.normalize()
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun readFromBuffer(buffer: PacketByteBuf) {
@@ -234,7 +304,8 @@ class StaticParticleMotion : ParticleMotion {
override val type = ParticleMotionType.STATIC
override fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3d, emitterPos: Vec3d) = Vec3d.ZERO
override fun getAcceleration(runtime: MoLangRuntime, velocity: Vec3d) = Vec3d.ZERO
override fun getVelocity(runtime: MoLangRuntime, particle: SnowstormParticle, velocity: Vec3d) = velocity
override fun getParticleDirection(runtime: MoLangRuntime, storm: ParticleStorm, velocity: Vec3d, minSpeed: Float) = velocity.normalize()
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun readFromBuffer(buffer: PacketByteBuf) {}
override fun writeToBuffer(buffer: PacketByteBuf) {}
@@ -32,6 +32,7 @@ interface ParticleRotation : CodecMapped {
) {
init {
registerSubtype(ParticleRotationType.DYNAMIC, DynamicParticleRotation::class.java, DynamicParticleRotation.CODEC)
registerSubtype(ParticleRotationType.PARAMETRIC, ParametricParticleRotation::class.java, ParametricParticleRotation.CODEC)
}
}
@@ -39,7 +40,34 @@ interface ParticleRotation : CodecMapped {
fun getInitialRotation(runtime: MoLangRuntime): Double
fun getInitialAngularVelocity(runtime: MoLangRuntime): Double
fun getAngularAcceleration(runtime: MoLangRuntime, angularVelocity: Double): Double
fun getAngularVelocity(runtime: MoLangRuntime, angle: Double, angularVelocity: Double): Double
}
class ParametricParticleRotation(var expression: Expression = NumberExpression(0.0)): ParticleRotation {
companion object {
val CODEC: Codec<ParametricParticleRotation> = RecordCodecBuilder.create { instance ->
instance.group(
PrimitiveCodec.STRING.fieldOf("type").forGetter { it.type.name },
EXPRESSION_CODEC.fieldOf("expression").forGetter { it.expression }
).apply(instance) { _, expression ->
ParametricParticleRotation(expression)
}
}
}
override val type = ParticleRotationType.PARAMETRIC
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun getInitialRotation(runtime: MoLangRuntime) = runtime.resolveDouble(expression)
override fun getInitialAngularVelocity(runtime: MoLangRuntime) = 0.0
override fun getAngularVelocity(runtime: MoLangRuntime, angle: Double, angularVelocity: Double) = runtime.resolveDouble(expression) - angle
override fun readFromBuffer(buffer: PacketByteBuf) {
expression = MoLang.createParser(buffer.readString()).parseExpression()
}
override fun writeToBuffer(buffer: PacketByteBuf) {
buffer.writeString(expression.getString())
}
}
class DynamicParticleRotation(
@@ -66,15 +94,15 @@ class DynamicParticleRotation(
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun getInitialRotation(runtime: MoLangRuntime) = runtime.resolveDouble(startRotation)
override fun getInitialAngularVelocity(runtime: MoLangRuntime) = runtime.resolveDouble(speed) / 20
override fun getAngularAcceleration(runtime: MoLangRuntime, angularVelocity: Double): Double {
override fun getAngularVelocity(runtime: MoLangRuntime, angle: Double, angularVelocity: Double): Double {
val acceleration = runtime.resolveDouble(acceleration)
val nextVelocity = angularVelocity + acceleration
val nextVelocity = angularVelocity * 20 + acceleration
val drag = nextVelocity * runtime.resolveDouble(drag)
return if (abs(drag) > abs(nextVelocity)) {
return angularVelocity + (if (abs(drag) > abs(nextVelocity)) {
0.0
} else {
nextVelocity - drag - angularVelocity
}
nextVelocity - drag - angularVelocity * 20
})
}
override fun readFromBuffer(buffer: PacketByteBuf) {
@@ -10,12 +10,7 @@ package com.cobblemon.mod.common.api.spawning
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.entity.Despawner
import com.cobblemon.mod.common.api.spawning.condition.AreaSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.BasicSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.GroundedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.*
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
import com.cobblemon.mod.common.api.spawning.context.GroundedSpawningContext
import com.cobblemon.mod.common.api.spawning.context.LavafloorSpawningContext
@@ -91,6 +86,7 @@ object BestSpawner {
SpawningCondition.register(SubmergedSpawningCondition.NAME, SubmergedSpawningCondition::class.java)
SpawningCondition.register(GroundedSpawningCondition.NAME, GroundedSpawningCondition::class.java)
SpawningCondition.register(SurfaceSpawningCondition.NAME, SurfaceSpawningCondition::class.java)
SpawningCondition.register(SeafloorSpawningCondition.NAME, SeafloorSpawningCondition::class.java)
LOGGER.info("Loaded ${SpawningCondition.conditionTypes.size} spawning condition types.")
SpawningContextCalculator.register(GroundedSpawningContextCalculator)
@@ -100,7 +96,7 @@ object BestSpawner {
SpawningContextCalculator.register(SurfaceSpawningContextCalculator)
SpawningContext.register(name = "grounded", clazz = GroundedSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
SpawningContext.register(name = "seafloor", clazz = SeafloorSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
SpawningContext.register(name = "seafloor", clazz = SeafloorSpawningContext::class.java, defaultCondition = SeafloorSpawningCondition.NAME)
SpawningContext.register(name = "lavafloor", clazz = LavafloorSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
SpawningContext.register(name = "submerged", clazz = SubmergedSpawningContext::class.java, defaultCondition = SubmergedSpawningCondition.NAME)
SpawningContext.register(name = "surface", clazz = SurfaceSpawningContext::class.java, defaultCondition = SurfaceSpawningCondition.NAME)
@@ -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.spawning.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.context.SeafloorSpawningContext
import com.cobblemon.mod.common.util.Merger
import net.minecraft.block.Block
/**
* Base type for a spawning condition that applies to some kind of [SeafloorSpawningContext]. This
* can be extended for subclasses of [SeafloorSpawningContext].
*
* Borrowed the Grounded context code since it was unintentionally spawning Pokémon on the sea floor
* already. Seems to be working fine /shrug
*
* @author FrankTheFarmer
* @since May 22nd, 2024
*/
abstract class SeafloorTypeSpawningCondition<T : SeafloorSpawningContext> : AreaTypeSpawningCondition<T>() {
var neededBaseBlocks: MutableList<RegistryLikeCondition<Block>>? = null
override fun fits(ctx: T): Boolean {
return if (!super.fits(ctx)) {
false
} else if (minHeight != null && ctx.height < minHeight!!) {
return false
} else if (maxHeight != null && ctx.height > maxHeight!!) {
return false
} else !(neededBaseBlocks != null && neededBaseBlocks!!.none { it.fits(ctx.baseBlock.block, ctx.blockRegistry) })
}
override fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
super.copyFrom(other, merger)
if (other is SeafloorTypeSpawningCondition) {
neededBaseBlocks = merger.merge(neededBaseBlocks, other.neededBaseBlocks)?.toMutableList()
}
}
}
/**
* A spawning condition for a [SeafloorSpawningContext].
*
* @author FrankTheFarmer
* @since May 22nd, 2024
*/
open class SeafloorSpawningCondition : SeafloorTypeSpawningCondition<SeafloorSpawningContext>() {
override fun contextClass() = SeafloorSpawningContext::class.java
companion object {
const val NAME = "seafloor"
}
}
@@ -49,7 +49,7 @@ open class AreaSpawningContext(
* This is not considering the provided state as what the entity would be on top of,
* but rather the space its hitbox would fill.
*/
open fun isSafeSpace(world: ServerWorld, pos: BlockPos, state: BlockState): Boolean = !state.isFullCube(world, pos)
open fun isSafeSpace(world: ServerWorld, pos: BlockPos, state: BlockState): Boolean = !state.isSolid
override fun postFilter(detail: SpawnDetail): Boolean {
if (!super.postFilter(detail)) {
@@ -34,7 +34,8 @@ class BestSpawnerConfig {
val contextWeights = mutableMapOf(
"grounded" to 1F,
"submerged" to 0.99F,
"surface" to 0.01F
"surface" to 0.01F,
"seafloor" to 1F
)
val buckets = mutableListOf(
SpawnBucket("common", 93.8F),
@@ -0,0 +1,51 @@
/*
* 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.battles.dispatch
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import net.minecraft.util.Identifier
import java.util.concurrent.CompletableFuture
/**
* An [InterpreterInstruction] that triggers an [ActionEffectTimeline]
* I think it might be better to have this as an abstract class so we can default the fields
*
* @author Apion
* @since May 19, 2024
*/
interface ActionEffectInstruction : InterpreterInstruction {
var future: CompletableFuture<*>
var holds: MutableSet<String>
//To expose via molang, so action effects can do different stuff in different instructions
//e.g. "x is confused" vs "x hit itself in confusion"
val id: Identifier
override fun invoke(battle: PokemonBattle) {
preActionEffect(battle)
val runtime = MoLangRuntime()
battle.addQueryFunctions(runtime.environment.getQueryStruct())
runtime.environment.getQueryStruct().addStandardFunctions()
addMolangQueries(runtime)
runActionEffect(battle, runtime)
postActionEffect(battle)
}
fun preActionEffect(battle: PokemonBattle)
fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime)
fun postActionEffect(battle: PokemonBattle)
fun addMolangQueries(runtime: MoLangRuntime) {
runtime.environment.getQueryStruct()
.addFunction("instruction_id") { StringValue(id.toString()) }
}
}
@@ -8,16 +8,28 @@
package com.cobblemon.mod.common.battles.interpreter.instructions
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
import com.cobblemon.mod.common.battles.dispatch.CauserInstruction
import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import java.util.concurrent.CompletableFuture
/**
* Format: |-activate|POKEMON|EFFECT
@@ -28,19 +40,50 @@ import net.minecraft.text.Text
* @author Hunter
* @since September 25th, 2022
*/
class ActivateInstruction(val instructionSet: InstructionSet, val message: BattleMessage) : InterpreterInstruction, CauserInstruction {
override fun invoke(battle: PokemonBattle) {
class ActivateInstruction(val instructionSet: InstructionSet, val message: BattleMessage) : ActionEffectInstruction, CauserInstruction {
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
override var holds = mutableSetOf<String>()
override val id = cobblemonResource("activate")
override fun preActionEffect(battle: PokemonBattle) {
val pokemon = message.battlePokemon(0, battle) ?: return
val effect = message.effectAt(1) ?: return
val extraEffect = message.effectAt(2)?.typelessData ?: Text.literal("UNKNOWN")
ShowdownInterpreter.broadcastOptionalAbility(battle, effect, pokemon)
battle.dispatch{
val pokemonName = pokemon.getName()
val sourceName = message.battlePokemonFromOptional(battle)?.getName() ?: Text.literal("UNKNOWN")
ShowdownInterpreter.lastCauser[battle.battleId] = message
battle.minorBattleActions[pokemon.uuid] = message
GO
}
}
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
battle.dispatch {
val effect = message.effectAt(1) ?: return@dispatch GO
val status = Statuses.getStatus(effect.id)
val actionEffect = status?.getActionEffect() ?: return@dispatch GO
val providers = mutableListOf<Any>(battle)
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
future.thenApply { holds.clear() }
return@dispatch GO
}
}
override fun postActionEffect(battle: PokemonBattle) {
battle.dispatch {
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
val extraEffect = message.effectAt(2)?.typelessData ?: Text.literal("UNKNOWN")
val effect = message.effectAt(1) ?: return@dispatch GO
val pokemonName = pokemon.getName()
val sourceName = message.battlePokemonFromOptional(battle)?.getName() ?: Text.literal("UNKNOWN")
val lang = when (effect.id) {
// Includes a 3rd argument being the magnitude level as a number
"magnitude" -> battleLang("activate.magnitude", message.argumentAt(2)?.toIntOrNull() ?: 1)
@@ -59,7 +102,8 @@ class ActivateInstruction(val instructionSet: InstructionSet, val message: Battl
else -> battleLang("activate.${effect.id}", pokemonName, sourceName, extraEffect)
}
battle.broadcastChatMessage(lang)
WaitDispatch(1F)
//We check holds here so the chat msg + particle effect happen more concurrently, instead of sequentially
UntilDispatch { "effects" !in holds}
}
}
}
@@ -8,14 +8,25 @@
package com.cobblemon.mod.common.battles.interpreter.instructions
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.util.Identifier
import java.util.concurrent.CompletableFuture
/**
* Format: |-boost|POKEMON|STAT|AMOUNT or |-unboost|POKEMON|STAT|AMOUNT
@@ -25,9 +36,33 @@ import com.cobblemon.mod.common.util.battleLang
* @author Hiroku
* @since August 20th, 2022
*/
class BoostInstruction(val instructionSet: InstructionSet, val message: BattleMessage, val remainingLines: Iterator<BattleMessage>, val isBoost: Boolean = true): InterpreterInstruction {
class BoostInstruction(val instructionSet: InstructionSet, val message: BattleMessage, val remainingLines: Iterator<BattleMessage>, val isBoost: Boolean = true): ActionEffectInstruction {
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
override var holds = mutableSetOf<String>()
override val id = cobblemonResource("boost")
override fun preActionEffect(battle: PokemonBattle) {
override fun invoke(battle: PokemonBattle) {
}
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
battle.dispatch {
val actionEffect = if (isBoost) BOOST_EFFECT else UNBOOST_EFFECT
val providers = mutableListOf<Any>(battle)
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
future.thenApply { holds.clear() }
return@dispatch GO
}
}
override fun postActionEffect(battle: PokemonBattle) {
val pokemon = message.battlePokemon(0, battle) ?: return
val statKey = message.argumentAt(1) ?: return
val stages = message.argumentAt(2)?.toInt() ?: return
@@ -35,7 +70,7 @@ class BoostInstruction(val instructionSet: InstructionSet, val message: BattleMe
val severity = Stats.getSeverity(stages)
val rootKey = if (isBoost) "boost" else "unboost"
battle.dispatchWaiting {
battle.dispatch {
val lang = when {
message.hasOptionalArgument("zeffect") -> battleLang("$rootKey.$severity.zeffect", pokemon.getName(), stat)
else -> battleLang("$rootKey.$severity", pokemon.getName(), stat)
@@ -47,6 +82,13 @@ class BoostInstruction(val instructionSet: InstructionSet, val message: BattleMe
// TODO: replace with context that tracks detailed information such as # of stages
repeat(stages) { pokemon.contextManager.add(context) }
battle.minorBattleActions[pokemon.uuid] = message
return@dispatch UntilDispatch { "effects" !in holds }
}
}
companion object {
val BOOST_EFFECT = ActionEffects.actionEffects[cobblemonResource("boost")]!!
val UNBOOST_EFFECT = ActionEffects.actionEffects[cobblemonResource("unboost")]!!
}
}
@@ -8,14 +8,27 @@
package com.cobblemon.mod.common.battles.interpreter.instructions
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.TargetsProvider
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.util.Identifier
import java.util.concurrent.CompletableFuture
/**
* Formats: |cant|POKEMON|REASON and |cant|POKEMON|REASON|MOVE
@@ -24,12 +37,40 @@ import com.cobblemon.mod.common.util.lang
* @author Deltric
* @since January 22nd, 2022
*/
class CantInstruction(val message: BattleMessage): InterpreterInstruction {
class CantInstruction(val message: BattleMessage): ActionEffectInstruction {
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
override var holds = mutableSetOf<String>()
override val id = cobblemonResource("cant")
override fun preActionEffect(battle: PokemonBattle) {
override fun invoke(battle: PokemonBattle) {
battle.dispatchWaiting {
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
val effectID = message.effectAt(1)?.id ?: return@dispatchWaiting
}
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
battle.dispatch {
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
val effectID = message.effectAt(1)?.id ?: return@dispatch GO
val name = pokemon.getName()
val status = Statuses.getStatus(effectID)
val actionEffect = status?.getActionEffect() ?: return@dispatch GO
val providers = mutableListOf<Any>(battle)
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
future.thenApply { holds.clear() }
GO
}
}
override fun postActionEffect(battle: PokemonBattle) {
battle.dispatch {
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
val effectID = message.effectAt(1)?.id ?: return@dispatch GO
val name = pokemon.getName()
// Move may be null as it's not always given
val moveName = message.moveAt(2)?.displayName ?: run { "(Unrecognized: ${message.argumentAt(2)})".text() }
@@ -38,7 +79,7 @@ class CantInstruction(val message: BattleMessage): InterpreterInstruction {
// TODO: in the games they use a generic image because there is a popup of the ability and the sprite of the mon, it may be good to have a similar system here
"armortail", "damp", "dazzling", "queenlymajesty" -> battleLang("cant.generic", name, moveName)
"par", "slp", "frz" -> {
val status = Statuses.getStatus(effectID)?.name?.path ?: return@dispatchWaiting
val status = Statuses.getStatus(effectID)?.name?.path ?: return@dispatch GO
lang("status.$status.is", name)
}
else -> battleLang("cant.$effectID", name, moveName)
@@ -46,6 +87,10 @@ class CantInstruction(val message: BattleMessage): InterpreterInstruction {
battle.broadcastChatMessage(lang.red())
battle.minorBattleActions[pokemon.uuid] = message
UntilDispatch { "effects" !in holds }
}
}
}
@@ -8,15 +8,21 @@
package com.cobblemon.mod.common.battles.interpreter.instructions
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.ShowdownInterpreter.lastCauser
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
@@ -25,11 +31,13 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePac
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.pokemon.evolution.progress.DamageTakenEvolutionProgress
import com.cobblemon.mod.common.pokemon.evolution.progress.RecoilEvolutionProgress
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import java.util.concurrent.CompletableFuture
import kotlin.math.roundToInt
import net.minecraft.text.Text
import net.minecraft.util.Identifier
/**
* Format: |-damage|POKEMON|HP STATUS
@@ -43,45 +51,87 @@ class DamageInstruction(
val actor: BattleActor,
val publicMessage: BattleMessage,
val privateMessage: BattleMessage
) : InterpreterInstruction {
var future = CompletableFuture.completedFuture(Unit)
) : ActionEffectInstruction {
val expectedTarget = publicMessage.battlePokemon(0, actor.battle)
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
override var holds = mutableSetOf<String>()
override val id = cobblemonResource("damage")
override fun invoke(battle: PokemonBattle) {
override fun preActionEffect(battle: PokemonBattle) {
val battlePokemon = publicMessage.battlePokemon(0, actor.battle) ?: return
val recoiling = privateMessage.optionalArgument("from")?.equals("recoil", true) == true
val lastCauser = instructionSet.getMostRecentCauser(comparedTo = this)
if (recoiling) {
battlePokemon.effectedPokemon.let { pokemon ->
if (RecoilEvolutionProgress.supports(pokemon)) {
val healthStr = privateMessage.argumentAt(1) ?: throw UnsupportedOperationException("Cant get recoil string")
val newHealth = "([0-9]+).*".toRegex().find(healthStr)?.groups?.get(1)?.value?.toIntOrNull() ?: throw UnsupportedOperationException("Cant get recoil string")
val difference = pokemon.currentHealth - newHealth
if (difference > 0) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is RecoilEvolutionProgress }) { RecoilEvolutionProgress() }
progress.updateProgress(RecoilEvolutionProgress.Progress(progress.currentProgress().recoil + difference))
}
doRecoilEvoChecks(battlePokemon)
if (lastCauser is MoveInstruction) {
battle.dispatch {
//Is the recoil hold currently used anywhere?
UntilDispatch { "recoil" !in lastCauser.holds }
}
}
}
val newHealth = privateMessage.argumentAt(1)?.split(" ")?.get(0) ?: return
val effect = privateMessage.effect()
val source = privateMessage.battlePokemonFromOptional(battle)
var causedFaint = newHealth == "0"
source?.let { ShowdownInterpreter.broadcastOptionalAbility(battle, effect, it) }
}
battle.dispatch {
if (lastCauser is MoveInstruction && recoiling) {
UntilDispatch { "recoil" !in lastCauser.holds }
} else {
GO
private fun doRecoilEvoChecks(battlePokemon: BattlePokemon) {
battlePokemon.effectedPokemon.let { pokemon ->
if (RecoilEvolutionProgress.supports(pokemon)) {
val healthStr = privateMessage.argumentAt(1) ?: throw UnsupportedOperationException(
"Cant get recoil string"
)
val newHealth =
"([0-9]+).*".toRegex().find(healthStr)?.groups?.get(1)?.value?.toIntOrNull()
?: throw UnsupportedOperationException("Cant get recoil string")
val difference = pokemon.currentHealth - newHealth
if (difference > 0) {
val progress = pokemon.evolutionProxy.current()
.progressFirstOrCreate({ it is RecoilEvolutionProgress }) { RecoilEvolutionProgress() }
progress.updateProgress(RecoilEvolutionProgress.Progress(progress.currentProgress().recoil + difference))
}
}
}
}
source?.let { ShowdownInterpreter.broadcastOptionalAbility(battle, effect, it) }
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
val effect = privateMessage.effect()
val battlePokemon = publicMessage.battlePokemon(0, actor.battle) ?: return
var status = effect?.id?.let { Statuses.getStatus(it) }
battle.dispatch {
val pokemon = privateMessage.battlePokemon(0, battle) ?: return@dispatch GO
//Showdown doesnt tell us on damage if its poison or toxic so we gotta consult the entity
if (status is PoisonStatus) {
status = pokemon.effectedPokemon.status?.status ?: status
}
val actionEffect = status?.getActionEffect() ?: return@dispatch GO
val providers = mutableListOf<Any>(battle)
battlePokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
future.thenApply { holds.clear() }
return@dispatch GO
}
}
override fun postActionEffect(battle: PokemonBattle) {
val newHealth = privateMessage.argumentAt(1)?.split(" ")?.get(0) ?: return
val battlePokemon = publicMessage.battlePokemon(0, actor.battle) ?: return
var causedFaint = newHealth == "0"
val effect = privateMessage.effect()
val source = privateMessage.battlePokemonFromOptional(battle)
val lastCauser = instructionSet.getMostRecentCauser(comparedTo = this)
battle.dispatch {
val pokemonName = battlePokemon.getName()
val pokemonEntity = battlePokemon.entity
//Play recoil animation if the pokemon recoiling isnt dead
if (!causedFaint && pokemonEntity != null) {
val pkt = PlayPoseableAnimationPacket(pokemonEntity.id, setOf("recoil"), emptySet())
pkt.sendToPlayersAround(
@@ -92,7 +142,7 @@ class DamageInstruction(
distance = 50.0
)
}
//Play hit particle
if (pokemonEntity != null) {
RunPosableMoLangPacket(pokemonEntity.id, setOf("q.particle('cobblemon:hit', 'target')")).sendToPlayersAround(
x = pokemonEntity.x,
@@ -157,8 +207,9 @@ class DamageInstruction(
} else if (causedFaint) {
GO
} else {
WaitDispatch(1F)
UntilDispatch {"effects" !in holds}
}
}
}
}
@@ -10,10 +10,10 @@ package com.cobblemon.mod.common.battles.interpreter.instructions
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.interpreter.Effect
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.moves.Moves
@@ -29,6 +29,7 @@ import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.pokemon.evolution.progress.UseMoveEvolutionProgress
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import java.util.concurrent.CompletableFuture
/**
@@ -121,6 +122,7 @@ class MoveInstruction(
}
runtime.environment.getQueryStruct().addFunction("move") { move.struct }
runtime.environment.getQueryStruct().addFunction("instruction_id") { StringValue(cobblemonResource("move").toString()) }
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.api.mulch.Mulchable
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import dev.lambdaurora.lambdynlights.util.SodiumDynamicLightHandler.pos
import net.minecraft.block.Block
import net.minecraft.block.BlockRenderType
import net.minecraft.block.BlockState
@@ -87,22 +88,31 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
init {
defaultState = this.stateManager.defaultState
.with(WAS_GENERATED, false)
.with(AGE, 0)
.with(MULCH, MulchVariant.NONE)
.with(AGE, 0)
.with(IS_ROOTED, false)
}
override fun grow(world: ServerWorld, random: Random, pos: BlockPos, state: BlockState) {
growHelper(world, random, pos, state, true)
}
//grow, but cooler
fun growHelper(world: ServerWorld, random: Random, pos: BlockPos, state: BlockState, boneMealed: Boolean = false) {
val berry = berry() ?: return
if (boneMealed && random.nextFloat() > berry.boneMealChance) return
val curAge = state.get(AGE)
val newAge = curAge + 1
if (newAge > FRUIT_AGE) return
val newState = state.with(AGE, newAge)
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
if (curAge == MATURE_AGE) {
treeEntity.generateGrowthPoints(world, state, pos, null)
determineMutation(world, random, pos, state)
treeEntity.generateGrowthPoints(world, newState, pos, null)
determineMutation(world, random, pos, newState)
}
world.setBlockState(pos, newState, Block.NOTIFY_LISTENERS)
convertMulchToEntity(world, newState, pos)
treeEntity.goToNextStageTimer(FRUIT_AGE - curAge)
treeEntity.markDirty()
}
@@ -122,7 +132,7 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
CobblemonEvents.BERRY_MUTATION_OFFER.post(BerryMutationOfferEvent(berry, world, state, pos, mutations)) { berryMutationOffer ->
if (berryMutationOffer.mutations.isNotEmpty()) {
var mutateChance = 125
if (getMulch(state) == MulchVariant.SURPRISE) {
if (getMulch(treeEntity) == MulchVariant.SURPRISE) {
mutateChance *= 4
treeEntity.decrementMulchDuration(world, pos, state)
}
@@ -143,7 +153,10 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
state: BlockState,
variant: MulchVariant
): Boolean {
return getMulch(state) == MulchVariant.NONE && state.get(AGE) < FLOWER_AGE && world.getBlockState(pos.down()).isOf(Blocks.FARMLAND)
val underBlockState = world.getBlockState(pos.down())
val validSoil = state.get(IS_ROOTED) || underBlockState.isIn(CobblemonBlockTags.BERRY_SOIL)
val treeEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return false
return getMulch(treeEntity) == MulchVariant.NONE && state.get(AGE) < FLOWER_AGE && validSoil
}
override fun applyMulch(
@@ -154,18 +167,15 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
variant: MulchVariant
) {
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
setMulch(world, pos, state, variant)
treeEntity.mulchDuration = variant.duration
treeEntity.setMulch(variant, world, state, pos)
world.playSound(null, pos, CobblemonSounds.MULCH_PLACE, SoundCategory.BLOCKS, 0.6F, 1F)
treeEntity.refreshTimers(pos)
}
@Deprecated("Deprecated in Java")
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
if (player.getStackInHand(hand).item is ShovelItem && getMulch(state) != MulchVariant.NONE) {
setMulch(world, pos, state, MulchVariant.NONE)
treeEntity.markDirty()
if (player.getStackInHand(hand).item is ShovelItem && getMulch(treeEntity) != MulchVariant.NONE) {
treeEntity.setMulch(MulchVariant.NONE, world, state, pos)
world.playSound(null, pos, CobblemonSounds.MULCH_REMOVE, SoundCategory.BLOCKS, 0.6F, 1F)
this.spawnBreakParticles(world, player, pos, state.with(AGE, 0))
return ActionResult.SUCCESS
@@ -189,7 +199,7 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
val below = world.getBlockState(pos.down())
return (state.get(WAS_GENERATED) && below.isIn(CobblemonBlockTags.BERRY_WILD_SOIL))
|| below.isIn(CobblemonBlockTags.BERRY_SOIL)
|| below.block is FarmlandBlock
|| state.get(IS_ROOTED)
}
@Deprecated("Deprecated in Java")
@@ -208,6 +218,7 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
builder.add(AGE)
builder.add(WAS_GENERATED)
builder.add(MULCH)
builder.add(IS_ROOTED)
}
override fun getPickStack(world: BlockView?, pos: BlockPos?, state: BlockState?): ItemStack {
@@ -239,6 +250,7 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
val AGE: IntProperty = IntProperty.of("age", 0, FRUIT_AGE)
val MULCH: EnumProperty<MulchVariant> = EnumProperty.of("mulch", MulchVariant::class.java)
val WAS_GENERATED: BooleanProperty = BooleanProperty.of("generated")
val IS_ROOTED: BooleanProperty = BooleanProperty.of("rooted")
// val PLANTED_SHAPE = VoxelShapes.union(
// VoxelShapes.cuboid(0.3125, -0.0625, 0.3125, 0.6875, 0.0, 0.6875),
// VoxelShapes.cuboid(0.375, 0.0, 0.375, 0.625, 0.0625, 0.625)
@@ -301,15 +313,14 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
val TALL_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
fun getMulch(state: BlockState): MulchVariant {
if (!state.contains(MULCH)) {
return MulchVariant.NONE
}
return state.get(MULCH)
}
fun getMulch(entity: BerryBlockEntity) = entity.mulchVariant
fun setMulch(world: World, pos: BlockPos, state: BlockState, mulch: MulchVariant) {
world.setBlockState(pos, state.with(MULCH, mulch))
fun convertMulchToEntity(world: ServerWorld, state: BlockState, pos: BlockPos) {
val entity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return
if (state.get(MULCH) != MulchVariant.NONE && state.get(MULCH) != entity.mulchVariant) {
entity.mulchVariant = state.get(MULCH)
world.setBlockState(pos, state.with(MULCH, MulchVariant.NONE))
}
}
}
}
@@ -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.block
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.cobblemon.mod.common.item.berry.BerryItem
import net.minecraft.block.AbstractBlock
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
/**
* @author Apion
* @since February 27, 2024
*/
class BindingSoilBlock(settings: Settings) : Block(settings) {
@Deprecated("Deprecated in Java")
override fun onUse(
state: BlockState,
world: World,
pos: BlockPos,
player: PlayerEntity,
hand: Hand,
hit: BlockHitResult
): ActionResult {
if (!world.isClient) {
val itemStack = player.getStackInHand(hand)
val item = itemStack.item
if (item is BerryItem) {
world.setBlockState(pos, item.block.defaultState.with(BerryBlock.IS_ROOTED, true))
if (!player.isCreative) {
itemStack.decrement(1)
}
}
}
return ActionResult.SUCCESS
}
}
@@ -19,6 +19,7 @@ import net.minecraft.entity.LivingEntity
import net.minecraft.entity.ai.pathing.NavigationType
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.inventory.SidedInventory
import net.minecraft.inventory.SimpleInventory
import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.server.world.ServerWorld
@@ -209,9 +210,8 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
): SidedInventory {
val tankEntity =
(if (state.get(PART) == TankPart.TOP) world.getBlockEntity(pos.down())
else world.getBlockEntity(pos)) as RestorationTankBlockEntity
return tankEntity.inv
else world.getBlockEntity(pos))
return if(tankEntity != null && tankEntity is RestorationTankBlockEntity) tankEntity.inv else DummyInventory()
}
enum class TankPart(private val label: String) : StringIdentifiable {
@@ -229,5 +229,18 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
val PART = EnumProperty.of("part", TankPart::class.java)
val TRIGGERED = Properties.TRIGGERED
val ON = BooleanProperty.of("on")
class DummyInventory : SimpleInventory(0), SidedInventory {
override fun getAvailableSlots(side: Direction): IntArray {
return IntArray(0)
}
override fun canInsert(slot: Int, stack: ItemStack, dir: Direction?): Boolean {
return false
}
override fun canExtract(slot: Int, stack: ItemStack, dir: Direction): Boolean {
return false
}
}
}
}
@@ -131,7 +131,6 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
if (isFake() && (player is ServerPlayerEntity)) {
spawnPokemon(world, pos, state, player)
}
world.setBlockState(pos, if (state.fluidState.isOf(Fluids.WATER)) Blocks.WATER.defaultState else Blocks.AIR.defaultState)
val bEntity = world.getBlockEntity(pos) as? GildedChestBlockEntity
bEntity?.markRemoved()
} else super.onBreak(world, pos, state, player)
@@ -187,10 +186,6 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
if (!player.world.isClient) {
PiglinBrain.onGuardedBlockInteracted(player, true)
}
val state = entity.poseableState
state.currentModel?.let {
it.moveToPose(null, state, it.getPose("OPEN")!!)
}
return ActionResult.SUCCESS
}
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.events.berry.BerryHarvestEvent
import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.BerryBlock.Companion.getMulch
import com.cobblemon.mod.common.block.BerryBlock.Companion.setMulch
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.entity.BlockEntity
@@ -43,8 +42,8 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
lateinit var berryIdentifier: Identifier
private val ticksPerMinute = 1200
var renderState: RenderState? = null
//The time left for the tree to grow to stage 5
var growthTimer: Int = 72000
//The time left for the tree until its either age 3 or age 5 (check block state, if we are at age 0<x<3 unti 3 otherwise until 5)
var growthTimer: Int = 0
set(value) {
if (value < 0) {
throw IllegalArgumentException("You cannot set the growth time to less than zero")
@@ -55,7 +54,7 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
field = value
}
//The time left for the tree to grow to the next stage
var stageTimer: Int = growthTimer / BerryBlock.MATURE_AGE
var stageTimer: Int = 0
set(value) {
if (field != value) {
this.markDirty()
@@ -63,6 +62,7 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
field = value
}
private val growthPoints = arrayListOf<Identifier>()
var mulchVariant = MulchVariant.NONE
/**
* The idea behind the growth point sequence is it's a 16-long string of
@@ -85,20 +85,6 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
markDirty()
}
fun decrementMulchDuration(world: World, pos: BlockPos, state: BlockState) {
val currentMulch = getMulch(state)
if (currentMulch == MulchVariant.NONE || currentMulch.duration == -1) {
return
}
val newDuration = mulchDuration - 1
mulchDuration = if (newDuration <= 0) {
setMulch(world, pos, state, MulchVariant.NONE)
0
} else {
newDuration
}
}
constructor(pos: BlockPos, state: BlockState, berryIdentifier: Identifier): this(pos, state) {
this.berryIdentifier = berryIdentifier
resetGrowTimers(pos, state)
@@ -107,6 +93,31 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
}
}
fun decrementMulchDuration(world: World, pos: BlockPos, state: BlockState) {
if (mulchVariant == MulchVariant.NONE || mulchVariant.duration == -1) {
return
}
val newDuration = mulchDuration - 1
mulchDuration = if (newDuration <= 0) {
mulchVariant = MulchVariant.NONE
this.markDirty()
world.updateListeners(pos, state, state, Block.NOTIFY_LISTENERS)
0
} else {
newDuration
}
}
fun setMulch(mulch: MulchVariant, world: World, state: BlockState, pos: BlockPos) {
this.mulchVariant = mulch
this.mulchDuration = mulch.duration
refreshTimers(pos)
world.updateListeners(pos, state, state, Block.NOTIFY_LISTENERS)
this.markDirty()
}
//This function basically runs twice when the berry tree is growing, once when it starts, once when it hits age 3
//Why dont we just go 0-5? Design thinks its annoying to balance the numbers using 0-5 and 3-5
fun resetGrowTimers(pos: BlockPos, state: BlockState) {
val curAge = state.get(BerryBlock.AGE)
if (curAge == 5) {
@@ -119,12 +130,15 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
val upperGrowthLimit = (if (curAge == 0) berry.growthTime.last else berry.refreshRate.last) * multiplier / 10
val growthRange = lowerGrowthLimit..upperGrowthLimit
this.growthTimer = this.applyMulchModifier(pos, growthRange.random() * ticksPerMinute)
this.goToNextStageTimer(BerryBlock.FRUIT_AGE - curAge)
this.growthTimer = this.applyMulchModifier(pos, growthRange.random() * ticksPerMinute, true)
val stagesLeft = if (curAge < 3) BerryBlock.MATURE_AGE - curAge else BerryBlock.FRUIT_AGE - curAge
this.goToNextStageTimer(stagesLeft)
}
fun goToNextStageTimer(stagesLeft: Int) {
val avgStageTime = growthTimer / stagesLeft
//A number between 80% and 100% the average stage time
//So stages have some variance
stageTimer = this.world?.random?.nextBetween((avgStageTime * 8) / 10, avgStageTime) ?:
(((Math.random() * 0.2) + 0.8) * avgStageTime).toInt()
growthTimer -= stageTimer
@@ -137,7 +151,7 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
* @param timer Timer to be modified
* @return The modified timer
*/
private fun applyMulchModifier(pos: BlockPos, timer: Int): Int {
private fun applyMulchModifier(pos: BlockPos, timer: Int, decrementMulch: Boolean = false): Int {
val state = world?.getBlockState(pos) ?: return timer
val curAge = state.get(BerryBlock.AGE)
@@ -145,11 +159,15 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
return timer
}
if (getMulch(state) != MulchVariant.GROWTH) {
if (mulchVariant != MulchVariant.GROWTH) {
return timer
}
return (timer * 0.66).toInt()
if (decrementMulch) {
world?.let {
decrementMulchDuration(it, pos, state)
}
}
return (timer * MulchVariant.GROWTH_TIME_MULTIPLIER).toInt()
}
/**
@@ -157,8 +175,8 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
* @param pos The position of the block
*/
fun refreshTimers(pos: BlockPos) {
this.growthTimer = this.applyMulchModifier(pos, growthTimer)
this.stageTimer = this.applyMulchModifier(pos, stageTimer)
this.growthTimer = this.applyMulchModifier(pos, growthTimer, false)
this.stageTimer = this.applyMulchModifier(pos, stageTimer, false)
}
/**
@@ -248,6 +266,9 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
if (nbt.contains(GROWTH_POINTS_SEQUENCE)) {
growthPointSequence = nbt.getString(GROWTH_POINTS_SEQUENCE)
}
if (nbt.contains(MULCH_VARIANT)) {
mulchVariant = MulchVariant.valueOf(nbt.getString(MULCH_VARIANT))
}
this.renderState?.needsRebuild = true
}
@@ -260,6 +281,7 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
nbt.putString(BERRY, berryIdentifier.toString())
nbt.putInt(MULCH_DURATION, mulchDuration)
nbt.putString(GROWTH_POINTS_SEQUENCE, growthPointSequence)
nbt.putString(MULCH_VARIANT, mulchVariant.toString())
}
override fun markDirty() {
@@ -323,11 +345,12 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
companion object {
internal val TICKER = BlockEntityTicker<BerryBlockEntity> { world, pos, state, blockEntity ->
if (world.isClient) return@BlockEntityTicker
if (state.get(BerryBlock.IS_ROOTED)) return@BlockEntityTicker
if (blockEntity.stageTimer >= 0) {
blockEntity.stageTimer--
}
if (blockEntity.stageTimer == 0) {
(state.block as BerryBlock).grow(world as ServerWorld, world.random, pos, state)
(state.block as BerryBlock).growHelper(world as ServerWorld, world.random, pos, state)
}
}
//private const val LIFE_CYCLES = "life_cycles"
@@ -337,6 +360,7 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
private const val STAGE_TIMER = "StageTimer"
private const val BERRY = "Berry"
private const val MULCH_DURATION = "MulchDuration"
private const val MULCH_VARIANT = "MulchVariant"
}
}
@@ -62,14 +62,6 @@ class FossilAnalyzerBlockEntity(
}
override fun removeStack(slot: Int): ItemStack {
// if(analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
// val fossilMultiblockStructure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
// if (fossilMultiblockStructure.fossilInventory.size > slot) {
// val itemStack = fossilMultiblockStructure.fossilInventory.removeAt(slot)
// markDirty()
// return itemStack
// }
// }
return ItemStack.EMPTY
}
@@ -111,7 +103,6 @@ class FossilAnalyzerBlockEntity(
}
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
// return analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure && dir == Direction.DOWN
return false
}
}
@@ -38,18 +38,14 @@ open class FossilMultiblockEntity(
}
}
get() {
return if (field != null) {
field
} else if (masterBlockPos != null && masterBlockPos != pos) {
if(masterBlockPos != null && masterBlockPos != pos) {
val chunkPos = ChunkPos(masterBlockPos)
if (world?.chunkManager?.isChunkLoaded(chunkPos.x, chunkPos.z) == true) {
val entity: FossilMultiblockEntity? = world?.getBlockEntity(masterBlockPos) as FossilMultiblockEntity?
field = entity?.multiblockStructure
}
field
} else {
null
}
return field
}
override fun markRemoved() {
@@ -12,13 +12,11 @@ import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.api.fossil.NaturalMaterials
import com.cobblemon.mod.common.api.multiblock.builder.MultiblockStructureBuilder
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockStructure
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.inventory.Inventories
import net.minecraft.inventory.SidedInventory
import net.minecraft.inventory.SimpleInventory
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtList
@@ -55,7 +53,9 @@ class RestorationTankBlockEntity(
constructor(tankEntity: RestorationTankBlockEntity) : this(tankEntity, *Array(8) { ItemStack.EMPTY })
override fun getMaxCountPerStack(): Int {
return 1
}
override fun markDirty() {
super.markDirty()
@@ -64,7 +64,6 @@ class RestorationTankBlockEntity(
if(!itemStack.isEmpty) {
val struct = tankEntity.multiblockStructure as? FossilMultiblockStructure
val returnIdentifier = NaturalMaterials.getReturnItem(itemStack)
if(tankEntity.world != null) {
if(struct?.insertOrganicMaterial(itemStack, tankEntity.world!!) == true) {
removeStack(i)
@@ -72,16 +71,15 @@ class RestorationTankBlockEntity(
val returnItem = Registries.ITEM.get(returnIdentifier)
val returnStack = ItemStack(returnItem, itemStack.count)
var done = false
if (returnStack.maxCount > 1) { // Only search for a merge if merging could be possible
for(j in 0 until size()) {
for(j in 1 until size()) {
// Look for a inventory slot to merge with
val existingStack : ItemStack = getStack(j)
if (Registries.ITEM.getId(existingStack.item) == returnIdentifier && existingStack.count + returnStack.count < existingStack.maxCount) {
if (existingStack.isEmpty
|| (Registries.ITEM.getId(existingStack.item) == returnIdentifier && existingStack.count + returnStack.count < existingStack.maxCount)) {
setStack(j, ItemStack(returnItem, existingStack.count + returnStack.count))
done = true
}
}
}
if (!done) {
setStack(i, ItemStack(returnItem, itemStack.count))
}
@@ -104,7 +102,7 @@ class RestorationTankBlockEntity(
}
override fun getAvailableSlots(side: Direction?): IntArray {
return intArrayOf(0,1,2,3,4,5,6,7)
return if (side == Direction.DOWN) intArrayOf(0,1,2,3,4,5,6,7) else intArrayOf(0)
}
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
@@ -113,16 +111,16 @@ class RestorationTankBlockEntity(
val structure = tankEntity.multiblockStructure as FossilMultiblockStructure
val canUtilize = stack?.let { NaturalMaterials.isNaturalMaterial(it) } == true
&& structure.organicMaterialInside < FossilMultiblockStructure.MATERIAL_TO_START
&& structure.createdPokemon == null
&& !structure.hasCreatedPokemon
val returnItem = NaturalMaterials.getReturnItem(stack!!) ?: return canUtilize
val returnStack = ItemStack(Registries.ITEM.get(returnItem), stack.count)
if(canUtilize && super.canInsert(returnStack)) {
// See if there's room for the return
if(returnStack == ItemStack.EMPTY) {
if(returnStack == ItemStack.EMPTY && getStack(0) == ItemStack.EMPTY) {
return true
}
// See if there's room for the return item
var emptyCount = 0
for(i in 0 until size()) {
for(i in 1 until size()) {
val existingStack : ItemStack = getStack(i)
if(existingStack == ItemStack.EMPTY) {
emptyCount += 1
@@ -130,14 +128,8 @@ class RestorationTankBlockEntity(
// 2 empty slots, safe to insert
return true
}
} else if(returnStack.maxCount > 1) {
if(Registries.ITEM.getId(existingStack.item) == returnItem && existingStack.count + returnStack.count < existingStack.maxCount) {
// Return item can merge with an item already in the inventory
return true
}
}
}
return false
}
}
}
@@ -10,7 +10,8 @@ package com.cobblemon.mod.common.block.multiblock
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.FossilRevivedEvent
import com.cobblemon.mod.common.api.fossil.Fossil
import com.cobblemon.mod.common.api.fossil.Fossils
import com.cobblemon.mod.common.api.fossil.NaturalMaterials
@@ -72,7 +73,7 @@ class FossilMultiblockStructure (
var organicMaterialInside = 0
private set
var createdPokemon: Pokemon? = null
var hasCreatedPokemon: Boolean = false
private set
var timeRemaining = -1
private set
@@ -102,8 +103,7 @@ class FossilMultiblockStructure (
if (player !is ServerPlayerEntity) {
return ActionResult.SUCCESS
}
if (this.createdPokemon != null) {
if (this.hasCreatedPokemon) {
if (this.fossilOwnerUUID != null && player.uuid != this.fossilOwnerUUID) {
var ownerName : String = "UNKNOWN_USER" // TODO: lang agnostic fallback
server()?.userCache?.getByUuid(this.fossilOwnerUUID)?.orElse(null)?.name?.let {
@@ -114,25 +114,28 @@ class FossilMultiblockStructure (
}
val ballType = (stack.item as PokeBallItem).pokeBall
if (!player.isCreative) {
stack?.decrement(1)
}
this.createdPokemon!!.caughtBall = ballType
player.party().add(this.createdPokemon!!)
this.fossilState.growthState = "Taken"
player.playSound(CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_POKEMON, SoundCategory.BLOCKS, 1.0F, 1.0F)
CobblemonCriteria.RESURRECT_POKEMON.trigger(player, createdPokemon!!)
val pokemon = this.resultingFossil?.result?.create()
if(pokemon != null) {
pokemon.caughtBall = ballType
player.party().add(pokemon)
this.fossilState.growthState = "Taken"
player.playSound(CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_POKEMON, SoundCategory.BLOCKS, 1.0F, 1.0F)
CobblemonEvents.FOSSIL_REVIVED.post(FossilRevivedEvent(pokemon, player))
}
// Turn the monitor off
val monitorState = world.getBlockState(monitorPos)
if(!monitorState.equals(MonitorBlock.MonitorScreen.OFF)) {
if(monitorState.contains(MonitorBlock.SCREEN) && !monitorState.equals(MonitorBlock.MonitorScreen.OFF)) {
world.setBlockState(monitorPos, monitorState.with(MonitorBlock.SCREEN, MonitorBlock.MonitorScreen.OFF))
}
this.createdPokemon = null
this.hasCreatedPokemon = false
this.fossilOwnerUUID = null
this.protectionTime = -1
this.updateFossilType(world)
@@ -144,15 +147,14 @@ class FossilMultiblockStructure (
// Reclaim the last fossil from the machine if their hand is empty
if (player.getStackInHand(interactionHand).isEmpty) {
if(!this.isRunning() && this.createdPokemon == null) {
if(!this.isRunning() && !this.hasCreatedPokemon) {
if (fossilInventory.isEmpty()) {
return ActionResult.CONSUME
}
player.setStackInHand(interactionHand, fossilInventory.last())
// remove last fossil in the fossil machine stack when grabbed out of the machine
this.fossilInventory.removeAt(fossilInventory.size - 1)
if(!world.isClient) {
if(player is ServerPlayerEntity) {
player.setStackInHand(interactionHand, fossilInventory.last())
// remove last fossil in the fossil machine stack when grabbed out of the machine
this.fossilInventory.removeAt(fossilInventory.size - 1)
world.playSound(null, analyzerPos, CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_FOSSIL, SoundCategory.BLOCKS)
this.updateFossilType(world)
this.syncToClient(world)
@@ -164,19 +166,17 @@ class FossilMultiblockStructure (
// Check if the player is holding a fossil and if so insert it into the machine.
if (Fossils.isFossilIngredient(stack)) {
if(!this.isRunning() && this.createdPokemon == null) {
if (!this.isRunning() && !this.hasCreatedPokemon) {
if (fossilInventory.size > Cobblemon.config.maxInsertedFossilItems) {
return ActionResult.FAIL
}
val copyFossilStack = stack.copyWithCount(1)
if (!player.isCreative) {
stack?.decrement(1)
}
fossilOwnerUUID = player.uuid
fossilInventory.add(copyFossilStack)
if(!world.isClient) {
if (player is ServerPlayerEntity) {
val copyFossilStack = stack.copyWithCount(1)
if (!player.isCreative) {
stack?.decrement(1)
}
fossilOwnerUUID = player.uuid
fossilInventory.add(copyFossilStack)
this.updateFossilType(world)
world.playSound(null, analyzerPos, CobblemonSounds.FOSSIL_MACHINE_INSERT_FOSSIL, SoundCategory.BLOCKS)
this.syncToClient(world)
@@ -188,7 +188,11 @@ class FossilMultiblockStructure (
// Check if the player is holding a natural material and if so, feed it to the machine.
if (NaturalMaterials.isNaturalMaterial(stack)) {
if (!this.isRunning() && this.createdPokemon == null && this.organicMaterialInside < MATERIAL_TO_START && insertOrganicMaterial(ItemStack(stack.item, 1), world)) {
if (player is ServerPlayerEntity
&& !this.isRunning()
&& !this.hasCreatedPokemon
&& this.organicMaterialInside < MATERIAL_TO_START
&& insertOrganicMaterial(ItemStack(stack.item, 1), world)) {
this.lastInteraction = world.time
if (!player.isCreative) {
val returnItem = NaturalMaterials.getReturnItem(stack)
@@ -220,6 +224,7 @@ class FossilMultiblockStructure (
entity.setPosition(fixedPosition.toCenterPos().subtract(0.0, 0.5, 0.0))
// TODO: Find a correct way to set the new entity's Yaw rotation. (Face away from the machine)
if (world.spawnEntity(entity)) {
CobblemonEvents.FOSSIL_REVIVED.post(FossilRevivedEvent(pokemon, null))
return true
} else {
Cobblemon.LOGGER.warn("Couldn't spawn resurrected Pokémon for some reason")
@@ -271,7 +276,7 @@ class FossilMultiblockStructure (
return 0
}
if(monitorPos == pos) {
if(createdPokemon != null) {
if(hasCreatedPokemon) {
return 15
}
if(!isRunning()) {
@@ -287,13 +292,13 @@ class FossilMultiblockStructure (
override fun onTriggerEvent(state: BlockState?, world: ServerWorld?, pos: BlockPos?, random: Random?) {
// instantiate the pokemon as a new entity and spawn it at the location of the machine
if(this.protectionTime <= 0) {
val wildPokemon: Pokemon = this.createdPokemon ?: return
val wildPokemon: Pokemon = if (hasCreatedPokemon) resultingFossil?.result?.create() ?: return else return
val direction = state?.get(HorizontalFacingBlock.FACING)?.opposite
if(pos != null && direction != null && world != null) {
val success = this.spawn(world, pos, direction, wildPokemon)
if(success) {
this.fossilState.growthState = "Taken"
this.createdPokemon = null
this.hasCreatedPokemon = false
this.fossilOwnerUUID = null
this.protectionTime = -1
world.playSound(null, tankBasePos, CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_POKEMON, SoundCategory.BLOCKS)
@@ -310,9 +315,9 @@ class FossilMultiblockStructure (
val analyzerEntity = world.getBlockEntity(analyzerPos) as? MultiblockEntity
val tankBaseEntity = world.getBlockEntity(tankBasePos) as? MultiblockEntity
val tankTopEntity = world.getBlockEntity(tankBasePos.up()) as? MultiblockEntity
val state = world.getBlockState(tankBaseEntity?.pos)
val direction = state.get(HorizontalFacingBlock.FACING).getOpposite()
val wildPokemon: Pokemon? = this.createdPokemon
val tankBaseBlockState = world.getBlockState(tankBaseEntity?.pos)
val direction = tankBaseBlockState.get(HorizontalFacingBlock.FACING).opposite
val wildPokemon: Pokemon? = if(hasCreatedPokemon) resultingFossil?.result?.create() else null
monitorEntity?.multiblockStructure = null
analyzerEntity?.multiblockStructure = null
@@ -372,7 +377,7 @@ class FossilMultiblockStructure (
world.playSound(null, tankBasePos, CobblemonSounds.FOSSIL_MACHINE_UNPROTECTED, SoundCategory.BLOCKS)
}
if (this.createdPokemon != null) {
if (this.hasCreatedPokemon) {
return
}
@@ -401,10 +406,7 @@ class FossilMultiblockStructure (
if (this.timeRemaining == 0) {
world.playSound(null, tankBasePos, CobblemonSounds.FOSSIL_MACHINE_FINISHED, SoundCategory.BLOCKS)
fossilInventory.clear()
this.resultingFossil?.let {
this.createdPokemon = it.result.create()
}
this.hasCreatedPokemon = true
if(this.fossilOwnerUUID != null) {
protectionTime = PROTECTION_TIME
}
@@ -475,20 +477,26 @@ class FossilMultiblockStructure (
val upperTankPos = tankBasePos.up()
val analyzerState = world.getBlockState(analyzerPos)
val tankState = world.getBlockState(tankBasePos.up())
world.setBlockState(analyzerPos, analyzerState.with(FossilAnalyzerBlock.ON, timeRemaining >= 0))
world.setBlockState(upperTankPos, tankState.with(RestorationTankBlock.ON, timeRemaining >= 0))
if (analyzerState.contains(FossilAnalyzerBlock.ON)) {
world.setBlockState(analyzerPos, analyzerState.with(FossilAnalyzerBlock.ON, timeRemaining >= 0))
}
if (tankState.contains(RestorationTankBlock.ON)) {
world.setBlockState(upperTankPos, tankState.with(RestorationTankBlock.ON, timeRemaining >= 0))
}
}
fun updateProgress(world: World) {
val screenID = if(protectionTime > 0F ) {
MonitorBlock.MonitorScreen.GREEN_PROGRESS_9
} else if (timeRemaining <= 0) {
MonitorBlock.MonitorScreen.OFF
} else {
getProgressScreen((TIME_TO_TAKE - timeRemaining) / TIME_PER_STAGE)
}
val monitorState = world.getBlockState(monitorPos)
world.setBlockState(monitorPos, monitorState.with(MonitorBlock.SCREEN, screenID))
if (monitorState.contains(MonitorBlock.SCREEN)) {
val screenID = if (protectionTime > 0F) {
MonitorBlock.MonitorScreen.GREEN_PROGRESS_9
} else if (timeRemaining <= 0) {
MonitorBlock.MonitorScreen.OFF
} else {
getProgressScreen((TIME_TO_TAKE - timeRemaining) / TIME_PER_STAGE)
}
world.setBlockState(monitorPos, monitorState.with(MonitorBlock.SCREEN, screenID))
}
}
fun getProgressScreen(progress:Int) : MonitorBlock.MonitorScreen {
@@ -603,9 +611,7 @@ class FossilMultiblockStructure (
result.putString(DataKeys.INSERTED_FOSSIL, this.resultingFossil!!.asString())
}
if (this.createdPokemon != null) {
result.put(DataKeys.CREATED_POKEMON, createdPokemon!!.saveToNBT(NbtCompound()))
}
result.putBoolean(DataKeys.HAS_CREATED_POKEMON, hasCreatedPokemon)
return result
}
@@ -654,7 +660,10 @@ class FossilMultiblockStructure (
}
if (nbt.contains(DataKeys.CREATED_POKEMON)) {
result.createdPokemon = Pokemon.loadFromNBT(nbt.getCompound(DataKeys.CREATED_POKEMON))
// migration of instances that saved the created pokeon in the nbt
result.hasCreatedPokemon = true
} else if (nbt.contains(DataKeys.HAS_CREATED_POKEMON)){
result.hasCreatedPokemon = nbt.getBoolean(DataKeys.HAS_CREATED_POKEMON)
}
result.fillLevel = result.organicMaterialInside * 8 / MATERIAL_TO_START
return result
@@ -62,6 +62,46 @@ object CobblemonBakingOverrides {
cobblemonModel("restoration_tank_connector", "none")
)
val COARSE_MULCH = registerOverride(
cobblemonResource("block/coarse_mulch"),
cobblemonModel("coarse_mulch", "none")
)
val GROWTH_MULCH = registerOverride(
cobblemonResource("block/growth_mulch"),
cobblemonModel("growth_mulch", "none")
)
val HUMID_MULCH = registerOverride(
cobblemonResource("block/humid_mulch"),
cobblemonModel("humid_mulch", "none")
)
val LOAMY_MULCH = registerOverride(
cobblemonResource("block/loamy_mulch"),
cobblemonModel("loamy_mulch", "none")
)
val PEAT_MULCH = registerOverride(
cobblemonResource("block/peat_mulch"),
cobblemonModel("peat_mulch", "none")
)
val RICH_MULCH = registerOverride(
cobblemonResource("block/rich_mulch"),
cobblemonModel("rich_mulch", "none")
)
val SANDY_MULCH = registerOverride(
cobblemonResource("block/sandy_mulch"),
cobblemonModel("sandy_mulch", "none")
)
val SURPRISE_MULCH = registerOverride(
cobblemonResource("block/surprise_mulch"),
cobblemonModel("surprise_mulch", "none")
)
fun registerOverride(modelLocation: Identifier, modelIdentifier: ModelIdentifier): BakingOverride {
val result = BakingOverride(modelLocation, modelIdentifier)
models.add(result)
@@ -15,9 +15,6 @@ import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import net.minecraft.client.MinecraftClient
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.util.math.RotationAxis.NEGATIVE_X
import net.minecraft.util.math.RotationAxis.POSITIVE_X
import net.minecraft.util.math.RotationAxis.POSITIVE_Y
object SpawnSnowstormParticleHandler : ClientNetworkPacketHandler<SpawnSnowstormParticlePacket> {
override fun handle(packet: SpawnSnowstormParticlePacket, client: MinecraftClient) {
@@ -10,16 +10,20 @@ package com.cobblemon.mod.common.client.particle
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies.species
import com.cobblemon.mod.common.api.snowstorm.BedrockParticleEffect
import com.cobblemon.mod.common.api.snowstorm.ParticleEmitterAction
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.client.render.SnowstormParticle
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.particle.SnowstormParticleEffect
import com.cobblemon.mod.common.util.math.geometry.transformDirection
import kotlin.random.Random
import net.minecraft.client.MinecraftClient
import net.minecraft.client.particle.NoRenderParticle
import net.minecraft.client.util.ParticleUtil.spawnParticle
import net.minecraft.client.world.ClientWorld
import net.minecraft.entity.Entity
import net.minecraft.util.math.Vec3d
@@ -48,12 +52,22 @@ class ParticleStorm(
.addFunction("entity_height") { DoubleValue(entity.boundingBox.yLength) }
.addFunction("entity_size") { DoubleValue(entity.boundingBox.run { if (xLength > yLength) xLength else yLength }) }
.addFunction("entity_radius") { DoubleValue(entity.boundingBox.run { if (xLength > yLength) xLength else yLength } / 2) }
.addFunction("entity_scale") {
val pokeEntity = entity as? PokemonEntity
val pokemon = pokeEntity?.pokemon
//Use form data if available, species as fall back
val baseScale = pokemon?.form?.baseScale ?: pokemon?.species?.baseScale ?: 1.0F
val pokemonScale = pokemon?.scaleModifier ?: 1.0F
val entityScale = pokeEntity?.scaleFactor ?: 1.0F
DoubleValue(baseScale * pokemonScale * entityScale)
}
// TODO replace with a generified call to if (entity is MoLangEntity) entity.applyVariables(env) or w/e
runtime.environment.setSimpleVariable("entity_width", DoubleValue(entity.boundingBox.xLength))
runtime.environment.setSimpleVariable("entity_height", DoubleValue(entity.boundingBox.yLength))
val longerDiameter = entity.boundingBox.run { if (xLength > yLength) xLength else yLength }
runtime.environment.setSimpleVariable("entity_size", DoubleValue(longerDiameter))
runtime.environment.setSimpleVariable("entity_radius", DoubleValue(longerDiameter / 2))
runtime.environment.setSimpleVariable("entity_scale", DoubleValue((entity as? PokemonEntity)?.scaleFactor ?: 1.0))
}
MinecraftClient.getInstance().particleManager.addParticle(this)
}
@@ -118,7 +118,7 @@ class SnowstormParticle(
init {
setVelocity(initialVelocity.x, initialVelocity.y, initialVelocity.z)
angle = storm.effect.particle.rotation.getInitialRotation(storm.runtime).toFloat()
angle = -storm.effect.particle.rotation.getInitialRotation(storm.runtime).toFloat()
prevAngle = angle
angularVelocity = storm.effect.particle.rotation.getInitialAngularVelocity(storm.runtime)
velocityMultiplier = 1F
@@ -256,24 +256,20 @@ class SnowstormParticle(
setParticleAgeInRuntime()
storm.effect.curves.forEach { it.apply(storm.runtime) }
storm.runtime.execute(storm.effect.particle.updateExpressions)
angularVelocity += storm.effect.particle.rotation.getAngularAcceleration(storm.runtime, angularVelocity) / 20
angularVelocity = storm.effect.particle.rotation.getAngularVelocity(storm.runtime, -angle.toDouble(), angularVelocity) / 20
if (age > maxAge || storm.runtime.resolveBoolean(storm.effect.particle.killExpression)) {
if (age >= maxAge || storm.runtime.resolveBoolean(storm.effect.particle.killExpression)) {
runExpirationEvents()
markDead()
return
} else {
val acceleration = storm.effect.particle.motion.getAcceleration(
storm.runtime,
Vec3d(velocityX, velocityY, velocityZ).multiply(20.0) // Uses blocks per second, not blocks per tick
).multiply(1 / 20.0).multiply(1 / 20.0) // blocks per second per second -> blocks per tick per tick
velocityX += acceleration.x
velocityY += acceleration.y
velocityZ += acceleration.z
val velocity = storm.effect.particle.motion.getVelocity(storm.runtime, this, Vec3d(velocityX, velocityY, velocityZ))
velocityX = velocity.x
velocityY = velocity.y
velocityZ = velocity.z
prevAngle = angle
angle = prevAngle + angularVelocity.toFloat()
// Subtract because Bedrock particles are counter-clockwise and Java Edition is clockwise.
angle = prevAngle - angularVelocity.toFloat()
}
viewDirection = storm.effect.particle.viewDirection.getDirection(
@@ -16,6 +16,7 @@ class BerryBlockEntityRenderState : BerryBlockEntity.RenderState {
override var needsRebuild = true
val vbo: VertexBuffer = VertexBuffer(VertexBuffer.Usage.STATIC)
var vboLightLevel: Int = 0
var drawVbo: Boolean = false
override fun close() {
vbo.close()
@@ -23,6 +23,7 @@ import net.minecraft.client.gl.ShaderProgram
import net.minecraft.client.gl.VertexBuffer
import net.minecraft.client.render.GameRenderer
import net.minecraft.client.render.LightmapTextureManager
import net.minecraft.client.render.RenderLayer
import net.minecraft.client.render.Tessellator
import net.minecraft.client.render.VertexConsumerProvider
import net.minecraft.client.render.VertexFormat
@@ -33,6 +34,7 @@ import net.minecraft.client.util.math.MatrixStack
import net.minecraft.util.math.Box
import net.minecraft.util.math.Vec3d
import org.joml.Matrix4f
import org.joml.Vector3f
class BerryBlockRenderer(private val context: BlockEntityRendererFactory.Context) : BlockEntityRenderer<BerryBlockEntity> {
@@ -45,33 +47,88 @@ class BerryBlockRenderer(private val context: BlockEntityRendererFactory.Context
override fun render(entity: BerryBlockEntity, tickDelta: Float, matrices: MatrixStack, vertexConsumers: VertexConsumerProvider, light: Int, overlay: Int) {
if (!isInRenderDistance(entity, entity.pos.toVec3d())) return
val blockState = entity.cachedState
val age = blockState.get(BerryBlock.AGE)
if (age <= BerryBlock.MATURE_AGE) {
return
}
if (entity.renderState == null) {
entity.renderState = BerryBlockEntityRenderState()
}
val renderState = entity.renderState as BerryBlockEntityRenderState
if (renderState.needsRebuild || renderState.vboLightLevel != light) {
renderToBuffer(entity, light, overlay, renderState.vbo)
renderToBuffer(entity, light, overlay, renderState)
renderState.vboLightLevel = light
(entity.renderState as BerryBlockEntityRenderState).needsRebuild = false
}
if (renderState.drawVbo) {
matrices.push()
CobblemonRenderLayers.BERRY_LAYER.startDrawing()
renderState.vbo.bind()
renderState.vbo.draw(
matrices.peek().positionMatrix.mul(RenderSystem.getModelViewMatrix()),
RenderSystem.getProjectionMatrix(),
GameRenderer.getRenderTypeCutoutProgram()
)
VertexBuffer.unbind()
CobblemonRenderLayers.BERRY_LAYER.endDrawing()
matrices.pop()
}
drawMulch(matrices, vertexConsumers, entity, light, overlay)
}
private fun drawMulch(
matrices: MatrixStack,
vertexConsumers: VertexConsumerProvider,
entity: BerryBlockEntity,
light: Int,
overlay: Int
) {
matrices.push()
CobblemonRenderLayers.BERRY_LAYER.startDrawing()
renderState.vbo.bind()
renderState.vbo.draw(
matrices.peek().positionMatrix.mul(RenderSystem.getModelViewMatrix()),
RenderSystem.getProjectionMatrix(),
GameRenderer.getRenderTypeCutoutProgram()
)
VertexBuffer.unbind()
CobblemonRenderLayers.BERRY_LAYER.endDrawing()
//Mulch is rendered on a different layer than the actual berries so
val mulchBuf = vertexConsumers.getBuffer(RenderLayer.getCutout())
val model = entity.mulchVariant.model
model?.let {
it.getModel().getQuads(entity.cachedState, null, entity.world?.random).forEach { quad ->
mulchBuf.quad(matrices.peek(), quad, 1F, 1F, 1F, light, overlay)
}
}
matrices.pop()
}
fun renderToBuffer(entity: BerryBlockEntity, light: Int, overlay: Int, buffer: VertexBuffer) {
fun renderToBuffer(entity: BerryBlockEntity, light: Int, overlay: Int, renderState: BerryBlockEntityRenderState) {
if (entity.cachedState.get(BerryBlock.AGE) == 0) {
renderBabyToBuffer(entity, light, overlay, renderState.vbo)
renderState.drawVbo = true
}
else if (entity.cachedState.get(BerryBlock.AGE) > BerryBlock.MATURE_AGE){
renderAdultToBuffer(entity, light, overlay, renderState.vbo)
renderState.drawVbo = true
}
else {
renderState.drawVbo = false
}
}
fun renderBabyToBuffer(entity: BerryBlockEntity, light: Int, overlay: Int, buffer: VertexBuffer) {
val bufferBuilder = Tessellator.getInstance().buffer
bufferBuilder.begin(CobblemonRenderLayers.BERRY_LAYER.drawMode, CobblemonRenderLayers.BERRY_LAYER.vertexFormat)
val berry = entity.berry() ?: return
val model = BerryModelRepository.modelOf(berry.fruitModelIdentifier) ?: return
val pos = berry.stageOnePositioning.position
model.setPosition(Axis.X_AXIS.ordinal, pos.x.toFloat())
model.setPosition(Axis.Y_AXIS.ordinal, pos.y.toFloat())
model.setPosition(Axis.Z_AXIS.ordinal, pos.z.toFloat())
val rot = berry.stageOnePositioning.rotation
model.setAngles(
Math.toRadians(180 - rot.x).toFloat(),
Math.toRadians(180 + rot.y).toFloat(),
Math.toRadians(rot.z).toFloat()
)
model.render(MatrixStack(), bufferBuilder, light, overlay)
val bufferBuilderFinal = bufferBuilder.end()
buffer.bind()
buffer.upload(bufferBuilderFinal)
VertexBuffer.unbind()
}
fun renderAdultToBuffer(entity: BerryBlockEntity, light: Int, overlay: Int, buffer: VertexBuffer) {
val blockState = entity.cachedState
val age = blockState.get(BerryBlock.AGE)
if (age <= BerryBlock.MATURE_AGE) {
@@ -61,17 +61,17 @@ class RestorationTankRenderer(ctx: BlockEntityRendererFactory.Context) : BlockEn
matrices.pop()
}
val fillLevel = struct.fillLevel
if (fillLevel == 0 && struct.createdPokemon == null) {
if (fillLevel == 0 && !struct.hasCreatedPokemon) {
return
}
if (struct.isRunning() or (struct.createdPokemon != null)) renderFetus(entity, tickDelta, matrices, vertexConsumers, light, overlay)
if (struct.isRunning() or (struct.hasCreatedPokemon)) renderFetus(entity, tickDelta, matrices, vertexConsumers, light, overlay)
matrices.push()
val transparentBuffer = vertexConsumers.getBuffer(RenderLayer.getTranslucent())
val fluidModel = if (struct.isRunning()) FLUID_MODELS[8]
else if (struct.createdPokemon != null) FLUID_MODELS[7]
else if (struct.hasCreatedPokemon) FLUID_MODELS[7]
else FLUID_MODELS[fillLevel.coerceAtMost(FLUID_MODELS.size - 1) - 1]
fluidModel.getQuads(entity.cachedState, null, entity.world?.random).forEach { quad ->
transparentBuffer?.quad(matrices.peek(), quad, 0.75f, 0.75f, 0.75f, light, OverlayTexture.DEFAULT_UV)
@@ -37,7 +37,7 @@ class JsonPose<T : Entity>(model: PoseableEntityModel<T>, json: JsonObject) {
it.environment.getQueryStruct().addFunctions(model.functions.functions)
}
val condition: ExpressionLike = json.singularToPluralList("condition").get("condition")?.normalizeToArray()?.map { it.asString }?.asExpressionLike() ?: "true".asExpressionLike()
val condition: ExpressionLike = json.singularToPluralList("condition").get("conditions")?.normalizeToArray()?.map { it.asString }?.asExpressionLike() ?: "true".asExpressionLike()
val poseName = json.get("poseName")?.asString ?: "pose"
val poseTypes = (json.get("poseTypes")?.asJsonArray?.map { name ->
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.render.models.blockbench
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.entity.Entity
/**
* A storage of locator information for a model, attached to its [joint].
@@ -68,20 +69,45 @@ class LocatorAccess(
* Updates all of the locator states with the position at this current frame.
* This is the same logic as ModelPart uses, that's why we reuse ModelPart#rotate.
*/
fun update(matrixStack: MatrixStack, state: MutableMap<String, MatrixWrapper>) {
fun update(matrixStack: MatrixStack, entity: Entity, scale: Float, state: MutableMap<String, MatrixWrapper>, isRoot: Boolean = false) {
matrixStack.push()
joint.transform(matrixStack)
if (isRoot) {
matrixStack.push()
matrixStack.scale(-1F, -1F, 1F)
state.getOrPut("root") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
matrixStack.pop()
// Put in an approximation of the target locator. If the model has one defined,
// this will be overridden.
matrixStack.push()
matrixStack.translate(0.0, -entity.boundingBox.yLength / 2.0 / scale, -entity.width * 0.6 / scale)
matrixStack.scale(-1F, -1F, 1F)
state.getOrPut("target") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
state.getOrPut("special_attack") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
matrixStack.pop()
// If we have the entity, put in a "middle" locator for center of mass
// this will be overridden.
matrixStack.push()
matrixStack.translate(0.0, -entity.boundingBox.yLength / 2.0 / scale, 0.0)
matrixStack.scale(-1F, -1F, 1F)
state.getOrPut("middle") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
matrixStack.pop()
}
for ((name, locator) in locators) {
matrixStack.push()
locator.transform(matrixStack)
matrixStack.scale(1F, -1F, 1F)
matrixStack.scale(-1F, -1F, 1F)
state.getOrPut(name) { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
matrixStack.pop()
}
children.forEach {
it.update(matrixStack, state)
it.update(matrixStack, entity, scale, state, isRoot = false)
}
matrixStack.pop()
@@ -261,6 +261,41 @@ abstract class PoseableEntityModel<T : Entity>(
)
)
}
.addFunction("bedrock_primary_quirk") { params ->
val animationGroup = params.getString(0)
val animationNames = params.get<MoValue>(1)?.let { if (it is ArrayStruct) it.map.values.map { it.asString() } else listOf(it.asString()) } ?: listOf()
val minSeconds = params.getDoubleOrNull(2) ?: 8F
val maxSeconds = params.getDoubleOrNull(3) ?: 30F
val loopTimes = params.getDoubleOrNull(4)?.toInt() ?: 1
val excludedLabels = mutableSetOf<String>()
var curve: WaveFunction = { t ->
if (t < 0.1) {
t * 10
} else if (t < 0.9) {
1F
} else {
1F
}
}
for (index in 5 until params.params.size) {
val param = params.get<MoValue>(index)
if (param is ObjectValue<*>) {
curve = param.obj as WaveFunction
continue
}
val label = params.getString(index) ?: continue
excludedLabels.add(label)
}
ObjectValue(
quirk(
secondsBetweenOccurrences = minSeconds.toFloat() to maxSeconds.toFloat(),
condition = { true },
loopTimes = 1..loopTimes,
animation = { PrimaryAnimation(bedrockStateful(animationGroup, animationNames.random()), excludedLabels = excludedLabels, curve = curve) }
)
)
}
@Transient
val runtime = MoLangRuntime().setup().setupClient().also { it.environment.getQueryStruct().addFunctions(functions.functions) }
@@ -788,7 +823,6 @@ abstract class PoseableEntityModel<T : Entity>(
matrixStack.push()
matrixStack.scale(-1F, -1F, 1F)
scale = entity.pokemon.form.baseScale * entity.pokemon.scaleModifier * (entity.delegate as PokemonClientDelegate).entityScaleModifier
matrixStack.scale(scale, scale, scale)
} else if (entity is EmptyPokeBallEntity) {
matrixStack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(entity.yaw))
@@ -803,27 +837,14 @@ abstract class PoseableEntityModel<T : Entity>(
matrixStack.scale(1F, -1F, 1F)
}
matrixStack.push()
matrixStack.scale(1F, -1F, 1F)
val states = state.locatorStates
states.getOrPut("root") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
matrixStack.pop()
if (isForLivingEntityRenderer) {
// Standard living entity offset, only God knows why Mojang did this.
matrixStack.translate(0.0, -1.5, 0.0)
}
// If we have the entity, put in an approximation of the target locator. If the model has one defined,
// this will be overridden.
matrixStack.push()
matrixStack.translate(0.0, -entity.boundingBox.yLength / 2.0 / scale + 1.5F, -entity.width * 0.6 / scale)
matrixStack.scale(1F, -1F, 1F)
states.getOrPut("target") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
states.getOrPut("special_attack") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
matrixStack.pop()
locatorAccess.update(matrixStack, states)
locatorAccess.update(matrixStack, entity, scale, states, isRoot = true)
}
fun ModelPart.translation(
@@ -135,6 +135,7 @@ abstract class PoseableEntityState<T : Entity> : Schedulable {
val storm = ParticleStorm(
effect = effect,
entity = entity,
matrixWrapper = matrixWrapper,
world = world,
runtime = particleRuntime,
@@ -39,6 +39,7 @@ import com.google.gson.annotations.SerializedName
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type
import java.util.function.Supplier
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
/**
@@ -49,7 +50,8 @@ import net.minecraft.util.math.Vec3d
* @author Hiroku
* @since August 7th, 2022
*/
class JsonPokemonPoseableModel(override val rootPart: Bone) : PokemonPoseableModel(), HeadedFrame {
class JsonPokemonPoseableModel(rootPart: Bone) : PokemonPoseableModel(), HeadedFrame {
override val rootPart = (rootPart as ModelPart).children.entries.first().let { rootPart.registerChildWithAllChildren(it.key) }
companion object {
val gson = GsonBuilder()
.setPrettyPrinting()
@@ -130,6 +132,7 @@ class JsonPokemonPoseableModel(override val rootPart: Bone) : PokemonPoseableMod
override fun createInstance(type: Type): JsonPokemonPoseableModel {
return JsonPokemonPoseableModel(modelPart!!).also {
model = it
modelPart as ModelPart
it.loadAllNamedChildren(modelPart!!)
}
}
@@ -0,0 +1,62 @@
/*
* 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.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.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
class SmeargleModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BipedFrame {
override val rootPart = root.registerChildWithAllChildren("smeargle")
override val head = getPart("head")
/* No arm swinging because it's holding its tail */
override val leftLeg = getPart("leg_left")
override val rightLeg = getPart("leg_right")
override var portraitScale = 1.29F
override var portraitTranslation = Vec3d(-0.03, 0.96, 0.0)
override var profileScale = 0.67F
override var profileTranslation = Vec3d(0.05, 0.8, 0.0)
lateinit var standing: PokemonPose
lateinit var walk: PokemonPose
//override val cryAnimation = CryProvider { _, _ -> bedrockStateful("smeargle", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("smeargle", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES + PoseType.SLEEP,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("smeargle", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("smeargle", "ground_idle"),
BipedWalkAnimation(this, periodMultiplier = 0.6F, amplitudeMultiplier = 0.9F)
)
)
}
}
@@ -47,7 +47,6 @@ class ShedinjaModel (root: ModelPart) : PokemonPoseableModel() {
poseName = "standing",
poseTypes = PoseType.UI_POSES + PoseType.STAND,
transformTicks = 10,
condition = { !it.isBattling },
quirks = arrayOf(blink),
idleAnimations = arrayOf(
bedrock("shedinja", "ground_idle")
@@ -20,7 +20,7 @@ import net.minecraft.util.math.Vec3d
class WailordModel(root: ModelPart) : PokemonPoseableModel() {
override val rootPart = root.registerChildWithAllChildren("wailord")
override var portraitScale = 0.45F
override var portraitTranslation = Vec3d(-0.38, 0.8, 0.0)
override var portraitTranslation = Vec3d(-0.38, 0.8, 6.69)
override var profileScale = 0.25F
override var profileTranslation = Vec3d(0.0, 1.2, -10.0)
@@ -21,7 +21,7 @@ class TyrantrumModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
override val head = getPart("head")
override var portraitTranslation = Vec3d(-0.86, 2.0, 0.0)
override var portraitTranslation = Vec3d(-0.86, 2.0, 5.0)
override var portraitScale = 0.59F
override var profileTranslation = Vec3d(0.02, 1.28, -10.0)
@@ -714,6 +714,7 @@ object PokemonModelRepository : VaryingModelRepository<PokemonEntity, PokemonPos
inbuilt("rowlet_hisui_bias", ::RowletHisuiBiasModel)
inbuilt("dartrix_hisui_bias", ::DartrixHisuiBiasModel)
inbuilt("decidueye_hisuian", ::DecidueyeHisuianModel)
inbuilt("smeargle", ::SmeargleModel)
}
override fun loadJsonPoser(json: String): (Bone) -> PokemonPoseableModel {
@@ -24,7 +24,6 @@ import com.mojang.brigadier.context.CommandContext
import net.minecraft.server.command.CommandManager
import net.minecraft.server.command.CommandManager.literal
import net.minecraft.server.command.ServerCommandSource
import net.minecraft.text.Text
/**
* Spawn Pokemon From Surrounding Pool
@@ -84,14 +83,15 @@ object SpawnPokemonFromPool {
val spawnAction = result.second.doSpawn(ctx = result.first)
spawnAction.future.thenApply {
if (it is EntitySpawnResult) {
for (entity in it.entities) {
player.sendMessage(commandLang("spawnpokemonfrompool.success", entity.displayName).green())
spawnAction.future
.thenApply {
if (it is EntitySpawnResult) {
for (entity in it.entities) {
player.sendMessage(commandLang("spawnpokemonfrompool", entity.displayName).green())
}
}
}
}
spawnAction.complete()
spawnsTriggered++
}
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.item.ability.AbilityChanger
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker.after
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.api.scheduling.taskBuilder
import com.cobblemon.mod.common.api.text.green
@@ -25,10 +26,13 @@ import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimationPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
import com.cobblemon.mod.common.trade.ActiveTrade
import com.cobblemon.mod.common.trade.DummyTradeParticipant
import com.cobblemon.mod.common.trade.PlayerTradeParticipant
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.toPokemon
import com.google.gson.GsonBuilder
@@ -61,6 +65,22 @@ object TestCommand {
}
try {
val player = context.source.entity as ServerPlayerEntity
val evolutionEntity = GenericBedrockEntity(world = player.world)
evolutionEntity?.apply {
category = cobblemonResource("evolution")
colliderHeight = 1.5F
colliderWidth = 1.5F
scale = 1F
syncAge = true // Otherwise particle animation will be starting from zero even if you come along partway through
setPosition(player.x, player.y, player.z + 4)
}
player.world.spawnEntity(evolutionEntity)
after(seconds = 0.5F) {
player.sendPacket(PlayPoseableAnimationPacket(evolutionEntity.id, setOf("evolution:animation.evolution.evolution"), emptySet()))
}
// readBerryDataFromCSV()
// this.testClosestBattle(context)
@@ -171,9 +171,6 @@ class CobblemonConfig {
@NodeCategory(Category.Spawning)
var baseApricornTreeGenerationChance = 0.1F
@NodeCategory(Category.Pokemon)
var ninjaskCreatesShedinja = true
@NodeCategory(Category.Pokemon)
var displayEntityLevelLabel = true
@@ -20,6 +20,7 @@ import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
import com.cobblemon.mod.common.api.net.serializers.Vec3DataSerializer
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.scheduling.Schedulable
@@ -57,6 +58,7 @@ import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket
import net.minecraft.particle.ParticleTypes
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.sound.SoundEvents
import net.minecraft.util.Identifier
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.hit.EntityHitResult
import net.minecraft.util.hit.HitResult
@@ -189,7 +191,7 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
val owner = owner
if (!pokemonEntity.pokemon.isWild()) {
owner?.sendMessage(lang("capture.not_wild", pokemonEntity.pokemon.species.translatedName).red())
owner?.sendMessage(lang("capture.not_wild", pokemonEntity.exposedSpecies.translatedName).red())
return drop()
}
@@ -204,7 +206,7 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
val hitBattlePokemon = hitActor?.activePokemon?.find { it.battlePokemon?.effectedPokemon?.entity == pokemonEntity }
if (throwerActor == null) {
owner.sendMessage(lang("capture.in_battle", pokemonEntity.pokemon.species.translatedName).red())
owner.sendMessage(lang("capture.in_battle", pokemonEntity.exposedSpecies.translatedName).red())
return drop()
}
@@ -230,13 +232,13 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
"capture.attempted_capture",
throwerActor.getName(),
pokeBall.item().name,
pokemonEntity.pokemon.species.translatedName
pokemonEntity.exposedSpecies.translatedName
).yellow()
)
battle.sendUpdate(BattleCaptureStartPacket(pokeBall.name, aspects, hitBattlePokemon.getPNX()))
throwerActor.forceChoose(ForcePassActionResponse())
} else if (pokemonEntity.isBusy) {
owner?.sendMessage(lang("capture.busy", pokemonEntity.pokemon.species.translatedName).red())
owner?.sendMessage(lang("capture.busy", pokemonEntity.exposedSpecies.translatedName).red())
return drop()
} else if (owner is ServerPlayerEntity && BattleRegistry.getBattleByParticipatingPlayer(owner) != null) {
owner.sendMessage(lang("you_in_battle").red())
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.feature.ChoiceSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeature
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.pokemon.feature.StringSpeciesFeature
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
@@ -615,129 +616,152 @@ open class PokemonEntity(
override fun interactMob(player: PlayerEntity, hand: Hand) : ActionResult {
val itemStack = player.getStackInHand(hand)
val colorFeatureType = SpeciesFeatures.getFeaturesFor(pokemon.species).find { it is ChoiceSpeciesFeatureProvider && DataKeys.CAN_BE_COLORED in it.keys }
val colorFeature = pokemon.getFeature<StringSpeciesFeature>(DataKeys.CAN_BE_COLORED)
if (itemStack.isOf(Items.SHEARS) && this.isShearable) {
this.sheared(SoundCategory.PLAYERS)
this.emitGameEvent(GameEvent.SHEAR, player)
itemStack.damage(1, player) { it.sendToolBreakStatus(hand) }
return ActionResult.SUCCESS
}
else if (itemStack.isOf(Items.BUCKET)) {
if (pokemon.getFeature<FlagSpeciesFeature>(DataKeys.CAN_BE_MILKED) != null) {
player.playSound(SoundEvents.ENTITY_GOAT_MILK, 1.0f, 1.0f)
val milkBucket = ItemUsage.exchangeStack(itemStack, player, Items.MILK_BUCKET.defaultStack)
player.setStackInHand(hand, milkBucket)
return ActionResult.success(world.isClient)
}
} else if (itemStack.isOf(Items.BOWL)) {
if (pokemon.aspects.any() {it.contains("mooshtank")}) {
player.playSound(SoundEvents.ENTITY_MOOSHROOM_MILK, 1.0f, 1.0f)
// if the Mooshtank ate a Flower beforehand
if (pokemon.lastFlowerFed != ItemStack.EMPTY && pokemon.aspects.any() {it.contains("mooshtank-brown")}) {
var effect: StatusEffect? = null
var duration = 0
if (ownerUuid == player.uuid || ownerUuid == null) {
if (itemStack.isOf(Items.SHEARS) && this.isShearable) {
this.sheared(SoundCategory.PLAYERS)
this.emitGameEvent(GameEvent.SHEAR, player)
itemStack.damage(1, player) { it.sendToolBreakStatus(hand) }
return ActionResult.SUCCESS
} else if (itemStack.isOf(Items.BUCKET)) {
if (pokemon.getFeature<FlagSpeciesFeature>(DataKeys.CAN_BE_MILKED) != null) {
player.playSound(SoundEvents.ENTITY_GOAT_MILK, 1.0f, 1.0f)
val milkBucket = ItemUsage.exchangeStack(itemStack, player, Items.MILK_BUCKET.defaultStack)
player.setStackInHand(hand, milkBucket)
return ActionResult.success(world.isClient)
}
} else if (itemStack.isOf(Items.BOWL)) {
if (pokemon.aspects.any { it.contains("mooshtank") }) {
player.playSound(SoundEvents.ENTITY_MOOSHROOM_MILK, 1.0f, 1.0f)
// if the Mooshtank ate a Flower beforehand
if (pokemon.lastFlowerFed != ItemStack.EMPTY && pokemon.aspects.any() { it.contains("mooshtank-brown") }) {
var effect: StatusEffect? = null
var duration = 0
if (pokemon.lastFlowerFed.isOf(Items.ALLIUM)) {
effect = StatusEffect.byRawId(12) // Fire Resistance
duration = 80 // 4 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.AZURE_BLUET)) {
effect = StatusEffect.byRawId(15) // Blindness
duration = 160 // 8 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.BLUE_ORCHID) || pokemon.lastFlowerFed.isOf(Items.DANDELION)) {
effect = StatusEffect.byRawId(23) // Saturation
duration = 7 // .35 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.CORNFLOWER)) {
effect = StatusEffect.byRawId(8) // Jump Boost
duration = 120 // 6 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.LILY_OF_THE_VALLEY)) {
effect = StatusEffect.byRawId(19) // Poison
duration = 240 // 12 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.OXEYE_DAISY)) {
effect = StatusEffect.byRawId(10) // Regeneration
duration = 160 // 8 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.POPPY) || pokemon.lastFlowerFed.isOf(Items.TORCHFLOWER)) {
effect = StatusEffect.byRawId(16) // Night Vision
duration = 100 // 5 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.PINK_TULIP) || pokemon.lastFlowerFed.isOf(Items.RED_TULIP) || pokemon.lastFlowerFed.isOf(Items.WHITE_TULIP) || pokemon.lastFlowerFed.isOf(Items.ORANGE_TULIP)) {
effect = StatusEffect.byRawId(18) // Weakness
duration = 180 // 9 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.WITHER_ROSE)) {
effect = StatusEffect.byRawId(20) // Wither
duration = 160 // 8 seconds
} else if (pokemon.lastFlowerFed.isOf(CobblemonItems.PEP_UP_FLOWER)) {
effect = StatusEffect.byRawId(25) // Levitation
duration = 160 // 8 seconds
if (pokemon.lastFlowerFed.isOf(Items.ALLIUM)) {
effect = StatusEffect.byRawId(12) // Fire Resistance
duration = 80 // 4 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.AZURE_BLUET)) {
effect = StatusEffect.byRawId(15) // Blindness
duration = 160 // 8 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.BLUE_ORCHID) || pokemon.lastFlowerFed.isOf(Items.DANDELION)) {
effect = StatusEffect.byRawId(23) // Saturation
duration = 7 // .35 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.CORNFLOWER)) {
effect = StatusEffect.byRawId(8) // Jump Boost
duration = 120 // 6 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.LILY_OF_THE_VALLEY)) {
effect = StatusEffect.byRawId(19) // Poison
duration = 240 // 12 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.OXEYE_DAISY)) {
effect = StatusEffect.byRawId(10) // Regeneration
duration = 160 // 8 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.POPPY) || pokemon.lastFlowerFed.isOf(Items.TORCHFLOWER)) {
effect = StatusEffect.byRawId(16) // Night Vision
duration = 100 // 5 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.PINK_TULIP) || pokemon.lastFlowerFed.isOf(Items.RED_TULIP) || pokemon.lastFlowerFed.isOf(
Items.WHITE_TULIP
) || pokemon.lastFlowerFed.isOf(Items.ORANGE_TULIP)
) {
effect = StatusEffect.byRawId(18) // Weakness
duration = 180 // 9 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.WITHER_ROSE)) {
effect = StatusEffect.byRawId(20) // Wither
duration = 160 // 8 seconds
} else if (pokemon.lastFlowerFed.isOf(CobblemonItems.PEP_UP_FLOWER)) {
effect = StatusEffect.byRawId(25) // Levitation
duration = 160 // 8 seconds
}
// modify the suspicious stew with the effect
val susStewStack = Items.SUSPICIOUS_STEW.defaultStack
SuspiciousStewItem.addEffectToStew(susStewStack, effect, duration)
val susStewEffect = ItemUsage.exchangeStack(itemStack, player, susStewStack)
//give player modified Suspicious Stew
player.setStackInHand(hand, susStewEffect)
// reset the flower fed state
pokemon.lastFlowerFed = ItemStack.EMPTY
return ActionResult.success(world.isClient)
} else {
val mushroomStew = ItemUsage.exchangeStack(itemStack, player, Items.MUSHROOM_STEW.defaultStack)
player.setStackInHand(hand, mushroomStew)
return ActionResult.success(world.isClient)
}
// modify the suspicious stew with the effect
val susStewStack = Items.SUSPICIOUS_STEW.defaultStack
SuspiciousStewItem.addEffectToStew(susStewStack, effect, duration)
val susStewEffect = ItemUsage.exchangeStack(itemStack, player, susStewStack)
//give player modified Suspicious Stew
player.setStackInHand(hand, susStewEffect)
// reset the flower fed state
pokemon.lastFlowerFed = ItemStack.EMPTY
return ActionResult.success(world.isClient)
}
else {
val mushroomStew = ItemUsage.exchangeStack(itemStack, player, Items.MUSHROOM_STEW.defaultStack)
player.setStackInHand(hand, mushroomStew)
return ActionResult.success(world.isClient)
}
}
}
// Flowers used on brown MooshTanks
else if (itemStack.isOf(Items.ALLIUM) ||
itemStack.isOf(Items.AZURE_BLUET) ||
itemStack.isOf(Items.BLUE_ORCHID) ||
itemStack.isOf(Items.DANDELION) ||
itemStack.isOf(Items.CORNFLOWER) ||
itemStack.isOf(Items.LILY_OF_THE_VALLEY) ||
itemStack.isOf(Items.OXEYE_DAISY) ||
itemStack.isOf(Items.POPPY) ||
itemStack.isOf(Items.TORCHFLOWER) ||
itemStack.isOf(Items.PINK_TULIP) ||
itemStack.isOf(Items.RED_TULIP) ||
itemStack.isOf(Items.WHITE_TULIP) ||
itemStack.isOf(Items.ORANGE_TULIP) ||
itemStack.isOf(Items.WITHER_ROSE) ||
itemStack.isOf(CobblemonItems.PEP_UP_FLOWER)) {
if (pokemon.aspects.any() {it.contains("mooshtank")}) {
player.playSound(SoundEvents.ENTITY_MOOSHROOM_EAT, 1.0f, 1.0f)
pokemon.lastFlowerFed = itemStack
// Flowers used on brown MooshTanks
else if (itemStack.isOf(Items.ALLIUM) ||
itemStack.isOf(Items.AZURE_BLUET) ||
itemStack.isOf(Items.BLUE_ORCHID) ||
itemStack.isOf(Items.DANDELION) ||
itemStack.isOf(Items.CORNFLOWER) ||
itemStack.isOf(Items.LILY_OF_THE_VALLEY) ||
itemStack.isOf(Items.OXEYE_DAISY) ||
itemStack.isOf(Items.POPPY) ||
itemStack.isOf(Items.TORCHFLOWER) ||
itemStack.isOf(Items.PINK_TULIP) ||
itemStack.isOf(Items.RED_TULIP) ||
itemStack.isOf(Items.WHITE_TULIP) ||
itemStack.isOf(Items.ORANGE_TULIP) ||
itemStack.isOf(Items.WITHER_ROSE) ||
itemStack.isOf(CobblemonItems.PEP_UP_FLOWER)
) {
if (pokemon.aspects.any { it.contains("mooshtank") }) {
player.playSound(SoundEvents.ENTITY_MOOSHROOM_EAT, 1.0f, 1.0f)
pokemon.lastFlowerFed = itemStack
return ActionResult.success(world.isClient)
}
} else if (!player.isSneaking && player.uuid == ownerUuid && (itemStack.isOf(CobblemonItems.RELIC_COIN)
|| itemStack.isOf(CobblemonItems.RELIC_COIN_POUCH)
|| itemStack.isOf(CobblemonItems.RELIC_COIN_SACK)
|| itemStack.isOf(Items.NETHERITE_SCRAP)
|| itemStack.isOf(Items.NETHERITE_INGOT)
|| itemStack.isOf(Items.NETHERITE_BLOCK))
) {
if (GimmighoulStashHandler.interactMob(player, hand, pokemon)) {
return ActionResult.SUCCESS
}
} else if (itemStack.item is DyeItem && colorFeatureType != null) {
val currentColor = colorFeature?.value ?: ""
val item = itemStack.item as DyeItem
if (!item.color.name.equals(currentColor, ignoreCase = true)) {
if (player is ServerPlayerEntity) {
if (colorFeature != null) {
colorFeature.value = item.color.name.lowercase()
this.pokemon.markFeatureDirty(colorFeature)
} else {
val newColorFeature =
StringSpeciesFeature(DataKeys.CAN_BE_COLORED, item.color.name.lowercase())
this.pokemon.features.add(newColorFeature)
this.pokemon.anyChangeObservable.emit(pokemon)
}
this.pokemon.updateAspects()
if (!player.isCreative) {
itemStack.decrement(1)
}
}
return ActionResult.success(world.isClient)
}
} else if (itemStack.item.equals(Items.WATER_BUCKET) && colorFeatureType != null) {
if (player is ServerPlayerEntity) {
if (colorFeature != null) {
if (!player.isCreative) {
itemStack.decrement(1)
player.giveOrDropItemStack(Items.BUCKET.defaultStack)
}
colorFeature.value = ""
this.pokemon.markFeatureDirty(colorFeature)
this.pokemon.updateAspects()
}
}
return ActionResult.success(world.isClient)
}
} else if(!player.isSneaking && (itemStack.isOf(CobblemonItems.RELIC_COIN)
|| itemStack.isOf(CobblemonItems.RELIC_COIN_POUCH)
|| itemStack.isOf(CobblemonItems.RELIC_COIN_SACK)
|| itemStack.isOf(Items.NETHERITE_SCRAP)
|| itemStack.isOf(Items.NETHERITE_INGOT)
|| itemStack.isOf(Items.NETHERITE_BLOCK))) {
if(GimmighoulStashHandler.interactMob(player, hand, pokemon)) {
return ActionResult.SUCCESS
}
}
else if (itemStack.item is DyeItem && colorFeature != null) {
val item = itemStack.item as DyeItem
if (item.color.name.lowercase() != colorFeature.value.lowercase()) {
itemStack.decrement(1)
colorFeature.value = item.color.name.lowercase()
this.pokemon.markFeatureDirty(colorFeature)
this.pokemon.updateAspects()
}
}
else if (itemStack.item.equals(Items.WATER_BUCKET) && colorFeature != null) {
itemStack.decrement(1)
player.giveOrDropItemStack(Items.BUCKET.defaultStack)
colorFeature.value = ""
this.pokemon.markFeatureDirty(colorFeature)
this.pokemon.updateAspects()
}
if (hand == Hand.MAIN_HAND && player is ServerPlayerEntity && pokemon.getOwnerPlayer() == player) {
if (player.isSneaking) {
@@ -235,7 +235,7 @@ class PokemonServerDelegate : PokemonSideDelegate {
}
if (entity.deathTime == 60) {
if (entity.owner == null) {
if (entity.pokemon.isWild()) {
entity.world.sendEntityStatus(entity, 60.toByte()) // Sends smoke effect
if(entity.world.gameRules.getBoolean(CobblemonGameRules.DO_POKEMON_LOOT)) {
(entity.drops ?: entity.pokemon.form.drops).drop(entity, entity.world as ServerWorld, entity.pos, entity.killer)
@@ -10,10 +10,9 @@ package com.cobblemon.mod.common.integration.jei.berry
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.integration.jei.CobblemonJeiProvider
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.item.berry.BerryItem
import mezz.jei.api.registration.IRecipeCategoryRegistration
import mezz.jei.api.registration.IRecipeRegistration
import net.minecraft.item.ItemStack
class BerryMutationProvider : CobblemonJeiProvider {
override fun registerCategory(registration: IRecipeCategoryRegistration) {
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.integration.jei.berry
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.item.berry.BerryItem
//Dont make these ItemStacks, they do not have consistent hash codes, breaks stuff
data class BerryMutationRecipe(
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.item
package com.cobblemon.mod.common.item.berry
import com.cobblemon.mod.common.block.BerryBlock
import net.minecraft.item.AliasedBlockItem
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.genericRuntime
import com.cobblemon.mod.common.util.resolveInt
@@ -15,7 +15,6 @@ import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.item.battle.BagItem
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.genericRuntime
@@ -16,7 +16,6 @@ import com.cobblemon.mod.common.api.item.PokemonAndMoveSelectingItem
import com.cobblemon.mod.common.api.moves.Move
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.item.battle.BagItem
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.genericRuntime
@@ -15,7 +15,6 @@ import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.item.battle.BagItem
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.genericRuntime
@@ -15,7 +15,6 @@ import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.api.pokemon.status.Status
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.item.battle.BagItem
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.entity.player.PlayerEntity
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.item.battle.BagItem
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.item.ItemStack
@@ -253,6 +253,37 @@ object CobblemonItemGroups {
entries.add(CobblemonItems.BLACK_TUMBLESTONE_BLOCK)
entries.add(CobblemonItems.SKY_TUMBLESTONE_BLOCK)
entries.add(CobblemonItems.POLISHED_TUMBLESTONE)
entries.add(CobblemonItems.POLISHED_TUMBLESTONE_STAIRS)
entries.add(CobblemonItems.POLISHED_TUMBLESTONE_SLAB)
entries.add(CobblemonItems.POLISHED_TUMBLESTONE_WALL)
entries.add(CobblemonItems.CHISELED_POLISHED_TUMBLESTONE)
entries.add(CobblemonItems.TUMBLESTONE_BRICKS)
entries.add(CobblemonItems.TUMBLESTONE_BRICK_STAIRS)
entries.add(CobblemonItems.TUMBLESTONE_BRICK_SLAB)
entries.add(CobblemonItems.TUMBLESTONE_BRICK_WALL)
entries.add(CobblemonItems.CHISELED_TUMBLESTONE_BRICKS)
entries.add(CobblemonItems.POLISHED_BLACK_TUMBLESTONE)
entries.add(CobblemonItems.POLISHED_BLACK_TUMBLESTONE_STAIRS)
entries.add(CobblemonItems.POLISHED_BLACK_TUMBLESTONE_SLAB)
entries.add(CobblemonItems.POLISHED_BLACK_TUMBLESTONE_WALL)
entries.add(CobblemonItems.CHISELED_POLISHED_BLACK_TUMBLESTONE)
entries.add(CobblemonItems.BLACK_TUMBLESTONE_BRICKS)
entries.add(CobblemonItems.BLACK_TUMBLESTONE_BRICK_STAIRS)
entries.add(CobblemonItems.BLACK_TUMBLESTONE_BRICK_SLAB)
entries.add(CobblemonItems.BLACK_TUMBLESTONE_BRICK_WALL)
entries.add(CobblemonItems.CHISELED_BLACK_TUMBLESTONE_BRICKS)
entries.add(CobblemonItems.POLISHED_SKY_TUMBLESTONE)
entries.add(CobblemonItems.POLISHED_SKY_TUMBLESTONE_STAIRS)
entries.add(CobblemonItems.POLISHED_SKY_TUMBLESTONE_SLAB)
entries.add(CobblemonItems.POLISHED_SKY_TUMBLESTONE_WALL)
entries.add(CobblemonItems.CHISELED_POLISHED_SKY_TUMBLESTONE)
entries.add(CobblemonItems.SKY_TUMBLESTONE_BRICKS)
entries.add(CobblemonItems.SKY_TUMBLESTONE_BRICK_STAIRS)
entries.add(CobblemonItems.SKY_TUMBLESTONE_BRICK_SLAB)
entries.add(CobblemonItems.SKY_TUMBLESTONE_BRICK_WALL)
entries.add(CobblemonItems.CHISELED_SKY_TUMBLESTONE_BRICKS)
entries.add(CobblemonItems.DAWN_STONE_ORE)
entries.add(CobblemonItems.DEEPSLATE_DAWN_STONE_ORE)
entries.add(CobblemonItems.DUSK_STONE_ORE)
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.pokemon.update
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
class FormUpdatePacket(pokemon: () -> Pokemon, form: FormData) : SingleUpdatePacket<FormData, FormUpdatePacket>(pokemon, form) {
override fun encodeValue(buffer: PacketByteBuf) {
this.value.encode(buffer)
}
override fun set(pokemon: Pokemon, value: FormData) {
pokemon.form = value
}
override val id: Identifier = ID
companion object {
val ID: Identifier = cobblemonResource("packets/form-update")
fun decode(buffer: PacketByteBuf): FormUpdatePacket {
val pokemon = decodePokemon(buffer)
val form = FormData()
form.species = pokemon().species
form.decode(buffer)
return FormUpdatePacket(pokemon, form)
}
}
}
@@ -43,6 +43,7 @@ object SnowstormParticleReader {
val emitterShapeEntityBoundingBoxJson = componentsJson.get("minecraft:emitter_shape_entity_aabb")?.asJsonObject
val emitterLifetimeEventsJson = componentsJson.get("minecraft:emitter_lifetime_events")?.asJsonObject
val dynamicMotionJson = componentsJson.get("minecraft:particle_motion_dynamic")?.asJsonObject
val parametricMotionJson = componentsJson.get("minecraft:particle_motion_parametric")?.asJsonObject
val particleAppearanceJson = componentsJson.get("minecraft:particle_appearance_billboard").asJsonObject
val sizeJson = particleAppearanceJson.get("size")?.asJsonArray
val particleLifetimeJson = componentsJson.get("minecraft:particle_lifetime_expression")?.asJsonObject
@@ -234,13 +235,22 @@ object SnowstormParticleReader {
val motion = if (dynamicMotionJson != null) {
val accelerationExpressions = dynamicMotionJson.get("linear_acceleration")?.asJsonArray?.map { it.asString.asExpression() }
?: listOf(0.0.asExpression(), 0.0.asExpression(), 0.0.asExpression())
val drag= dynamicMotionJson.get("linear_drag_coefficient")?.asString?.asExpression() ?: 0.0.asExpression()
val drag = dynamicMotionJson.get("linear_drag_coefficient")?.asString?.asExpression() ?: 0.0.asExpression()
DynamicParticleMotion(
direction = direction!!,
speed = speed,
acceleration = Triple(accelerationExpressions[0], accelerationExpressions[1], accelerationExpressions[2]),
drag = drag
)
} else if (parametricMotionJson != null) {
val offsetExpressions = parametricMotionJson.get("relative_position")?.asJsonArray?.map { it.asString.asExpression() }
?: listOf(0.0.asExpression(), 0.0.asExpression(), 0.0.asExpression())
val directionExpressions = parametricMotionJson.get("direction")?.asJsonArray?.map { it.asString.asExpression() }
?: listOf(0.0.asExpression(), 0.0.asExpression(), 0.0.asExpression())
ParametricParticleMotion(
offset = Triple(offsetExpressions[0], offsetExpressions[1], offsetExpressions[2]),
direction = Triple(directionExpressions[0], directionExpressions[1], directionExpressions[2])
)
} else {
StaticParticleMotion()
}
@@ -299,12 +309,19 @@ object SnowstormParticleReader {
textureSizeY = uvModeJson.get("texture_height")?.asInt ?: 128
)
}
val rotation = DynamicParticleRotation(
startRotation = startRotation,
speed = rotationSpeed,
acceleration = dynamicMotionJson?.get("rotation_acceleration")?.asString?.asExpression() ?: 0.0.asExpression(),
drag = dynamicMotionJson?.get("rotation_drag_coefficient")?.asString?.asExpression() ?: 0.0.asExpression()
)
val motionJson = dynamicMotionJson ?: parametricMotionJson
val parametricParticleRotation = motionJson?.get("rotation")?.asString?.asExpression()
val rotation = if (parametricParticleRotation != null) {
ParametricParticleRotation(expression = parametricParticleRotation)
} else {
DynamicParticleRotation(
startRotation = startRotation,
speed = rotationSpeed,
acceleration = dynamicMotionJson?.get("rotation_acceleration")?.asString?.asExpression() ?: 0.0.asExpression(),
drag = dynamicMotionJson?.get("rotation_drag_coefficient")?.asString?.asExpression() ?: 0.0.asExpression()
)
}
val tinting = if (colourJson is JsonObject) {
GradientParticleTinting(
interpolant = colourJson.get("interpolant").asString.asExpression(),
@@ -18,12 +18,12 @@ import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
@@ -14,12 +14,12 @@ import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalcula
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import kotlin.math.roundToInt
import kotlin.random.Random
import net.minecraft.entity.LivingEntity
@@ -12,12 +12,12 @@ import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculator
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import kotlin.math.max
import kotlin.math.roundToInt
import kotlin.random.Random
@@ -13,12 +13,12 @@ import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculator
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import kotlin.math.roundToInt
import kotlin.math.sqrt
import kotlin.random.Random
@@ -15,12 +15,12 @@ import com.cobblemon.mod.common.api.pokeball.catching.calculators.CriticalCaptur
import com.cobblemon.mod.common.api.pokeball.catching.calculators.PokedexProgressCaptureMultiplierProvider
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import kotlin.math.roundToInt
import kotlin.math.sqrt
import kotlin.random.Random
@@ -15,12 +15,12 @@ import com.cobblemon.mod.common.api.pokeball.catching.calculators.CriticalCaptur
import com.cobblemon.mod.common.api.pokeball.catching.calculators.PokedexProgressCaptureMultiplierProvider
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import kotlin.math.pow
import kotlin.math.roundToInt
import kotlin.random.Random
@@ -15,12 +15,12 @@ import com.cobblemon.mod.common.api.pokeball.catching.calculators.CriticalCaptur
import com.cobblemon.mod.common.api.pokeball.catching.calculators.PokedexProgressCaptureMultiplierProvider
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import kotlin.math.pow
import kotlin.math.roundToInt
import kotlin.random.Random
@@ -13,12 +13,12 @@ import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalcula
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CriticalCaptureProvider
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import kotlin.math.max
import kotlin.math.pow
import kotlin.math.roundToInt
@@ -14,12 +14,12 @@ import com.cobblemon.mod.common.api.pokeball.catching.calculators.CriticalCaptur
import com.cobblemon.mod.common.api.pokeball.catching.calculators.PokedexProgressCaptureMultiplierProvider
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import kotlin.math.max
import kotlin.math.pow
import kotlin.math.roundToInt
@@ -34,6 +34,7 @@ import com.cobblemon.mod.common.api.pokemon.experience.ExperienceSource
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeature
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.pokemon.feature.SynchronizedSpeciesFeature
import com.cobblemon.mod.common.api.pokemon.feature.SynchronizedSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.friendship.FriendshipMutationCalculator
import com.cobblemon.mod.common.api.pokemon.labels.CobblemonPokemonLabels
import com.cobblemon.mod.common.api.pokemon.moves.LearnsetQuery
@@ -472,7 +473,11 @@ open class Pokemon : ShowdownIdentifiable {
SeasonFeatureHandler.updateSeason(this, level, position.toBlockPos())
val entity = PokemonEntity(level, this)
illusion?.start(entity)
entity.setPositionSafely(position)
val sentOut = entity.setPositionSafely(position)
//If sendout failed, fall back
if (!sentOut) {
entity.setPos(position.x, position.y, position.z)
}
mutation(entity)
level.spawnEntity(entity)
state = SentOutState(entity)
@@ -1586,7 +1591,7 @@ open class Pokemon : ShowdownIdentifiable {
}
}
private val _form = SimpleObservable<FormData>()
private val _form = registerObservable(SimpleObservable<FormData>()) { FormUpdatePacket({ this }, it) }
private val _species = registerObservable(SimpleObservable<Species>()) { SpeciesUpdatePacket({ this }, it) }
private val _nickname = registerObservable(SimpleObservable<MutableText?>()) { NicknameUpdatePacket({ this }, it) }
private val _experience = registerObservable(SimpleObservable<Int>()) { ExperienceUpdatePacket({ this }, it) }
@@ -1614,7 +1619,8 @@ open class Pokemon : ShowdownIdentifiable {
private val _originalTrainerName = registerObservable(SimpleObservable<String?>()) { OriginalTrainerUpdatePacket({ this }, it) }
private val _features = registerObservable(SimpleObservable<SpeciesFeature>()) {
if (it is SynchronizedSpeciesFeature) {
val featureProvider = SpeciesFeatures.getFeature(it.name)
if (it is SynchronizedSpeciesFeature && featureProvider is SynchronizedSpeciesFeatureProvider && featureProvider.visible) {
SpeciesFeatureUpdatePacket({ this }, species.resourceIdentifier, it)
} else {
null
@@ -45,6 +45,7 @@ object CobblemonRequirementAdapter : RequirementAdapter {
this.registerType(WeatherRequirement.ADAPTER_VARIANT, WeatherRequirement::class)
this.registerType(StatCompareRequirement.ADAPTER_VARIANT, StatCompareRequirement::class)
this.registerType(StatEqualRequirement.ADAPTER_VARIANT, StatEqualRequirement::class)
this.registerType(AttackDefenceRatioRequirement.ADAPTER_VARIANT, AttackDefenceRatioRequirement::class)
this.registerType(BattleCriticalHitsRequirement.ADAPTER_VARIANT, BattleCriticalHitsRequirement::class)
this.registerType(DamageTakenRequirement.ADAPTER_VARIANT, DamageTakenRequirement::class)
this.registerType(UseMoveRequirement.ADAPTER_VARIANT, UseMoveRequirement::class)
@@ -0,0 +1,33 @@
/*
* 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.pokemon.evolution.requirements
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
import com.cobblemon.mod.common.pokemon.Pokemon
class AttackDefenceRatioRequirement : EvolutionRequirement {
companion object {
const val ADAPTER_VARIANT = "attack_defence_ratio"
}
enum class AttackDefenceRatio {
ATTACK_HIGHER,
DEFENCE_HIGHER,
EQUAL
}
val ratio = AttackDefenceRatio.ATTACK_HIGHER
override fun check(pokemon: Pokemon): Boolean {
return when (ratio) {
AttackDefenceRatio.ATTACK_HIGHER -> pokemon.attack > pokemon.defence
AttackDefenceRatio.DEFENCE_HIGHER -> pokemon.defence > pokemon.attack
AttackDefenceRatio.EQUAL -> pokemon.attack == pokemon.defence
}
}
}
@@ -31,6 +31,7 @@ import net.minecraft.world.World
open class BlockClickEvolution(
override val id: String,
override val result: PokemonProperties,
override val shedder: PokemonProperties?,
override val requiredContext: RegistryLikeCondition<Block>,
override var optional: Boolean,
override var consumeHeldItem: Boolean,
@@ -40,6 +41,7 @@ open class BlockClickEvolution(
constructor(): this(
id = "id",
result = PokemonProperties(),
shedder = null,
requiredContext = BlockIdentifierCondition(Identifier("minecraft", "dirt")),
optional = true,
consumeHeldItem = true,
@@ -19,6 +19,7 @@ internal class DummyEvolution : Evolution {
override val id = "dummy"
override val result: PokemonProperties = PokemonProperties()
override val shedder: PokemonProperties? = null
override var optional = false
override var consumeHeldItem = false
override val requirements: MutableSet<EvolutionRequirement> = mutableSetOf()
@@ -32,6 +32,7 @@ import net.minecraft.world.World
open class ItemInteractionEvolution(
override val id: String,
override val result: PokemonProperties,
override val shedder: PokemonProperties?,
override val requiredContext: NbtItemPredicate,
override var optional: Boolean,
override var consumeHeldItem: Boolean,
@@ -41,6 +42,7 @@ open class ItemInteractionEvolution(
constructor(): this(
id = "id",
result = PokemonProperties(),
shedder = null,
requiredContext = NbtItemPredicate(ItemIdentifierCondition(Identifier("minecraft", "fish")), NbtPredicate.ANY),
optional = true,
consumeHeldItem = true,
@@ -25,6 +25,7 @@ import com.cobblemon.mod.common.pokemon.Pokemon
open class LevelUpEvolution(
override val id: String,
override val result: PokemonProperties,
override val shedder: PokemonProperties?,
override var optional: Boolean,
override var consumeHeldItem: Boolean,
override val requirements: MutableSet<EvolutionRequirement>,
@@ -36,6 +37,7 @@ open class LevelUpEvolution(
constructor(): this(
id = "id",
result = PokemonProperties(),
shedder = null,
optional = true,
consumeHeldItem = true,
requirements = mutableSetOf(),
@@ -26,6 +26,7 @@ import com.cobblemon.mod.common.pokemon.Pokemon
open class TradeEvolution(
override val id: String,
override val result: PokemonProperties,
override val shedder: PokemonProperties?,
override val requiredContext: PokemonProperties,
override var optional: Boolean,
override var consumeHeldItem: Boolean,
@@ -35,6 +36,7 @@ open class TradeEvolution(
constructor(): this(
id = "id",
result = PokemonProperties(),
shedder = null,
requiredContext = PokemonProperties(),
optional = true,
consumeHeldItem = true,
@@ -16,4 +16,9 @@ import net.minecraft.util.Identifier
*
* @author Deltric
*/
class VolatileStatus(name: Identifier) : Status(name, "", "", "")
open class VolatileStatus(
name: Identifier,
showdownName: String,
applyMessage: String,
removeMessage: String,
) : Status(name, showdownName, applyMessage, removeMessage)
@@ -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.pokemon.status.statuses.nonpersistent
import com.cobblemon.mod.common.pokemon.status.VolatileStatus
import com.cobblemon.mod.common.util.cobblemonResource
class AttractStatus : VolatileStatus(
cobblemonResource("attract"),
"attract",
"cobblemon.battle.attract_start",
"cobblemon.battle.attract_snapped"
)
@@ -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.pokemon.status.statuses.nonpersistent
import com.cobblemon.mod.common.pokemon.status.VolatileStatus
import com.cobblemon.mod.common.util.cobblemonResource
class ConfuseStatus : VolatileStatus(
cobblemonResource("confused"),
"confusion",
"cobblemon.battle.confusion_start",
"cobblemon.battle.confusion_snapped"
)
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.pokemon.status.statuses
package com.cobblemon.mod.common.pokemon.status.statuses.persistent
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
import com.cobblemon.mod.common.util.cobblemonResource
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.pokemon.status.statuses
package com.cobblemon.mod.common.pokemon.status.statuses.persistent
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
import com.cobblemon.mod.common.util.cobblemonResource
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.pokemon.status.statuses
package com.cobblemon.mod.common.pokemon.status.statuses.persistent
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
import com.cobblemon.mod.common.util.cobblemonResource
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.pokemon.status.statuses
package com.cobblemon.mod.common.pokemon.status.statuses.persistent
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.status.PersistentStatus

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