Compare commits

...
Author SHA1 Message Date
Plastered_Crab 3fa0544c28 - adjusted logic to prepare for detecting user switches and to allow for better use of pivot moves 2024-07-01 11:51:52 -07:00
Plastered_Crab e2445db9c4 - adjusted logic 2024-06-21 18:07:35 -07:00
Plastered_Crab 9610332de5 - adjusted team list 2024-06-19 22:38:15 -07:00
Plastered_Crab 0c08a3c271 - adjusted team list 2024-06-19 22:23:56 -07:00
Plastered_Crab 7aafc377db - added test sun team, rain team, and comp team to fight against for testing 2024-06-19 22:17:17 -07:00
Plastered_Crab 91fad06986 - start of incorporating skill levels into the battle ai 2024-06-19 16:33:07 -07:00
Lily 6865654f37 started on move tags 2024-01-26 17:57:03 -05:00
jeffw773 17b3345abe - Merged in MoreHoldItems branch and adjusted teams 2024-01-10 16:49:32 -08:00
jeffw773 9c037dbad2 Merge remote-tracking branch 'origin/main' into better_battle_ai_test 2024-01-10 16:12:58 -08:00
jeffw773 8afbe3a3a7 Merge branch 'MoreHoldItems' into better_battle_ai_test
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/PokemonProperties.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/properties/PropertiesCompletionProvider.kt
2024-01-10 14:42:27 -08:00
jeffw773 916147ee0d - Battle AI should keep track of some volatiles more now 2024-01-08 14:30:12 -08:00
jeffw773 ad83d0e4a1 - Battle AI can now detect Type Changes
- Added hard "pivoter" NPC
2024-01-06 23:32:56 -08:00
apion 7c164fc4c8 Revert "Added BotanyPots integration"
This reverts commit 846acaef88.
2024-01-07 02:28:40 -05:00
Apion 2ccda57c66 Merge branch 'botanypots' into 'main'
Added BotanyPots integration

Closes #642

See merge request cable-mc/cobblemon!828
2024-01-06 15:17:50 +00:00
Greger 846acaef88 Added BotanyPots integration 2024-01-06 15:17:50 +00:00
Hiroku 5fa12ff1c5 Merge branch 'punch-apricorn' into 'main'
Punch apricorn

See merge request cable-mc/cobblemon!843
2024-01-06 01:11:53 +00:00
tmetcalfe89 2b3db32243 right, we should make sure it's proper age before harvesting 2024-01-05 15:20:34 -08:00
jeffw773 5a993f3d88 - Fixed Battle AI to be able to use status moves on opponents 2024-01-05 13:59:36 -08:00
tmetcalfe89 4b54405017 added ability to punch apricorn to harvest 2024-01-05 13:48:16 -08:00
Paul 3824531735 Made moveToPose able to function without an entity 2024-01-05 16:14:32 +10:00
Paul 63a30dcfd0 Merge remote-tracking branch 'origin/main' 2024-01-04 21:47:27 +10:00
Paul 9cc63b59e6 Fixed forge runs 2024-01-04 21:44:37 +10:00
Hiroku cb17c29be5 Merge branch 'squawkabilly_aspect' into 'main'
Fix Squawkabilly forms to use correct aspect

Closes #742

See merge request cable-mc/cobblemon!841
2024-01-04 11:28:02 +00:00
Kyozenno fd16d59ea2 Fix Squawkabilly forms to use correct aspect 2024-01-04 11:28:01 +00:00
JazzMcNade 373c383097 Fixed matching the activepokemon on both parties in the tracker party. Fixed added newly revealed pokemon to the tracker party. 2024-01-04 00:47:13 -06:00
jeffw773 00c095ba8d - Almost done with rework of ActiveTracker 2024-01-03 21:24:32 -08:00
dhi_holo e8236b55a4 Recipe Fixes plus Information in Description
There are 6 items left unimplemented from the recipe document.
Soothe Bell, due to being an out of battle effect, I don't want to attempt, and Amulet Coin needs design work before implementation.
The four terrain seeds, due to future plans, I'm not sure if need to be set up differently. Beyond those six held items, this branch has all currently requested held items to my knowledge, all should now work as intended, although I have not confirmed all battle dialogue works, in part due to not wanting to find all the documentations of what the lines should be. I might come back to that another day, though.

Commits marked with "No Obtain Method" refer to items which have obtain methods of loot or whatever, which can be handled later by people familiar with those systems. If not, I'm down to learn, just not a thing for today.
2024-01-04 13:52:21 +10:00
dhi_holo 2ea07899ea -Added Sticky Barb
No Obtain Method
2024-01-04 13:01:30 +10:00
dhi_holo d0ce03cd5e -Added Shed Shell
No Obtain Method
2024-01-04 13:00:02 +10:00
dhi_holo 8ae0c409a3 -Added Light Ball
No Obtain Method
2024-01-04 12:58:15 +10:00
dhi_holo 2782cb5f34 -Added Lagging Tail
No Obtain Method
2024-01-04 12:56:09 +10:00
dhi_holo 2f10707211 -Added Grip Claw
No Obtain Method
2024-01-04 12:54:48 +10:00
dhi_holo f40a9d762a -Added Float Stone
No Obtain Method
2024-01-04 12:53:17 +10:00
dhi_holo 17587f97e5 -Added Absorb Bulb
No Obtain Method
2024-01-04 12:51:46 +10:00
dhi_holo 5230820657 -Added Punching Glove 2024-01-04 12:49:01 +10:00
dhi_holo 61c55e136f -Added Throat Spray
need to figure out how to add the potion brewing later, not a huge priority at this stage
2024-01-04 12:45:59 +10:00
dhi_holo 22cced74f5 -Added Luminous Moss 2024-01-04 12:43:40 +10:00
dhi_holo 3cd2dec8e0 -Added Loaded Dice 2024-01-04 12:40:34 +10:00
dhi_holo cea930893a -Added Iron Ball 2024-01-04 10:26:54 +10:00
dhi_holo 8662364ca8 -Added Wide Lens and Zoom Lens 2024-01-04 10:24:00 +10:00
dhi_holo 1fd079b1b4 -Added Weather Rocks
Damp Rock, Heat Rock, Smooth Rock, Icy Rock, terrible items
2024-01-04 10:19:53 +10:00
dhi_holo 244876a26c -Added Weakness Policy 2024-01-04 10:11:52 +10:00
dhi_holo e7790f530f -Added Utility Umbrella 2024-01-04 10:08:56 +10:00
dhi_holo 04a9ab0e67 -Adds Terrain Extender
If someone wants to work out how to make it so we can have a recipe that costs a water bucket, rather than water from a bucket, that'd be good, for now the recipe just costs an empty bucket
2024-01-04 10:06:39 +10:00
dhi_holo 3f338d27c3 -Added Scope Lens 2024-01-04 09:55:46 +10:00
dhi_holo 5da3dfb451 -Added Room Service 2024-01-04 09:54:00 +10:00
dhi_holo fd329e00cd -Added Ring Target 2024-01-04 09:52:00 +10:00
dhi_holo 6da47a8b2c -Added Red Card
Not gonna test this one for now, don't feel like setting up a second instance test. But, given Eject Button works, I'd say decent shot this one also works
2024-01-04 09:49:02 +10:00
dhi_holo 860c73fac9 -Added Protective Pads 2024-01-04 09:45:50 +10:00
dhi_holo e26fdd4bdd -Added Metronome
Deciding I'll ignore all held items with a * next to the name for now, not sure what they mean but hey, makes it easier to work through, and I can come back to them afterwards
2024-01-04 09:42:31 +10:00
dhi_holo 8e642f71b8 Merge branch 'main' into MoreHoldItems 2024-01-04 09:32:19 +10:00
Apion 2d6c7f5148 Merge branch 'dialogue' into 'main'
Dialogue

See merge request cable-mc/cobblemon!834
2024-01-03 12:20:39 +00:00
Paul 7a761003c5 Merge remote-tracking branch 'origin/main' into dialogue
# Conflicts:
#	CHANGELOG.md
2024-01-03 19:07:51 +10:00
Paul f9fa9dcf24 Added ServerRealTimeTaskTracker for server-sided epoch scheduler and use it for dialogue 2024-01-03 19:07:18 +10:00
Hiroku dfc4873ce3 Merge branch 'open-itemdropentry' into 'main'
Opens up the ItemDropEntry class, so it can be extended.

See merge request cable-mc/cobblemon!836
2024-01-03 08:46:07 +00:00
dhi_holo f17884b4e1 Eject Button Lang, Read Description
All of the items added just now by me were tested besides Eviolite (don't feel like crunching those numbers today), Eject Pack is broken in my testing (see https://canary.discord.com/channels/725110639554592879/993671151870017656/1192009424282320957 for details), and ofc no item sprites yet, but that's dev life
2024-01-03 17:53:57 +10:00
Nick 65bd00033c Merge branch 'berry-rendering' into 'main'
Improve Berry Rendering Perf

See merge request cable-mc/cobblemon!838
2024-01-03 07:35:28 +00:00
Apion f82fa2b4e6 Improve Berry Rendering Perf 2024-01-03 07:35:27 +00:00
dhi_holo c40b456948 -Added Eviolite and Expert Belt
test and finalise this little bit time (also corrected some jsons)
2024-01-03 17:19:31 +10:00
dhi_holo 7e74e85315 -Added Eject Button and Pack
I'm not testing these in game rn, I'll do that at the end of the session, which will probably be in a couple more items. But, these all hit the internet at the same time, so haha
2024-01-03 17:09:13 +10:00
dhi_holo 21fd4d7cbf -Added Covert Cloak 2024-01-03 17:00:58 +10:00
dhi_holo 908bff9892 -Added Cell Battery and Clear Amulet 2024-01-03 16:57:32 +10:00
Nick 966acecf6d Merge branch 'Fetus_Updates' into 'main'
Fix for Fossil Block Renderers and Fetus Asset Updates

See merge request cable-mc/cobblemon!839
2024-01-03 06:31:05 +00:00
dhi_holo fa14f70bbb -Added Shell Bell
Bell Go Heal
2024-01-03 15:59:01 +10:00
JazzMcNade 2cd09af4d0 Added all remaining fetus models for the fossil machine, updated existing fetus assets. Reregistered fossil block entity renderers. 2024-01-02 23:51:50 -06:00
jeffw773 610ea0f30c - Added Sandstorm Team to battle that has stall, hazards, removal 2024-01-02 15:13:33 -08:00
jeffw773 a9c92d72d0 - NPC team for Red was buffed and given hazard removal
- AI can now do hazard removal properly (was broken)
2024-01-02 12:22:53 -08:00
jeffw773 25cc5f526b Merge remote-tracking branch 'origin/better_battle_ai_test' into better_battle_ai_test 2024-01-01 23:37:17 -08:00
jeffw773 3cb4f938f1 - AI can now use Hazards, Clear Hazards, and Status Moves
- Added trickroom NPC Team
2024-01-01 23:37:00 -08:00
Deltric 388e03e261 Opens up the ItemDropEntry class, so it can be extended. 2024-01-02 01:12:48 -06:00
dhi_holo 346559f292 Tags on top of Tags
Added a very large amount of tags.
The different tiers of Poké balls, also the Apricorn balls so it's easier for people to see what balls count for that advancement, different healing and battle item types, some evolution item sets which made sense to make tags (may add more later, these ones were just the obvious ones to make thing easier for future), the three colours for tumblestones, machine blocks, as well as adding the fence gate item tag, and adding the new tumblestone stuff to the pickaxe tag (although they can still be mined with an axe, probably something that needs fixing)

Mod loader tags were not tested on Forge, due to a crash involving CobblemonEntities, but Fabric works all nicely
2024-01-02 14:44:01 +10:00
dhi_holo 331b282fcf Decorated sherds tag, and fixed Focus Sash not being named
`focus_Sash` isn't valid, sadly
2024-01-02 12:41:28 +10:00
JazzMcNade a876666bc7 -Fixed an issue in which a pokemon with less than 4 moves would not allow its currently populated moveslots to be overwritten. 2024-01-01 20:31:24 -06:00
dhi_holo 7f97eeafed Update is_held_item.json
Reorganized alphabetically, for the most part, roughly matches the registry now.
2024-01-02 11:45:38 +10:00
dhi_holo 51f5a1f5a8 Merge branch 'main' into MoreHoldItems 2024-01-02 11:35:53 +10:00
Paul 1829f7d48a Renamed FlatfileMoLangDataStoreFactory.kt to NbtMoLangDataStoreFactory.kt 2024-01-01 22:44:30 +10:00
Hiroku bcc3c885d7 Merge branch 'holographic-cleaners-inc' into 'main'
Tags on Tags (plus a couple tiny fixes)

Closes #736 and #706

See merge request cable-mc/cobblemon!835
2024-01-01 12:40:09 +00:00
Paul e2a6dbc709 Undid temp example thingy dw about it 2024-01-01 22:13:15 +10:00
Paul b32ca4af5d Added autocompletion to /opendialogue 2024-01-01 22:11:58 +10:00
Paul a2e728905e Clarified PlayerRenderableFace 2024-01-01 21:47:07 +10:00
dhi_holo 7c0535b8bd Merge branch 'main' into MoreHoldItems 2024-01-01 21:08:33 +10:00
dhi_holo ead232be6f Merge branch 'main' into holographic-cleaners-inc 2024-01-01 19:59:44 +10:00
dhi_holo feaba85ba0 Update CHANGELOG.md
improved a description slightly
2024-01-01 19:52:26 +10:00
dhi_holo 6a8822ca76 Undone what was done before
Given it exists in another branch, and this was just me learning anyway, removed the ore from this branch.
2024-01-01 19:50:36 +10:00
dhi_holo 4f15ac0cbc Update CobblemonItems.kt
No More "why are the held items not in alphabetical order" for the time being (besides the three Powders and four Herbs, I left them together at the bottom because nobody cares about them)
2024-01-01 18:37:53 +10:00
Paul 8fc6d93be4 Merge remote-tracking branch 'origin/main' into dialogue
# Conflicts:
#	CHANGELOG.md
2024-01-01 18:22:33 +10:00
Paul 97f8dc562a Oopsy 2024-01-01 18:20:08 +10:00
JazzMcNade 35a0d8a80d -Added Blunder Policy 2024-01-01 01:31:08 -06:00
Hiroku 6616cd1d4c Merge branch 'moe-minor-fixes' into 'main'
empty evolutions tag cleanup and related fixes

See merge request cable-mc/cobblemon!832
2024-01-01 07:08:00 +00:00
Paul f9635cf03f Merge remote-tracking branch 'origin/main' into dialogue
# Conflicts:
#	CHANGELOG.md
2024-01-01 16:52:41 +10:00
Hiroku 49db87737f Merge branch 'original-trainer' into 'main'
Original Trainer Implementation

See merge request cable-mc/cobblemon!831
2024-01-01 06:51:09 +00:00
Hiroku a414213255 Merge branch 'main' into 'original-trainer'
# Conflicts:
#   CHANGELOG.md
#   common/src/main/kotlin/com/cobblemon/mod/common/util/MiscUtils.kt
2024-01-01 06:44:21 +00:00
Paul 59a8c21429 Merge remote-tracking branch 'origin/main' into dialogue
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/data/CobblemonDataProvider.kt
2024-01-01 16:27:58 +10:00
Paul b67fda7a37 changelog for dialogue 2024-01-01 16:27:25 +10:00
Hiroku 4392a61ab5 Merge branch 'archaeology' into 'main'
Archeology

See merge request cable-mc/cobblemon!833
2024-01-01 06:26:13 +00:00
Hiroku a9d96818ec Removed duplicated block entity renderer registration 2024-01-01 06:24:35 +00:00
Paul 6b285e48e9 Added faces to dialogue API 2024-01-01 16:20:46 +10:00
JazzMcNade c0270d3ef0 -Added Binding Band 2023-12-31 23:58:24 -06:00
JazzMcNade 82a1eb2a55 -Finished Air Balloon implementation. Fixed white space on recipes for for focus sash and Ability Shield 2023-12-31 21:44:26 -06:00
JazzMcNade a528ea952a -Added Ability Shield and Focus Sash. Begin work on Air Balloon. 2023-12-31 21:20:10 -06:00
JazzMcNade 592b40cd12 -Added temporary party store for cloned parties. Allows for proper faint behavior for clone team. 2023-12-31 18:49:45 -06:00
MoeBoy76 532ef09acd updates for recent transformed parts update 2024-01-01 08:14:21 +11:00
JazzMcNade 9e05cfa385 -Set simulation flag for clone battles 2023-12-31 14:22:47 -06:00
MoeBoy76 f0f7bb96ad Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into archaeology
# Conflicts:
#	CHANGELOG.md
2024-01-01 07:10:17 +11:00
Paul ecfb6c60c3 Added dialogue API 2023-12-30 21:55:35 +10:00
jeffw773 36f13f5b77 - Added Red, Blue, Volo, Evice to AI Battle pool
- AI can now use Trick Room
- Tweaked switchin considerations more to account for speed more
2023-12-30 02:05:45 -08:00
jeffw773 141a88ba13 - Cynthia Battle NPC tweaked + AI now doesn't constantly switch out
- Recovery moves consideration tweaked from .3 to .45
- Disabled moves now will not be considered
2023-12-29 19:50:16 -08:00
dhi_holo 5be436f25c Merge branch 'main' into holographic-cleaners-inc 2023-12-29 18:55:23 +10:00
dhi_holo 2ddc265b0f Destiny Tears
Updated Destiny Knot to use a ghast tear
2023-12-29 18:54:26 +10:00
jeffw773 fc269e853c - Cynthia Battle AI is now stronger and hungers for battle
- Moves that must be used don't crash the battle for the Battle AI now
2023-12-29 00:21:28 -08:00
jeffw773 df44486eee - Added less chance of the strong AI being cheesed by a bad Ability 2023-12-28 22:29:13 -08:00
jeffw773 d7881a327a Merge remote-tracking branch 'origin/better_battle_ai_test' into better_battle_ai_test 2023-12-28 22:19:55 -08:00
jeffw773 6ff719d2ec - Made Battle AI able to know if it was given a bad abiltiy and will switch out 2023-12-28 22:19:26 -08:00
JazzMcNade 088fd65159 -Fixed cloning pokemon not capturing the current HP correctly 2023-12-29 00:06:38 -06:00
jeffw773 3a2d4028a5 - Cynthia battle NPC can be set to be any level (VERY HARD) 2023-12-28 12:37:12 -08:00
jeffw773 a6a9dce811 Merge remote-tracking branch 'origin/1.4.1' into better_battle_ai_test 2023-12-28 11:54:21 -08:00
jeffw773 55f0a392e9 - Added Cynthia (with team) from BDSP to NPC battle list
- Added ability to specify AI type before NPC battle
- Allow for different orders of commands for /randombattle
2023-12-28 11:53:49 -08:00
MoeBoy76 52af8ceb99 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into moe-minor-fixes
# Conflicts:
#	CHANGELOG.md
2023-12-28 23:15:51 +11:00
Hiroku 14a5060100 Merge branch 'dispatch-refactor' into 'main'
Refactored battle interpretor to load into a sequence before starting dispatches

See merge request cable-mc/cobblemon!830
2023-12-28 10:55:22 +00:00
Hiroku 9d33586b44 Merge branch 'improved-transformed-parts' into 'main'
Rebuilt transformed model parts

See merge request cable-mc/cobblemon!829
2023-12-28 10:55:09 +00:00
MoeBoy76 d4f328157e Update file PokemonBattle.kt 2023-12-27 23:35:49 +00:00
MoeBoy76 c4331043f7 changelog file that was missed in selection 2023-12-28 10:05:09 +11:00
MoeBoy76 9945d8a069 cleanup check and fix for empty evolutions in species files 2023-12-28 10:03:39 +11:00
dhi_holo 2595d6ee91 Tags for Ore!
Adds block and item tags for each evolution store ore type, as well as adding the ores to the modloader tags properly, making it match with how ores are handled regularly.
Also boat tags.
2023-12-28 06:04:51 +10:00
dhi_holo f16e024bbe I don't get it
Readded the Iron recipe. No clue why it disappeared, just, isn't a file for it suddenly.
Closes #736
2023-12-28 05:29:05 +10:00
Charzard4261 87a5522acc Update CHANGELOG.md 2023-12-27 16:10:14 +00:00
Charzard4261 2c812cdc3c Change system to explicitly declare if an Original Trainer is NONE, PLAYER or NPC through the use of OriginalTrainerType 2023-12-27 16:06:52 +00:00
jeffw773 fb3b2a9452 Merge branch 'main' into better_battle_ai_test
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/subscreen/BattleGeneralActionSelection.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
2023-12-27 08:01:21 -08:00
dhi_holo f6e910038e Update beast_ball.png
Replaces `assets\cobblemon\textures\item\poke_balls\models\beast_ball.png` with `assets\cobblemon\textures\poke_balls\beast_ball.png`, so they match, as only one was updated.
Resolves #706
2023-12-27 18:55:52 +10:00
dhi_holo 5c7d96897d Generation Sucks, Please Help
Added basically everything for Netherrack Fire Stone Ore, but the generation doesn't work and I'm not sure why. Feel free to investigate it, won't merge until it's solved.
2023-12-27 18:50:53 +10:00
jeffw773 dff934b308 - Added better considerations for meaningful switch-ins mid battle 2023-12-26 23:31:28 -08:00
dhi_holo 862d892c84 More EMI Compat
Added the new tags to the lang file, so they look nicer with mods such as EMI
2023-12-27 15:31:50 +10:00
dhi_holo 9246e1e8ee Held Tags
Should add all missed held items to the held item tag, as well as adding Metal Coat to the Held Items tab, and moving Power Herb to after the six Power Items because sure, alphabetical is nice, but it's a set, let's let it sit together xD
2023-12-27 14:23:43 +10:00
jeffw773 0de555610a - Stronger Battle AI test will send in the best matchup after faint
- Also tweaked Tailwind considerations and healing considerations
2023-12-26 13:42:33 -08:00
Paul 0715cb465f Fixed faints with loop set to true causing super whacky animations 2023-12-26 13:30:22 +10:00
Paul 2e251972aa Merge branch 'main' into improved-transformed-parts 2023-12-26 12:48:22 +10:00
Charzard4261 7a1f96e31e Add missing OTPlayer Username -> UUID lookup in PokemonProperties.apply(PokemonEntity) 2023-12-25 21:07:50 +00:00
Charzard4261 d405b099b3 Added the ability to modify the Original Trainer of a Pokémon through Commands 2023-12-25 21:04:42 +00:00
Hiroku 74322e2f48 Turn snapshot back on 2023-12-25 11:13:48 +00:00
Paul 40a37fedd2 Actually maybe don't try tracking causality yet 2023-12-25 20:28:47 +10:00
Paul 3bc6c50d79 Don't use deprecated scheduling 2023-12-25 20:19:13 +10:00
Paul 1ef9f44a1b Merge remote-tracking branch 'origin/main' into dispatch-refactor
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/ShowdownInterpreter.kt
2023-12-25 20:18:43 +10:00
MoeBoy76 f1ed339d9d update for 1.4.1 2023-12-25 11:26:26 +11:00
Paul f07930ef16 Just a little AI activity access widener 2023-12-25 09:53:11 +10:00
jeffw773 2e213e0472 - Started fixing Battle AI's switch in/out logic 2023-12-24 00:04:54 -08:00
Paul c6d18cda44 Fixed a fringe flickering issue with pose transitioning 2023-12-24 14:21:27 +10:00
Paul 263b86e833 Merge remote-tracking branch 'origin/main' into improved-transformed-parts
# Conflicts:
#	CHANGELOG.md
#	common/src/main/kotlin/com/cobblemon/mod/common/client/entity/PokemonClientDelegate.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/PoseableEntityState.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
2023-12-24 10:45:12 +10:00
Paul 953560051d Merge conflict corrections 2023-12-24 10:26:12 +10:00
Paul bf3904a214 Merge remote-tracking branch 'origin/main' into tracked-data-cleanup
# Conflicts:
#	CHANGELOG.md
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/BattleBuilder.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/entity/PokemonPastureBlockEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/entity/PokemonClientDelegate.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokeball/EmptyPokeBallEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonServerDelegate.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/evolution/requirements/BattleCriticalHitsRequirement.kt
2023-12-24 10:04:03 +10:00
Hiroku 1708eb9b3e Merge branch 'scheduling-rework' into 'main'
Rebuilt scheduling mechanism to be better contained and avoid side issues

See merge request cable-mc/cobblemon!793
2023-12-23 23:15:25 +00:00
Hiroku 0dee18dbfd Merge branch 'spawn-rules' into 'main'
Spawn rules

See merge request cable-mc/cobblemon!825
2023-12-23 23:15:14 +00:00
jeffw773 eeb4000b71 - fixed weather conditions causing null issue for battle AI 2023-12-23 14:40:31 -08:00
JazzMcNade 2fbf3100c5 -Added more status immunity and effect info to the AI 2023-12-23 16:33:52 -06:00
Paul 7ee7aaab76 Licenses 2023-12-23 21:18:58 +10:00
Paul 3dd4f515dd Squeezed OT into a single value pair 2023-12-23 21:00:16 +10:00
Paul f24310d043 Merge remote-tracking branch 'origin/main' into original-trainer 2023-12-23 20:38:48 +10:00
Paul 61bfe02322 Merge branch 'spawn-rules' into scheduling-rework 2023-12-23 20:17:48 +10:00
Paul 7cbcb30aff Add standard functions from a .setup() on MoLangRuntime 2023-12-23 15:21:59 +10:00
Paul 6fc8fa5b60 Wait no that's not where general functions are meant to go 2023-12-23 15:19:34 +10:00
Paul 7763a6d3e7 Added standard functions to registry entry molang values 2023-12-23 15:18:40 +10:00
Paul 1288469172 Some fixes for spawn rules and include real example 2023-12-23 15:06:42 +10:00
Paul 86cf79bff9 Merge remote-tracking branch 'origin/main' into spawn-rules 2023-12-23 14:10:21 +10:00
Paul 765e9f2cc3 snapshot = false 2023-12-23 11:39:57 +10:00
Paul ffda34742c Edge case protection with nickname migration 2023-12-23 11:01:03 +10:00
Paul c2385edbe7 more more changelog [skip-ci] 2023-12-23 11:00:00 +10:00
Paul 961ad0f4bd more changelog [skip-ci] 2023-12-23 10:45:07 +10:00
Paul 8851594347 Changelog polishing and removing stacks of unused item files 2023-12-23 10:44:32 +10:00
Paul 3c1ad2bea6 Add pokeball vivillon variant back into advancement 2023-12-23 09:10:17 +10:00
Hiroku e1960792d7 Merge branch 'bush_pathing' into 'main'
Berry bush avoidance

See merge request cable-mc/cobblemon!824
2023-12-22 22:50:24 +00:00
gr3ger 23b9f933c7 Updated changelog. 2023-12-22 23:37:27 +01:00
Paul 9662699bcb Fix mongo adapter constructor issue 2023-12-23 08:29:54 +10:00
Hiroku 32b2d2023b Merge branch 'nickname-migrate' into 'main'
Adds json nickname migrate + bug fix for non player entity backed battle actors.

See merge request cable-mc/cobblemon!821
2023-12-22 22:24:03 +00:00
Hiroku c64612859c Merge branch '732_spelling' into 'main'
Fix wrong name on animation/model folders

Closes #732

See merge request cable-mc/cobblemon!822
2023-12-22 22:17:12 +00:00
gr3ger 6c97c4c238 Removed extra call to getBlockState() and made function return on match instead of going through all if statements. 2023-12-22 23:11:58 +01:00
Lily 8c6f7335a5 bring changelog up to date. pls keep updating this from now on!! 2023-12-22 19:25:43 +00:00
gr3ger 6535d43964 PoC bush avoidance 2023-12-22 18:16:21 +01:00
gr3ger 234d1dad44 Closes #732
Tested spawning entities after changes, nothing seems out of the ordinary.
2023-12-22 10:12:47 +01:00
Deltric da247e9902 Adds json nickname migrate + bug fix for non player entity backed battle actors. 2023-12-22 01:47:19 -06:00
Paul c747d089ca More reworking of dispatches and instructions 2023-12-22 13:50:01 +10:00
Paul 8c881ab85a Merge branch 'main' into dispatch-refactor 2023-12-22 13:25:42 +10:00
Paul 0655d63dc5 Removed a couple of recipe advancements that are for items not ingame 2023-12-22 12:06:23 +10:00
Paul 81ba5ef615 Removed errant "light powder" item 2023-12-22 10:53:59 +10:00
Paul 8354c3838a Merge remote-tracking branch 'origin/main' 2023-12-22 10:10:12 +10:00
Paul 80db305aba Updated heaps of langs from crowdin 2023-12-22 10:10:00 +10:00
jeffw773 802bf10c6e - fixed switching out for the random NPC battle actor 2023-12-21 15:40:18 -08:00
jeffw773 42f442233a - Added Command to start random NPC battle with random party to use for testing AI 2023-12-21 14:30:35 -08:00
jeffw773 addd063406 - Adjusted how Strong Battle AI calculates what damaging moves to use (based on mainline game battle damage calculations) 2023-12-21 00:06:38 -08:00
jeffw773 bae01e7143 - Strong Battle AI is working! It hungers for the blood of the innocent. Still going to be worked on as time goes on though 2023-12-20 21:34:31 -08:00
apion ec43a78ae4 Merge branch 'main' into archaeology 2023-12-20 22:48:50 -05:00
jeffw773 fd0e2fa43f - Only 2 main errors left before testing can begin 2023-12-19 22:47:25 -08:00
Hiroku 60e0314f29 Merge branch 'Spawn_Level_Scaling' into 'main'
Level scaling tweak

See merge request cable-mc/cobblemon!820
2023-12-19 22:14:21 +00:00
Nick 5f868630e0 Merge branch 'neo-bounties' into 'main'
:)

See merge request cable-mc/cobblemon!818
2023-12-19 20:46:10 +00:00
MoeBoy76 762a53923c license fix 2023-12-19 13:36:16 +11:00
MoeBoy76 1cf03d4a0d Merge branch 'tumblestones' into 'archaeology'
move ancient pokeballs to archaeology branch

See merge request cable-mc/cobblemon!819
2023-12-19 01:56:23 +00:00
Lily 48f7be485f move ancient pokeballs to archaeology branch 2023-12-19 01:56:22 +00:00
MoeBoy76 89364a9fd0 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into archaeology 2023-12-19 12:51:12 +11:00
MoeBoy76 b93adae945 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into Spawn_Level_Scaling
# Conflicts:
#	CHANGELOG.md
2023-12-19 12:49:57 +11:00
MoeBoy76 9f1ed344d8 Changelog 2023-12-19 12:46:48 +11:00
MoeBoy76 2c256eefde Correct logic and cleanup unused test code 2023-12-19 12:27:41 +11:00
Neovitalism b075fce83f :)
#725 - Adds a boolean property to PokemonEntity to make it not count towards the spawn cap.
#728 - Makes PokemonEntity respect minecraft's silent flag.
2023-12-18 15:10:19 -06:00
Paul f2ed332c4c Fixed minor error in lang 2023-12-19 05:51:00 +10:00
Paul e8f083d8bf Merge remote-tracking branch 'origin/main' 2023-12-19 05:50:38 +10:00
Paul 43d965f2a1 Further reworks of dispatch refactor 2023-12-19 05:50:11 +10:00
Hiroku 1684a341a1 Merge branch 'Swim_Behavior_Fix' into 'main'
Fixed Swim Behaviors for some Pokemon

See merge request cable-mc/cobblemon!817
2023-12-18 19:40:34 +00:00
Hiroku b4e1bf794d Merge branch 'Pokemon_Weight_Watchers' into 'main'
Changed calculations of Pokemon's weight and height before it is sent to Showdown so that battle damage for weight-based moves are accurate

See merge request cable-mc/cobblemon!816
2023-12-18 19:40:22 +00:00
jeffw773 c85ad7d60c - Changelog 2023-12-18 11:28:53 -08:00
jeffw773 e61e92a5d4 - Changelog 2023-12-18 11:27:08 -08:00
jeffw773 a245e9661c - final tweaks for MR 2023-12-18 11:01:35 -08:00
jeffw773 059152b34c - removed unused test code 2023-12-18 10:58:59 -08:00
jeffw773 76260fb998 - removed unused test code 2023-12-18 10:52:01 -08:00
Lily e4a698b04b subtitles for fossil machine sounds 2023-12-18 18:48:08 +00:00
jeffw773 0d35157fcf - removed unused test code 2023-12-18 10:42:43 -08:00
jeffw773 302388d0a6 - Added Swim behaviors to Samurott line, Clodsire, Wimpod, Golisopod, Quaxwell, Quaquaval, 2023-12-18 10:30:07 -08:00
jeffw773 3aa45d83b6 - Made additional changes to the test StrongBattleAI file. Fixed Obstruct spelling and begun converting over the functions to take in battle and request. Still more left to do. 2023-12-18 09:51:09 -08:00
jeffw773 ab957f82e1 - Altered some code for how Pokemon's height and weight is calculated for weight-based moves during battles as it had issues and was doing more damage than it should have been 2023-12-17 20:18:41 -08:00
jeffw773 fef82ae6bf - Altered some code for how Pokemon's height and weight is calculated for weight-based moves during battles as it had issues and was doing more damage than it should have been 2023-12-17 20:16:06 -08:00
jeffw773 8b979c1944 - Altered some code for how Pokemon's height and weight is calculated for weight-based moves during battles as it had issues and was doing more damage than it should have been 2023-12-17 19:52:32 -08:00
Paul 4bbbaacacf Merge remote-tracking branch 'origin/main' 2023-12-18 08:34:34 +10:00
jeffw773 eda8e15509 Merge remote-tracking branch 'origin/main' 2023-12-17 10:03:21 -08:00
Nick d53a71c188 Merge branch '1.4.1-fixes' into 'main'
1.4.1 fixes

See merge request cable-mc/cobblemon!814
2023-12-17 01:16:44 +00:00
Hiroku d2f02b0d06 1.4.1 fixes 2023-12-17 01:16:44 +00:00
Paul 5c0bb194b6 Fixed Tyrogue fainting into the floor 2023-12-17 10:50:08 +10:00
Paul e41562f5ad Fixed status apply fail messages for when they're already afflicted by that status 2023-12-17 10:49:59 +10:00
Paul 69ea1ac3bf Fixed things like Keen Eye not having translation stuff 2023-12-17 10:29:19 +10:00
Paul 7c15f0f924 Fixed confusing poison heal messages 2023-12-17 09:40:32 +10:00
Paul 6165e5e871 Corrected mistaken changelog entry 2023-12-17 09:19:41 +10:00
Paul dd8f59e0d0 Removed pink bow recipe as not needed anymore 2023-12-17 09:11:56 +10:00
Paul 87611795e9 Fixed battle messages getting out of sync after spectating, unspectating, re-spectating 2023-12-17 09:11:18 +10:00
Paul d2fe9b1132 Fixed Life Orb not having a proper item name 2023-12-17 08:57:26 +10:00
Hiroku e95759a710 Merge branch 'whatsy/bugfixes' into 'main'
Various bugfixes for 1.4.1

Closes #722 and #724

See merge request cable-mc/cobblemon!805
2023-12-16 21:50:39 +00:00
Hiroku 7284f8d35d Merge branch 'can-moe-scrape' into 'main'
Teal Mask species data

See merge request cable-mc/cobblemon!806
2023-12-16 21:24:07 +00:00
Hiroku 334dd1206e Merge branch 'share-the-explove' into 'main'
Balanced EXP Share Recipe

Closes #727

See merge request cable-mc/cobblemon!811
2023-12-16 21:23:32 +00:00
Hiroku e7caab0ae0 Merge branch 'Waldleufer/spawnCSVScriptPolishing' into 'main'
minor spawn CSV Script improvements and pokemon.json fix

See merge request cable-mc/cobblemon!812
2023-12-16 21:22:58 +00:00
Waldleufer 47f02f6e50 Restored Script default settings 2023-12-16 11:42:31 +01:00
Waldleufer 3367b0c39c dissolved bug in script
that would occur if the filled out Multiplier column for one pokemon was followed by an empty field
2023-12-16 11:40:47 +01:00
Waldleufer 0e43d7730c verified omanyte, omastar kabuto and kabutops json 2023-12-16 11:07:06 +01:00
Waldleufer b5604686f2 fix: Updated all minecraft:nether_quartz to minecraft:quartz 2023-12-16 11:05:00 +01:00
Waldleufer e030dbfbf5 refactor: Made the script report more readable. 2023-12-16 11:03:56 +01:00
jeffw773 2eb6062b9c - Beginning test of a smarter battle AI for Wild and NPC battles 2023-12-15 23:48:09 -08:00
Lily 96647a9398 fix conflict in changelog 2023-12-15 19:09:58 +00:00
Charzard4261 33cfbf7d1c First implementation of storing OT UUID and Name. Updates for Starter Selection, Catching, and Shedninja creation. Needs thorough reviewing. 2023-12-13 22:30:42 +00:00
dhi_holo 9e2c0ab0db Balanced EXP Share Recipe
Closes #727
2023-12-14 06:48:43 +10:00
Lily 1bc953f3f3 lang update 2023-12-12 14:40:06 +00:00
jeffw773 d089eab60f - Start of a tweak to how spawns work in terms of scaling the level of Pokémon based off of player party highest level 2023-12-11 17:19:19 -08:00
JazzMcNade 09da392c36 Added quirk support for tank fetuses, added basic posers and variation jsons for a bunch of fossil mons 2023-12-11 00:10:28 -06:00
Hiroku d18cab39a9 Merge branch 'kkeenanwong-main-patch-46161' into 'main'
Update garganacl.json quartz item ID

See merge request cable-mc/cobblemon!808
2023-12-11 03:21:09 +00:00
Keenan Wong 4492666f5a Update garganacl.json quartz item ID 2023-12-11 03:04:33 +00:00
Paul 6ddf048984 Started rework of battle dispatch construction for more context-aware interpreting 2023-12-11 10:15:18 +10:00
Hiroku 6a0985958b Merge branch 'reknot' into 'main'
Knot Flip

See merge request cable-mc/cobblemon!807
2023-12-10 23:37:43 +00:00
dhi_holo 308c5e6d32 Knot Flip
Destiny Knot recipe has been vertically flipped, to closer match the sprite.
2023-12-11 09:15:40 +10:00
MoeBoy76 bd83cc143b redo broken é 2023-12-09 23:43:12 +11:00
MoeBoy76 c9e6cb1022 fix line separator 2023-12-09 23:40:43 +11:00
MoeBoy76 74c75b7faa Applin evo data 2023-12-09 23:31:52 +11:00
MoeBoy76 45819a17f6 Teal Mask new data 2023-12-09 22:36:32 +11:00
Lily e3f84248b8 Added StructureLikeCondition.kt and a new StructureRequirement for evolutions. Closes #722 2023-12-05 19:35:30 -05:00
Lily 0ff518194f Fix grammar errors on certain pokeballs. made small fixes to Vivillon evolutions. Closes #724 2023-12-05 18:42:58 -05:00
Lily 634a1ad6fc Fixed Inkay evolution, fixed conflicting biome requirements for ocean, river, sun, and tundra vivillion variants. Closes #724 2023-12-05 16:58:39 +00:00
Lily 41217ebdcc Remove poke ball variant from the Vivillionaire advancement 2023-12-05 16:42:05 +00:00
JazzMcNade f396b63a34 Added FossilRegistrySyncPacket
Multiplayer clients can now see fossil tank fetuses
2023-12-05 01:17:44 -06:00
Hiroku a021ec1337 Merge branch 'sound' into 'main'
Fixed missing cries again

See merge request cable-mc/cobblemon!803
2023-12-04 20:30:00 +00:00
Sterrezo b03f95b7da Fixed missing cries again
Scatterbug and Spewpa lost their cries somewhere so I re-added them. No changelog because this happened during development, so it's as it was in 1.4
2023-12-04 17:55:26 +01:00
Paul d8cee44661 Changelog [skip-ci] 2023-12-02 14:56:45 +10:00
Hiroku 5dd2287441 Merge branch 'fastutil-crash' into 'main'
Fixed fastutil crash

See merge request cable-mc/cobblemon!802
2023-12-02 04:39:25 +00:00
Paul 1eb6af14c2 Changelog 2023-12-02 10:53:18 +10:00
Paul 61b73972d4 Merge branch '1.4.0a'
# Conflicts:
#	CHANGELOG.md
2023-12-02 10:39:31 +10:00
Hiroku f8bcf8c291 Merge branch 'rendercontext-additions' into 'main'
Add species and aspects to RenderContext

See merge request cable-mc/cobblemon!799
2023-12-02 00:28:20 +00:00
Waterpicker 543d408e63 Merge remote-tracking branch 'cable-mc/main' into rendercontext-additions 2023-12-01 00:46:35 -06:00
Apion 2cdc781e93 Merge branch 'battles-unlanguished' into 'main'
Added langs and update interpreter

See merge request cable-mc/cobblemon!801
2023-12-01 05:45:42 +00:00
Segfault Guy 8dc0a7f72d Interpreter trimmings and fixes. 2023-12-01 00:18:50 -05:00
Paul 8b5231d6f0 Alternative way to fix dupe issue to not crash (huge) 2023-12-01 12:07:00 +10:00
Hiroku 665c4cc26c Merge branch 'particle-command' into 'main'
Added /bedrockparticle command

See merge request cable-mc/cobblemon!798
2023-11-30 22:50:25 +00:00
Lily 253cfd55d8 Merge remote-tracking branch 'origin/archaeology' into archaeology 2023-11-30 15:27:35 -05:00
apion 7c860d8f91 Fix some of seg's review threads 2023-11-30 12:39:09 -05:00
apion 006e86f3c3 Fix my review threads 2023-11-30 11:37:52 -05:00
JazzMcNade e75d0c564f -onUse for the fossil tube top now redirects to the bottom half of the tube if it's available. (Allows for better client prediction.) 2023-11-29 21:25:10 -06:00
Lily 1e97143a6a poisonous potatoes now subtract from the fuel level because why not 2023-11-29 22:11:37 -05:00
MoeBoy76 91dec859e8 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into battles-unlanguished
# Conflicts:
#	CHANGELOG.md
2023-11-30 11:55:53 +11:00
Apion 85f08c9d28 Merge branch 'regionalise' into 'main'
Regionalise

See merge request cable-mc/cobblemon!800
2023-11-30 00:51:21 +00:00
MoeBoy76 d465f465c8 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into battles-unlanguished
# Conflicts:
#	CHANGELOG.md
2023-11-30 11:43:02 +11:00
MoeBoy76 a87bd35fd6 changelog time 2023-11-30 11:38:39 +11:00
MoeBoy76 0c32da0ce1 added species feature for all regional forms that were missing it 2023-11-30 08:51:54 +11:00
Waterpicker 2bdb110c92 Remove some debug code I missed! 2023-11-29 14:04:20 -06:00
Waterpicker d21f3747fc Add species and aspects to RenderContext 2023-11-29 13:56:53 -06:00
jeffw773 fb303fcd97 - Started basic functionality of Battle menu that can be controlled by WASD or keybinded keys. Still more todo on this, but wanted to make a branch for it now 2023-11-29 11:35:50 -08:00
JazzMcNade 7b4e63001b -Refactored comparator output and Trigger behavior into the multiblock structure.
-Ownership protection now prevents release via a redstone signal.
2023-11-29 01:25:55 -06:00
jeffw773 31b185b96c - Added -endability and -transform handlers 2023-11-28 00:06:52 -08:00
jeffw773 034a51b904 Merge remote-tracking branch 'origin/battles-unlanguished' into battles-unlanguished 2023-11-27 22:12:01 -08:00
jeffw773 9e1c491d9c - Moe said it was okay 2023-11-27 22:11:42 -08:00
jmmcp f695a99705 Added -swapboost langs for Guard Swap, Power Swap, and Heart Swap, as well as -start lang for Tar Shot. 2023-11-28 00:31:13 -05:00
jmmcp a8567f9190 Added check for optional effects in handleStartInstruction to allow for proper handling of moves like Reflect Type. 2023-11-27 23:39:27 -05:00
jmmcp 7d455b2dac Merge remote-tracking branch 'origin/battles-unlanguished' into battles-unlanguished
# Conflicts:
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
2023-11-27 23:10:57 -05:00
jmmcp 1dffbd1b37 Added -start langs for embargo and general typeadd moves. 2023-11-27 23:10:10 -05:00
jeffw773 332e25fa76 Merge remote-tracking branch 'origin/battles-unlanguished' into battles-unlanguished 2023-11-27 20:07:53 -08:00
jeffw773 f080fb5639 - Added Substitute Start Language 2023-11-27 20:07:34 -08:00
jmmcp 769a1862e4 Added -copyboost lang, new copyboost instruction handler, new copy method to ContextManager, silent optional argument check to -clearnegativeboost instruction handler, and tweaked -enditem instruction handler in CobblemonHeldItemManager to allow for cases with null source (example: Fling). 2023-11-27 22:25:54 -05:00
jmmcp 127c5e7a8b Merge branch 'main' into battles-unlanguished 2023-11-27 19:11:22 -05:00
JazzMcNade 58fba24f01 -Natural materials data is now synced with the client
-Allowed for improved client prediction in onUse()
-Fixed an issue in which a redstone signal into the machine wouldn't work if the machine was loaded in the completed state.
2023-11-27 01:48:36 -06:00
JazzMcNade f49265ae52 -Fixed return items not working with the Fossil Machine 2023-11-26 22:07:39 -06:00
Paul d1a71373c0 Added /bedrockparticle command 2023-11-27 13:26:26 +10:00
Hiroku 77def6de43 Merge branch 'mooshtank_fix_and_shuckle_nerf' into 'main'
Shuckle Berry Nerf + Brown Mooshtank Sus Stew Fix

See merge request cable-mc/cobblemon!792
2023-11-27 01:42:01 +00:00
jmmcp 973c6bb05f Merge remote-tracking branch 'origin/battles-unlanguished' into battles-unlanguished 2023-11-26 19:39:26 -05:00
jmmcp 5b83e49669 Added -start lang for type changing moves Camouflage, Conversion, and Conversion2, as well as -sidestart and -sideend langs for Lucky Chant and Mist. 2023-11-26 19:38:59 -05:00
Hiroku e01bc2eeab Merge branch 'nickname_invalid_format' into '1.4.0'
Resolves a bug in the format in which the nickname is saved in JSON.

See merge request cable-mc/cobblemon!797
2023-11-26 23:19:50 +00:00
Pedro Souza f6f3070b8e changelog 2023-11-26 20:18:37 -03:00
Pedro Souza 0f2301e668 Resolved nickname invalid format bug 2023-11-26 12:24:54 -03:00
jeffw773 80ce5bab58 - Added battle language for powertrick and powershift 2023-11-26 01:25:15 -08:00
Hiroku 8fb678b0f8 Merge branch 'battle_music_enhancements' into 'main'
Add new battle music features

Closes #587

See merge request cable-mc/cobblemon!790
2023-11-26 08:17:45 +00:00
jeffw773 c0a77aca23 - Added battle language for Stockpile 2023-11-26 00:02:37 -08:00
jmmcp e4c0eab8e2 Added -fieldactivate langs for Fairy Lock and Teatime, as well as a new handler for -invertboost instructions. 2023-11-26 00:02:04 -05:00
Hiroku b9ff46e1b2 Merge branch 'sirrfuchs-main-patch-53627' into 'main'
Added canFly true to Yveltal

See merge request cable-mc/cobblemon!796
2023-11-25 21:22:45 +00:00
sirrfuchs 45a998d3d7 Added canFly true to Yveltal 2023-11-25 16:27:57 +00:00
Hiroku a108d85531 Merge branch 'root-burn' into 'main'
Remedy Alternate Cooking

See merge request cable-mc/cobblemon!795
2023-11-25 07:22:27 +00:00
dhi_holo 0fae45f811 Remedy Alternate Cooking
Smokers and Campfires work now
2023-11-25 10:13:19 +10:00
Hiroku 88717d598d Merge branch 'Waldleufer/speciesJsonFixes' into 'main'
small species json fixes

See merge request cable-mc/cobblemon!794
2023-11-24 22:14:28 +00:00
Waldleufer 33123b4a33 small species json fixes 2023-11-24 22:14:27 +00:00
JazzMcNade 001864e20c -Added a sound when a pokemon is kicked out of the fossil machine via a redstone signal. 2023-11-24 02:31:27 -06:00
Paul 0c9fe19da3 Merge remote-tracking branch 'origin/main' into scheduling-rework
# Conflicts:
#	CHANGELOG.md
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
2023-11-24 10:04:51 +10:00
Hiroku 74e0915ec8 Merge branch 'SmarterDrops' into 'main'
Corrected some drops, added missing drops to a bunch of base forms and forms.

See merge request cable-mc/cobblemon!789
2023-11-24 00:00:28 +00:00
Waldleufer 39cd5e9726 Corrected some drops, added missing drops to a bunch of base forms and forms. 2023-11-24 00:00:28 +00:00
JazzMcNade 28ebd1dec1 The fossil mon rendered inside of the tank now always faces the opposite direction of the tank. 2023-11-23 17:03:18 -06:00
JazzMcNade 94bbc0e574 -Added "smart" hoppering for the fossil compartment. Hoppers can only insert into the compartment of the resulting inventory would be a valid subset of a fossil recipe. 2023-11-23 00:26:32 -06:00
jeffw773 876be2afcf - Brown Mooshtanks give Suspicious Stews like vanilla brown mooshrooms do (plus Pep Up Flower)
- Nerfed Infinite Berry Juice Shuckle
2023-11-22 19:07:39 -08:00
jeffw773 d693f452bc - Fixed Mooshtanks not giving mushroom stew in creative
- Nerfed Shuckle infinite Berry Juice
2023-11-22 15:49:54 -08:00
JazzMcNade 034e7d315c Fixed null pointer exceptions / sync issues from early return in onBreak 2023-11-22 02:39:02 -06:00
jeffw773 fb785b4204 - Hopper only sends fossils to machine when the machine doesn't already have a valid fossil combination inside 2023-11-21 23:18:27 -08:00
jeffw773 ac807ce25b - Hopper only sends fossils to machine when not full or not running 2023-11-21 23:11:06 -08:00
jeffw773 c065f0f8fd - Hoppers now dispense Fossil Items into Microwave of Fossil Machine 2023-11-21 22:42:30 -08:00
jmmcp a1e94b8200 Added -activate langs for eerie spell and crafty shield. 2023-11-22 01:04:11 -05:00
JazzMcNade 9c7d286dd8 -Sending a redstone signal to a tube that has completed the resurrection will cause the fossil pokemon to pop out as a wild pokemon.
-Replaced magic numbers with constants
-Added a bunch of TODOs
2023-11-21 02:00:18 -06:00
jeffw773 69d8e4fab9 - Removed Fossils from Consumable UI list 2023-11-20 21:52:37 -08:00
jeffw773 b788fe3984 - Fixed Skull Fossil lang 2023-11-20 21:08:56 -08:00
apion cfd90fac04 Fixed sherd names 2023-11-20 23:20:44 -05:00
apion 1cf2359195 Bumped ver number and fixed visual formation bug 2023-11-20 23:16:13 -05:00
JazzMcNade 6868d7f26c -Added Comparator output for the monitor of the fossil machine.
-Refactored tube comparator output to use a constant instead of a magic number.
2023-11-20 21:54:52 -06:00
apion 3e58229a89 Fixed another formation issue 2023-11-20 20:56:23 -05:00
apion d76f1524bd Added sherd items and a creative tab for archeology. 2023-11-20 06:44:18 -05:00
apion 58b5eb155d Fixed sounds and hoppers putting material in to finished machines 2023-11-20 04:07:32 -05:00
JazzMcNade fd9caa537b Added Comparator output for the tube 2023-11-20 02:52:44 -06:00
apion 5262972a8d Start working on hopper for tubes 2023-11-20 03:39:30 -05:00
apion 011a095314 Dumb style thing 2023-11-20 01:13:08 -05:00
apion 3520c50541 Fixed issue where adjacent machines didn't form properly 2023-11-20 01:01:39 -05:00
Paul 3eccac861f Migrated regular pose transitioning over to being a primary animation 2023-11-20 15:24:34 +10:00
Paul 7a85b0b690 Fixed dedicated transition animations not resetting the primary animation portion 2023-11-20 14:49:28 +10:00
jeffw773 ff3aa5d782 - Apricorns now work as organic material 2023-11-19 19:34:13 -08:00
Segfault Guy 8b4552a532 Unique wild encounter themes. 2023-11-19 22:33:26 -05:00
Segfault Guy 41673ba90f Battle music for spectators. 2023-11-19 22:33:26 -05:00
Segfault Guy 18f9942dc6 BattleStartedEvent rework and battle music override support. 2023-11-19 22:33:23 -05:00
Segfault Guy 875fe46d21 Player specific battle themes. 2023-11-19 22:32:42 -05:00
Segfault Guy 66c6f9bdd1 BattleMusicHandler upgrades and bug fixes. 2023-11-19 22:32:04 -05:00
jeffw773 9e6dec8605 - Fossil pokemon will become wild pokemon if the machine is broken when the machine is done running. 2023-11-19 18:29:19 -08:00
Paul 1fe223c772 Comments are good 2023-11-20 11:57:46 +10:00
Paul d89ccc110e Merge remote-tracking branch 'origin/main' into improved-transformed-parts 2023-11-20 11:57:40 +10:00
Paul 72c337195a Added the concept of a primary animation 2023-11-20 11:54:35 +10:00
Hiroku b72af84c8c Merge branch 'tooltip-fix' into 'main'
Tooltip fix

See merge request cable-mc/cobblemon!788
2023-11-20 00:32:18 +00:00
miasmus a5ee270b8b account for tooltips with only 1 element 2023-11-19 19:00:05 -05:00
miasmus c08ef4247d doing it this way cause the other way was bad 2023-11-19 18:24:47 -05:00
jmmcp 9424bafabb Merge remote-tracking branch 'origin/battles-unlanguished' into battles-unlanguished 2023-11-19 18:21:02 -05:00
jmmcp a96ecfcdf2 Added -fail instructions for Burn Up and Double Shock, as well as lang for healing moves and weight based moves against Dynamaxed mons failing 2023-11-19 18:20:07 -05:00
Hiroku b1d11e8c22 Merge branch 'HOLDY2' into 'main'
Fixed some minor errors in the newly added dropps

See merge request cable-mc/cobblemon!787
2023-11-19 22:05:02 +00:00
Waldleufer c1fb1379a9 Fixed some minor errors in the newly added dropps 2023-11-19 22:05:02 +00:00
Hiroku 5a220e0ef0 Merge branch 'implement_pokemon' into 'main'
Implemented Drifloon line and minor asset updates.

See merge request cable-mc/cobblemon!786
2023-11-19 22:01:03 +00:00
Hiroku b379fc62b1 Merge branch 'Waldleufer/cry_checker_script' into 'main'
cry audio checker script

See merge request cable-mc/cobblemon!785
2023-11-19 21:55:53 +00:00
Waldleufer d442e4e956 cry audio checker script 2023-11-19 21:55:53 +00:00
Tyzillion bb22c22db9 added "implemented": true to the species.json for the new Pokemon implemented. 2023-11-19 10:33:58 -05:00
JazzMcNade 09bc174401 Added code to display enditem.incinerate's message when it occurs. 2023-11-19 04:26:13 -06:00
Tyzillion e109a1d59f Implemented Drifloon line, Updated Dodrio textures, updated aegislash_shiny texture, Updated models for Geodude line. Added emissives for Noctowl. 2023-11-18 22:12:23 -05:00
jmmcp 3b97a0c847 Added -activate langs for Skill Swap and Wide Guard, -enditem langs for Corrosive Gas and Bug Bite, -fail lang for Aura Wheel, and allow for effectID to pass through as null in handle fail instruction for fails that don't have an effect, such as Baton Pass and Bide. 2023-11-18 16:02:58 -05:00
jeffw773 28a2ec3fc0 - Fossil pokemon and tank fluid now get removed from the tanks when player takes pokemon out with the pokeball 2023-11-18 10:11:48 -08:00
apion 72bba7fdfd Allowed for use of tags for natural materials 2023-11-18 06:19:08 -05:00
apion a258e48582 Pokemon now stay suspended in the tank when it finishes 2023-11-18 02:58:56 -05:00
apion 21a15686b2 Added full tube texture 2023-11-18 02:26:11 -05:00
jeffw773 5da7183ae3 - Fossils can be placed and removed from the Fossil Machine with an empty hand now 2023-11-17 23:04:10 -08:00
apion d847972bb1 Fixed issue where you couldn't form a multiblock from previously formed blocks, also fixed the no block update when fossils are inserted 2023-11-18 01:06:45 -05:00
jeffw773 5e88e73e3f - Fossil machine drops the fossils that are in the machine if the machine hasn't started yet 2023-11-17 21:48:02 -08:00
JazzMcNade 87e8fcd7f7 Added -end lang for Healblock and Skydrop. Added -fail for Skydrop 2023-11-17 22:47:41 -06:00
Hiroku b34f8a045d Merge branch 'null-safety-time' into 'main'
Fix to NullPointerException in PokemonOnShoulderHandler

Closes #677

See merge request cable-mc/cobblemon!783
2023-11-18 04:26:38 +00:00
jeffw773 e9086ec2bd - Prevented Fossil Tank from being overfilled which caused a crash and corrupted the world save 2023-11-17 20:00:07 -08:00
Paul 866db464c4 Merge remote-tracking branch 'origin/main' into improved-transformed-parts
# Conflicts:
#	CHANGELOG.md
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/pokemon/gen3/BeldumModel.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
2023-11-18 13:40:36 +10:00
JPAKx4 4f79b9f8a7 Fix to https://gitlab.com/cable-mc/cobblemon/-/issues/677 2023-11-17 21:30:49 -06:00
JazzMcNade d1b651a876 Added -activate langs for Grudge, Speedswap and Switcheroo. 2023-11-17 21:13:55 -06:00
Hiroku b30a67651e Merge branch 'VillagerCropInteractionFix' into 'main'
Fix for villagers not being able to pick and plant mint seeds, vivichoke seeds...

Closes #647

See merge request cable-mc/cobblemon!760
2023-11-18 01:43:10 +00:00
JazzMcNade a1398e9495 Fixed capitalization. 2023-11-17 19:22:42 -06:00
jmmcp ca319764f0 Added -activate langs for Ingrain, Mist, Poltergeist, Powder, Quash, Quick Guard, Safeguard, and Sketch. 2023-11-17 19:00:12 -05:00
Hiroku ac03c68f41 Merge branch 'defaultIssueTemplate' into 'main'
Added a default Issue Template

See merge request cable-mc/cobblemon!769
2023-11-17 22:16:35 +00:00
Nick 0eb4b43b49 Merge branch 'updated-showdown' into 'main'
Updated Showdown version to latest master from Smogon

Closes #542

See merge request cable-mc/cobblemon!764
2023-11-17 21:18:05 +00:00
Hiroku b49376dba5 Updated Showdown version to latest master from Smogon 2023-11-17 21:18:03 +00:00
Nick 81f6e7b786 Merge branch 'shuckle_milk' into 'main'
Added ability for Shuckles to be milked for Berry Juice

See merge request cable-mc/cobblemon!770
2023-11-17 21:17:28 +00:00
Plastered_Crab 574ee1ae4a Added ability for Shuckles to be milked for Berry Juice 2023-11-17 21:17:27 +00:00
Nick 07a83f54a2 Merge branch 'HOLDY-lite' into 'main'
It's holding time

See merge request cable-mc/cobblemon!777
2023-11-17 19:41:59 +00:00
Jordan 6f3b530d7b It's holding time 2023-11-17 19:41:59 +00:00
Nick 4b86dc1e58 Merge branch 'ability-optional-arguments' into 'main'
Implement method to properly handle showdown ability instructions with optional arguments.

Closes #499

See merge request cable-mc/cobblemon!778
2023-11-17 19:30:45 +00:00
JMMCP bb141aee50 Implement method to properly handle showdown ability instructions with optional arguments. 2023-11-17 19:30:45 +00:00
Nick acb473c01f Merge branch 'Waldleufer/fixUndergroundSpawningAlgorithm' into 'main'
Underground Spawning Algorithm Improvements

See merge request cable-mc/cobblemon!737
2023-11-17 18:37:02 +00:00
Waldleufer a8dc3f40a4 Underground Spawning Algorithm Improvements 2023-11-17 18:37:02 +00:00
Nick b55f11628d Merge branch 'Waldleufer/spawnSpreadsheetToCodeAutomation' into 'main'
Validated and corrected all Spawnfiles

See merge request cable-mc/cobblemon!755
2023-11-17 18:27:17 +00:00
Waldleufer 150c03d4f5 Validated and corrected all Spawnfiles 2023-11-17 18:27:17 +00:00
Nick 1c092588f4 Merge branch 'HealingWishUnfaintPostBattleFix' into 'main'
HealingWishUnfaintPostBattleFix

See merge request cable-mc/cobblemon!779
2023-11-17 18:19:11 +00:00
JazzMcNade 683473c34c - Added activate.electricterrain 2023-11-17 02:43:36 -06:00
jeffw773 c7dee1bd53 - Added some more -start flags 2023-11-16 23:00:03 -08:00
jeffw773 d83a1086d3 - Added start.futuresight 2023-11-16 22:17:35 -08:00
Hiroku b34ca3d42b Merge branch 'HealthBarUpdateOnDamageFix' into 'main'
Fix for health bars not updating when a pokemon takes damage from commit 132a43c5

See merge request cable-mc/cobblemon!775
2023-11-17 05:04:34 +00:00
MoeBoy76 3a1389d719 start of missing battle messages 2023-11-17 10:18:44 +11:00
Hiroku e608dc84ae Merge branch 'mongodb-timestamp' into 'main'
Added timestamp to MongoDB store to signify when the save was created.

See merge request cable-mc/cobblemon!781
2023-11-16 14:49:52 +00:00
navneetset c987a5ba19 Added timestamp to MongoDB store to signify when the save was created. 2023-11-16 14:08:18 +01:00
apion 3dbcc2d28c Moved a bunch of stuff into the MultiblockStructure and out of the individual blockentities. Hopefully helps with the synchronization issues. 2023-11-16 06:41:38 -05:00
Hiroku d65090ce10 Merge branch 'main' into 'main'
Issue #699 - Fixed typo in Coba Berry tooltip.

See merge request cable-mc/cobblemon!780
2023-11-16 11:09:45 +00:00
Michael 206ec25a99 Issue #699 - Fixed typo in Coba Berry tooltip. 2023-11-16 03:22:47 -05:00
JazzMcNade ccd376c055 Refactoring 2023-11-14 22:19:40 -06:00
Lily b8e95bd210 Merge remote-tracking branch 'origin/archaeology' into archaeology 2023-11-13 16:45:57 -05:00
Lily b14465f2a1 advancements and recipe unlocks and so forth 2023-11-13 16:45:45 -05:00
JazzMcNade 6214fb1b97 The handleDamageInstruction inner dispatch is now placed at the head of the queue to make sure it stays ahead of the handleFaintInstruction dispatch. 2023-11-13 01:13:27 -06:00
JazzMcNade a65cee3e24 Fix for health bars not updating when a pokemon takes damage from commit 132a43c5 2023-11-13 00:51:50 -06:00
Waldleufer 1a0135c1fd Added a feature request issue template. 2023-11-12 10:25:44 +01:00
Waldleufer 23dab6546f Added requested changes 2023-11-12 10:25:16 +01:00
jmmcp 2a60048f7e Remove unnecessary test injection. 2023-11-11 23:46:01 -05:00
Waldleufer b4787b8db8 Added a default Issue Template 2023-11-11 19:44:02 +01:00
Paul eefc125f24 Changelog to confirm fix for pokeballs being locked open sometimes 2023-11-11 09:57:08 +10:00
Paul f95f17d0c6 Merge remote-tracking branch 'origin/main' into improved-transformed-parts 2023-11-11 09:46:52 +10:00
Paul ba7e98e95c Migrated more things to contained schedulers and handle animation duration more directly 2023-11-11 08:59:03 +10:00
Hiroku cdcc071ce0 Merge branch 'main' into 'VillagerCropInteractionFix'
# Conflicts:
#   CHANGELOG.md
2023-11-10 22:51:44 +00:00
Lily 02067929a2 add advancement for obtaining a fossil item 2023-11-10 18:46:31 +00:00
Lily 0bc3e8465b finish list of fossil machine fuels 2023-11-10 18:37:01 +00:00
Lily 0ebf5712b9 add item tag for fossil items 2023-11-10 16:04:02 +00:00
Lily 4020e28da9 add recipes to fossil machine blocks 2023-11-10 15:37:13 +00:00
Lily dbc074bfde Update protected message to show on action bar 2023-11-09 23:47:04 -05:00
Lily 17cdedf65f Rename ResurrectionMachineMultiblockBuilder to FossilMultiblockBuilder for consistency 2023-11-09 23:46:28 -05:00
Paul f96766505b Merge remote-tracking branch 'origin/scheduling-rework' into improved-transformed-parts
# Conflicts:
#	CHANGELOG.md
2023-11-10 14:32:44 +10:00
Paul fe96642be4 Conflict resolution 2023-11-10 14:31:53 +10:00
Lily 701af6cd8f Fix conflicting imports 2023-11-09 23:14:45 -05:00
Paul 5515fbfeda Merge remote-tracking branch 'origin/main' into improved-transformed-parts
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/pokemon/gen1/GolbatModel.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/pokemon/gen1/ZubatModel.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/pokemon/gen2/CrobatModel.kt
2023-11-10 14:11:09 +10:00
Lily 41a0dce7fc Merge remote-tracking branch 'origin/main' into archaeology
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlockEntities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	fabric/src/main/kotlin/com/cobblemon/mod/fabric/CobblemonFabric.kt
2023-11-09 23:07:30 -05:00
Lily 3b122b7bcc Add fossils to the creative inventory 2023-11-09 22:36:39 -05:00
Lily 2b3500dfc3 Add a sound for retrieving a fossil from the fossil machine 2023-11-09 22:05:32 -05:00
Lily e921253a57 Added the missing fossil jsons 2023-11-09 21:53:05 -05:00
Lily da45797022 Added a 5-minute protection period where only the player who input the fossil can claim the resulting Pokemon 2023-11-09 21:34:46 -05:00
Lily fc1f68711d Added more sounds to the fossil machine, and added a visual when done growing 2023-11-09 20:57:29 -05:00
Lily da9c1f440c Added sounds to the fossil machine 2023-11-09 19:48:26 -05:00
Paul ae10ca9541 Rebuilt scheduling mechanism to be better contained and avoid side issues 2023-11-10 08:48:13 +10:00
Lily 80d2f76dfc Merge remote-tracking branch 'origin/archaeology' into archaeology 2023-11-09 17:38:20 -05:00
Lily ebf3c9b82f Changed NaturalMaterial into a data class. Added a returnItem parameter for returning leftovers after using an item as fuel (e.g. a bowl when using mushroom stew) 2023-11-09 17:38:05 -05:00
Lily af57ac45a2 Added list of cobblemon items that can be used as fuel for the fossil machine. Also moved mint seeds to their own tag. 2023-11-09 17:35:37 -05:00
JazzMcNade 1c25ae69a5 Fix for villagers not being able to pick and plant mint seeds, vivichoke seeds and revival herbs. Closes #647 2023-11-08 03:05:03 -06:00
Paul 7f464f2e3e Changelogfor prior change 2023-11-06 09:39:29 +10:00
Paul d8e209e4a2 Rebuilt transformed model parts, pose transitions are now smoother and interpolate active idle animations 2023-11-06 09:38:43 +10:00
Paul 41a87d0c8a Added baby fossil Pokémon rendering in the resurrection tube 2023-10-30 20:11:59 +10:00
Paul 45faed038f Merge remote-tracking branch 'origin/main' into archaeology 2023-10-30 17:06:19 +10:00
Deltric 30a78f978d textures didn't commit? 2023-10-23 20:22:21 -05:00
Deltric d8c1d50aa5 Progress on fossils 2023-10-23 20:20:17 -05:00
Deltric eb7bf65e02 I probably missed this during rebase 2023-10-21 14:19:18 -05:00
Deltric c7d3164ff4 Merge branch 'sus-archaeology' into archaeology
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	fabric/src/main/kotlin/com/cobblemon/mod/fabric/CobblemonFabric.kt
2023-10-21 13:44:00 -05:00
Paul e6da43be6d Merge remote-tracking branch 'origin/main' into spawn-rules
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/PokemonProperties.kt
2023-10-21 12:59:57 +10:00
Paul f52345036d Changelog again 2023-10-19 20:44:43 +10:00
Paul bd99ff2bfd Changelog 2023-10-19 20:38:21 +10:00
Paul 19510ca8ba Merge remote-tracking branch 'origin/main' into tracked-data-cleanup 2023-10-19 20:37:30 +10:00
Paul 0ece90bcf1 Reworked our use of data trackers to be more like vanilla (maybe no T-pose anymore?), added battle ID to spawn packet so battle poses always work 2023-10-17 21:53:31 +10:00
MoeBoy76 c2894b4bf1 Fix mixin double up 2023-10-09 09:53:09 +11:00
MoeBoy76 8c3958cdf0 Update from main and added fossil structures from Slippy 2023-10-09 09:42:47 +11:00
MoeBoy76 92cb847755 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into sus-archaeology
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
2023-10-09 09:15:05 +11:00
Paul fc4fed42b5 Added (most of) a spawn rule system for datapacking spawning systems 2023-10-02 15:26:37 +10:00
MoeBoy76 ea332515c4 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into sus-archaeology
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlockEntities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/resources/mixins.cobblemon-common.json
2023-09-05 12:45:53 +10:00
MoeBoy76 49965d6856 fossil structures from build team 2023-09-05 12:42:11 +10:00
apion 6e7694d650 Move some multiblock stuff into api, add licenses 2023-08-28 19:28:12 -04:00
apion a301779a42 Hopefully finalized system for synchronizing multiblocks 2023-08-28 17:14:20 -04:00
apion 440a07d1de Fixed synchronization issue 2023-08-28 15:24:57 -04:00
apion fc7951cbcd Changed multiblock formation logic to use VoxelShape instead of Box, fix some misc rendering issues with the fossil block 2023-08-27 23:29:02 -04:00
apion 44b58ab9a0 Added the connecting bit to the fossil tube 2023-08-27 18:31:10 -04:00
apion d24a6fa75c Added monitor animation 2023-08-27 16:40:16 -04:00
apion fca867a8b8 Asset organization stuff 2023-08-27 15:37:33 -04:00
apion 9514c48eaa Add staged filling to the fossil tube 2023-08-27 11:42:45 -04:00
apion d029b4380a Separate some textures/models 2023-08-26 19:58:55 -04:00
apion 4c29c9726b Fix some desync stuff 2023-08-26 16:33:35 -04:00
apion af9fff9793 Added a system for forcing models to be baked, more progress on rendering fossil tube 2023-08-26 14:25:08 -04:00
apion a8d84c4a28 More messing with animations 2023-08-25 19:45:33 -04:00
apion 5215388e33 Start working on adding the animations to the fossil blocks 2023-08-25 15:51:19 -04:00
apion 93c7685ef7 Added loading/saving of multiblock structures 2023-08-25 11:04:13 -04:00
apion 5c395a1987 Added fossil tube animations 2023-08-24 19:24:23 -04:00
apion 755d367760 Added break functionality to multiblocks, fixed a few issues 2023-08-24 16:18:46 -04:00
apion 3b26d24958 Add the actual functionality of the fossil machine multiblock. Can now insert organic material 2023-08-24 15:13:55 -04:00
apion 27afe40892 Add the fossil tube to the resurrection machine multiblock 2023-08-24 12:48:24 -04:00
apion fcd4dccdad Split resurrection machine into three blocks, add a system for multiblock structure formation (this is pretty freaking cool) 2023-08-24 12:19:08 -04:00
apion 35e206a5d6 Implemented the insertion of organic material into the resurrection machine and data-driven organic material values 2023-08-24 07:10:32 -04:00
apion b51d007341 Start working on resurrection machine 2023-08-23 19:59:19 -04:00
jmmcp a85fd79818 Experimenting with the proper function to add to pre-existing archaeology loot tables. 2023-08-21 17:19:09 -04:00
2314 changed files with 47113 additions and 9117 deletions

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
<!-- 1. START HERE: (Before filling all this out, please quickly go to the list of all issues: (https://gitlab.com/cable-mc/cobblemon/-/issues/?sort=created_date&state=all) and do a quick search to see if your issue has already been reported. If it has, please add a comment to the existing issue instead of creating a new one.)-->
<!-- 2. If the issue was not already reported, please fill out the following template. -->
<!-- 3. If you do not know something, don't worry! Just fill out as much as you can to make sure the issue can get fixed asap. -->
## Summary
(Please summarize the bug encountered concisely)
## Steps to reproduce
(How one can reproduce the issue - this is very important)
## What is the current bug behavior?
(What actually happens)
## What is the expected correct behavior?
(What you should see instead)
## Cobblemon Version and Mod Loader
(Are you using Forge or Fabric? And what version of Cobblemon are you using?)
## Mods used besides Cobblemon
(Did you have any other mods installed besides Cobblemon? If so, please list them here)
## Relevant logs, screenshots and/or videos
(Paste any relevant logs - use code blocks (```) at the start and end to format console output, logs, and code, as
it's very hard to read otherwise. Or just put a link here)
## Additional Information
(If you can, link to the line of code that might be responsible for the problem, or any other info that might be useful)
/label ~bug
@@ -0,0 +1,31 @@
<!-- 1. START HERE: (Before filling all this out, please quickly go to the list of all issues: (https://gitlab.com/cable-mc/cobblemon/-/issues/?sort=created_date&state=all) and do a quick search to see if your feature was already requested. If it is, please add a comment to the existing issue instead of creating a new one.)-->
<!-- 2. If the feature request was not already reported, please fill out the following template. -->
<!-- 3. If you do not know something, don't worry! Just fill out as much as you can. -->
<!-- 4. The more info you provide here, the greater the chance of it being considered by the Design team. -->
## Summary
(Please summarize your feature request concisely)
## What are the projected benefits of this feature?
- (List)
- (benefits)
- (here)
## What could speak against this feature?
- (List)
- (possible)
- (cons)
## Why do you think the benefits outweigh the cons?
(Explain why you think the benefits outweigh the cons)
## Mods involved besides Cobblemon
(Does your suggestion require any other mods besides Cobblemon? If so, please list them here)
## Sketches, screenshots and/or videos
(Feel free to add any sketches, screenshots or videos that might help the Design team understand your suggestion)
## Additional Information
(Is there anything else you would like to add?)
/label ~feature ~suggestion ~needs-design
+159 -60
View File
@@ -1,68 +1,133 @@
# Changelog
## [1.4.1](#1-4-1)
## [1.5.0](#1-5-0)
### Additions
- Added over (number here) ruin structures, where you can find Pokémon-themed Armor Trims and Pottery Sherds, Tumblestones, and more.
- Added (number here) fossil structures, where you can brush Suspicious Sand/Gravel for Fossils.
- Added the Data Monitor, Fossil Compartment, and Restoration Tube. Placed in the correct formation, you can use these to create a Restoration Machine where you can bring fossils back to life.
- Added 3 variants of Tumblestones, which can be planted near Lava or Magma to grow harvestable Tumblestone Clusters.
- Added Tumblestone Blocks, a storage block crafted from 9 Tumblestones.
- Added Ancient Poké Balls, which are aesthetic variants crafted from Tumblestones.
- Added the Ancient Feather, Wing, and Jet Balls, which fly further than regular Poké Balls.
- Added the Ancient Heavy, Leaden, and Gigaton Balls, which are heavier and don't fly as far as regular Poké Balls. These will recieve more functionality in a future update.
- Added Original Trainer functionality. To edit the OT through commands, you must specify originaltrainertype=<Player/NPC>, and originaltrainer=<Username or UUID/NPC Name>
### Changes
- Slightly lowered the volume of all cries
- Updated Pokeball animations and model.
- Turtwig can now be put on shoulder.
- Updated Zubat line model, texture, and animations.
- Added animations for Hitmontop, Tyrogue, and Mightyena.
- Tweaked animations for Dusknoir, Ratatta, Bewear, Exeggutor, and Alolan Exeggutor.
- Sized Kantonian Exeggutor down. Still big but not TOO BIG.
- Tweaked cries for Pikachu, Raichu and Alolan Raichu.
- Pasture blocks will now also connect their bottom left and right sides to walls, iron bars, glass panes and any other modded block that follows the same connection rules.
- The config option `consumeHeldItems` has been removed, please see the Datapack & Resourcepack Creators for instructions on the updated method.
- Using Potions, Status Heals, Ethers, and Antidotes will now return a glass bottle
- Using a Remedy, Fine Remedy, or Superb Remedy will no longer lower friendship with a Pokémon
- The Healing Machine now has a more difficult recipe, placing it later game.
- Heal Powder can now be composted with a 75% chance of adding a layer
- Mental, Power, White, and Mirror Herbs can now be composted with a 100% chance of adding a layer.
- Added emissive to Hoothoot.
- Mining Evolution Stone Ores with a Fortune pickaxe will now increase the amount of items recieved
- Black Augurite can now be used to craft stone axes and obsidian.
- Using Experience Candies brings up the Party Pokémon Select screen when not targeting a Pokémon.
- Added tab completion for statuses to commands
### Additions
- Added battle spectating, can disable in config
- Cobblemon now has compatibility with [Adorn](https://modrinth.com/mod/adorn) allowing you to craft Apricorn wood furniture.
- Berries can now be used in recipes from [Farmer's Delight](https://modrinth.com/mod/farmers-delight) and [Farmer's Delight (Fabric)](https://modrinth.com/mod/farmers-delight-fabric), as well as any other mods using the same berry tags.
- The nature of cobblemon will now be displayed italicized when a mint has been applied. Hovering over the nature will display the mint that was applied. This is the intended behaviour, because the original nature and taste of the Cobblemon does not change when a mint is applied.
- Giving Pokémon items now plays a sound
- A boat, sign and hanging sign is now craftable with Apricorn wood. The recipes are the same shape as Minecraft's equivalent with Apricorn planks as replacements.
- Added the Cleanse Tag held item.
- Added the Inferno, Void, and Forsaken patterns for Vivillon. These can be obtained by evolving a Spewpa in the Nether, End, or Deep Dark respectively.
- Added the Litwick line.
- Bees can now be fed using Pep-Up Flowers.
- Mooshtank can now be milked with a bowl for Mushroom Stew.
- Added cries to Beldum, Metang and Metagross.
- Pokémon now transition more smoothly between different poses.
- Added more held items to the held item tag, and Metal Coat to the held item tab.
- Added all ores to the modloader ore tags, and added tags for each ore type to the mod (both blocks and items).
- Changed the Destiny Knot to be crafted with a Ghast Tear, rather than a diamond.
- You can now punch grown apricorns to harvest them.
### Fixes
- Fixed various stone related blocks not being valid for Big Roots to spread on the Fabric version.
- Re-added the recipe for the Iron Vitamin.
- Cleaned up empty evolutions declaration in species files (no, I'm not listing all 169 by name).
- Fixed Exeggcute not being able to evolve.
- Fixed Cubone not being able to evolve into Alolan Marowak.
- Fixed Squawkabilly forms not being recognized.
### Developer
- Significantly changed the way properties in PokemonEntity work. This could break some plugins (albeit in a quick-fix way).
- Rebuilt the scheduling API to more clearly force side choices and allow more local temporal frames of reference for tasks.
- Added dialogue API.
- Opened up the ItemDropEntry class to allow for it to be extended.
### Datapack & Resourcepack Creators
- The maximum amount of fossils that can fit in the Fossil Compartment can be adjusted in the config.
- Custom fossils can be defined using a list of items and the resulting Pokémon. An example of `aerodactyl.json`:
```JSON
{
"result": "aerodactyl",
"fossils": [
"cobblemon:old_amber_fossil"
]
}
```
- Fetus models can be defined in `bedrock/fossils`.
- Fetus textures can be defined in `textures/fossils`.
- Fuel for the Restoration Machine is registered inside the `natural_materials` folder. To register more fuels, create a JSON file containing an array of objects. Each object supports the following fields:
- `content`: Integer containing the amount of fuel to add.
- `item` OR `tag`: Identifier of the item or item tag to be inserted.
- `returnItem`: Identifier of an item to return to the player after consumption.
- Added 3 new item tags: `ancient_poke_balls`, `fossils`, and `tumblestones`.
- Added a spawn rules system to modify general spawning behaviour, see the [wiki](https://wiki.cobblemon.com/index.php/Spawn_Rules) for more information.
- Fixed an issue with transformed parts in posers causing whacky positional issues.
- Fixed Poké Balls freezing in an open state if you look away when it's meant to close.
- Added dialogue datapack folder and /opendialogue command.
## [1.4.1 (December 23rd, 2023)](#1-4-1)
### Additions
- Added battle spectating. Press R on a player in a battle and you can spectate and bully them for their tactics.
- Added the Litwick and Drifloon lines.
- Cobblemon now has compatibility with [Adorn](https://modrinth.com/mod/adorn), allowing you to craft Apricorn wood furniture.
- Berries can now be used in recipes from [Farmer's Delight](https://modrinth.com/mod/farmers-delight) and [Farmer's Delight (Fabric)](https://modrinth.com/mod/farmers-delight-fabric), as well as any other mods using the same berry tags.
- Boats, signs and hanging signs are now craftable with Apricorn wood.
- Added the Fairy Feather, Iron Ball, Cleanse Tag, Flame Orb, Life Orb, Smoke Ball, and Toxic Orb held items.
- Added the Inferno, Void, and Forsaken patterns for Vivillon. These can be obtained by evolving a Spewpa in the Nether, End, or Deep Dark respectively.
- Bees can now be fed using Pep-Up Flowers.
- Mooshtank can now be milked with a bowl for Mushroom Stew.
- Updated Showdown version to use generation 9 battle data.
- Added cries to Beldum, Metang and Metagross.
- Added a /bedrockparticle command to run Snowstorm-format particle effects.
- Added data for Dipplin, Fezandipiti, Munkidori, Ogerpon, Okidogi, Poltchageist and Sinistcha.
### Changes
- Using Potions, Status Heals, Ethers, and Antidotes will now return a glass bottle
- Using a Remedy, Fine Remedy, or Superb Remedy will no longer lower friendship with a Pokémon.
- The Healing Machine now has a [much more difficult recipe](https://wiki.cobblemon.com/index.php/Healing_Machine), placing it later game.
- Made the EXP. Share recipe cheaper.
- Turtwig can now be put on your shoulder.
- Updated Zubat line model, texture, and animations.
- Updated Geodude line models and textures.
- Added animations for Hitmontop, Tyrogue, and Mightyena.
- Tweaked animations for Dusknoir, Ratatta, Bewear, Exeggutor, and Alolan Exeggutor.
- Sized Kantonian Exeggutor down. Still big, but not TOO big.
- Tweaked cries for Pikachu, Raichu and Alolan Raichu.
- Fixed Swimming behaviors for Wimpod line, Oshawott line, Quaxly line, and Clodsire
- Changed the way level scaling works in spawning. By default, anything with a spawn range of up to 5 either side of the party highest level and everything else will spawn per its specified ranges.
- The nature of Pokémon will now be displayed italicized when a mint has been applied. Hovering over the nature will display the mint that was applied.
- Slightly lowered the volume of all cries.
- Giving Pokémon items now plays a sound
- Updated the Poké Ball model and animations.
- Pasture blocks will now also connect their bottom left and right sides to walls, iron bars, glass panes, and any other modded block that follows the same connection rules.
- The config option `consumeHeldItems` has been removed, please see the Datapack & Resourcepack Creators section for instructions on the updated method.
- Heal Powder can now be composted with a 75% chance of adding a layer
- Mental, Power, White, and Mirror Herbs can now be composted with a 100% chance of adding a layer.
- Added glowing eyes to Hoothoot and Noctowl.
- Mining Evolution Stone Ores with a Fortune pickaxe will now increase the amount of items received.
- Black Augurite can now be used to craft stone axes and obsidian.
- Using Experience Candies brings up the Party Pokémon Select screen when not targeting a Pokémon.
- Added tab completion for statuses to commands.
- Remedies can now be cooked in a Smoker and on a Campfire.
- Vertically flipped the Destiny Knot recipe.
### Fixes
- Fixed Raticate, Onix, Unfezant, Bergmite, Avalugg, Boltund and Revavroom cries not playing.
- Fixed Alolan Ratticate animations causing a crash.
- Fixed Quaxwell not doing its cry.
- Fixed Shroomish not using its idle.
- Fixed how Weight and Height is calculated for Pokémon, fixing the damage from moves like Low Kick.
- Fixed a staggering number of battle messages.
- Fixed various stone related blocks not being valid for Big Roots to spread onto on the Fabric version.
- Updated the registration of compostable items to improve compatibility with Fabric forks such as Quilt. Please note this does not mean we officially support Quilt, this change was only done since it was possible by correcting the registration to use the new intended way in the Fabric API.
- Fixed Dispensers being unable to shear grown Apricorns.
- Fixed Bowl not being given back to player after using Berry Juice
- Added context for -fail and -block handlers for battle text and added 16 related battle texts for them
- Fixed Battle text for Disable, Laser Focus, Foresight, Fire Spin, Telekinesis, Curse, Recharge and Encore
- Fixed missing text for snowy weather in battles
- Fixed missing text for attempting to catch an uncatchable Pokémon
- Fixed Moonphases for Clefairy line
- Fixed issue where Potions, Super Potions, and Hyper Potions did not work during battle
- Fixed the compatibility patch with the Forge version of [Carry On](https://modrinth.com/mod/carry-on) due to a bug on the mod, the Fabric version was unchanged and is still compatible.
- Added the ability to place Berries on modded Farmland blocks.
- Shouldered Pokémon now hop off when selected in team and r is pressed. This also is in effect in battles leading to shouldered Pokémon jumping of the shoulder of the trainer when it is their turn.
- Shouldered Pokémon now hop off when selected in team and R is pressed. This also is in effect in battles leading to shouldered Pokémon jumping of the shoulder of the trainer when it is their turn.
- Made more items compostable and changed the process for making items compostable.
- Added the ability for Hoppers to fill Brewing Stands with Medicinal Brews and Potions.
- Apricorn blocks are now flammable.
- Apricorn blocks are now flammable. Probably should have started that way, but we got there.
- The default pose for Pokémon being passengers is now "standing".
- Fixed issue where some IVs were changing every player relog.
- Fixed cursed battle message
- Fixed mistakes in pokemon spawning JSONs
- Fixed issue where some IVs were changing every time a player logged back in.
- Fixed advancement crash from bad datapack evolution data.
- Fixed global influences being applied to TickingSpawners twice.
- Reverted the default SpawningSelector back to FlatContextWeightedSelector. This fixes multiple weight related issues, including weights with SpawningInfluences.
- Apricorn Planting advancement should work again.
- Advancement "Patterned Wings" should now allow High Plains and Icy Snow Vivillon to register.
- Advancement "Vivillonaire" should now allow High Plains and Icy Snow Vivillon to register.
- Fixed the last battle critical hits evolution requirement not working.
- Fixed the damage taken evolution requirement not saving progress.
- Fixed the defeated Pokémon evolution requirement not saving progress.
@@ -70,20 +135,54 @@
- Fixed an exploit that could convert a single piece of Blaze Powder into an extra Medicinal Brew on the Forge version.
- Fixed an issue where health percentages would show incorrectly after healing
- Fixed the move Revival Blessing not allowing you to select from fainted party members.
- Fixed villagers not being able to pick up and plant mint seeds, vivichoke seeds, and revival herbs.
- Fixed Exeggcute faint.
- Fixed Raticate, Onix, Unfezant, Bergmite, Avalugg, Boltund and Revavroom cries not playing.
- Fixed Alolan Ratticate animations causing a crash.
- Fixed Quaxwell not doing its cry.
- Fixed Shroomish not using its idle.
- Fixed various spawn configuration issues across the board.
- Fixed a possible visual duplication of sent out Pokémon.
- Fixed battle text for Trace, Receiver, and Power of Alchemy.
- Fixed tooltips being appended too late in items.
- Fixed battles ending background music when battle music is not present.
- Fixed battles ending background music, instead of pausing, when battle music is played.
- Fixed a bunch of regionals to actually be obtainable, namely the unmodelled ones
- Fixed battle text for moves that were missing.
- Fixed a formatting error that affected Pokémon nicknames when the storage type is JSON.
- Fixed a crash that could occur on some servers relating to chunk loading and teleporting.
- Fixed an issue with Inkay's evolution requirement.
- Fixed conflicting evolution requirements that would cause the Ocean, River, Sun, and Tundra variants of Vivillon to be unobtainable through evolution.
- Fixed the Modern variant of Vivillon not being obtainable through evolution.
- Fixed Pokémon pathing through berry bushes, harming themselves in the process.
### Developer
- Fixed the `SpawnEvent` not respecting usage of `Cancelable#cancel`.
- Added the `EvolutionTestedEvent`, this allows listening and overriding the final result of evolution requirement tests.
- Rebuilt the scheduling API to more clearly force side choices and allow more local temporal frames of reference for tasks.
- Added utility script that can be used to generate all Spawn JSONS for all pokemon from the spawning spreadsheet in 1 click ([cobblemon_spawn_csv_to_json.py](utilityscripts%2Fcobblemon_spawn_csv_to_json.py)).
- The `HeldItemManager` has a new method `shouldConsumeItem`, this will return false by default to prevent breaking changes, see the documentation and update your implementations as needed.
- Added and implemented minSkyLight and maxSkyLight as config options for SpawnConditions
- Player specific battle themes can now be assigned to `PlayerData#battleTheme`.
- Changed design of `BattleStartedPreEvent`. Will now expose the `PokemonBattle`.
### Datapack & Resourcepack Creators
- Added 3 new item tags: `cobblemon:held/consumed_in_npc_battle`, `cobblemon:held/consumed_in_pvp_battle` & `cobblemon:held/consumed_in_wild_battle` these will determine which items get consumed in the implied battle types by Cobblemon, keep in mind the controller for this behaviour can be overriden by 3rd party.
- Unique wild encounter themes can now be associated with a specific species (or form) by assigning a SoundEvent identifier to the `battleTheme` field in the species' data configuration.
- Added a `structure` evolution condition, used to check if a Pokémon is in a given structure.
### Localization
- Updated translations for:
- French and Canadian French
- Simplified and Traditional Chinese
- Spanish and Mexican Spanish
- Pirate English
- German
- Thai
- Portuguese and Brazilian Portuguese
- Polish
- Italian
- Dutch
- Ukrainian
- Russian
Thank you so much to all of our community translators that bring the mod to the rest of the world!
## [1.4.0 - The Friends and Farms Update (October 13th, 2023)](#1-4-0)
### Additions
@@ -872,20 +971,20 @@
- Fixed spawning in several biome tag categories.
- Fixed resource pack support for Pokémon models and textures.
- **Model Fixes**
- Fixed Paras and Nidoqueen looking very weird.
- Fixed Hitmonchan asserting dominance with a T-pose as well as Grimer and Muk pointing their hands to the sky.
- Fixed specific Pokémon suddenly pausing their animation after staying still for a long time.
- Fixed Mankey's feet being buried in the ground.
- Updated the Substitute model and updated its shiny texture to be better for the red-green colorblind.
- Improved Horsea, Seadra, and Kingdra animations, especially on land.
- Fixed Paras and Nidoqueen looking very weird.
- Fixed Hitmonchan asserting dominance with a T-pose as well as Grimer and Muk pointing their hands to the sky.
- Fixed specific Pokémon suddenly pausing their animation after staying still for a long time.
- Fixed Mankey's feet being buried in the ground.
- Updated the Substitute model and updated its shiny texture to be better for the red-green colorblind.
- Improved Horsea, Seadra, and Kingdra animations, especially on land.
- **Battle Fixes**
- Fixed an issue with battles where Pokémon had a different max HP causing some desync issues with the health bars.
- Fixed Magnitude battle messages.
- Moves that are disabled or out of PP now show transparently in the battle GUI instead of being hidden completely.
- Statuses like sleep and frozen no longer last forever if it was carried over from a previous battle.
- Fixed an issue with battles where Pokémon had a different max HP causing some desync issues with the health bars.
- Fixed Magnitude battle messages.
- Moves that are disabled or out of PP now show transparently in the battle GUI instead of being hidden completely.
- Statuses like sleep and frozen no longer last forever if it was carried over from a previous battle.
### Localization
- Added species, ability, and move translations for `ko_ko`, `jp_jp`, `fr_fr`, `es_es`, `it_it`, and `zh_cn`.
## [1.0.0 (November 12th, 2022)](#1-0-0)
- Initial release.
- Initial release.
@@ -8,12 +8,9 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pose;
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityModel;
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
@@ -0,0 +1,20 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.duck;
import net.minecraft.sound.SoundCategory;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
public interface SoundManagerDuck {
void pauseSounds(@Nullable Identifier id, @Nullable SoundCategory category);
void resumeSounds(@Nullable Identifier id, @Nullable SoundCategory category);
}
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.duck;
import net.minecraft.client.sound.SoundInstance;
import net.minecraft.sound.SoundCategory;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public interface SoundSystemDuck {
void pauseSounds(@Nullable Identifier id, @Nullable SoundCategory category);
void resumeSounds(@Nullable Identifier id, @Nullable SoundCategory category);
Collection<SoundInstance> getSounds(@Nullable SoundCategory category);
}
@@ -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.mixin;
import net.minecraft.entity.passive.VillagerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.village.VillagerProfession;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.cobblemon.mod.common.villager.VillagerGatherableItems;
import java.util.Objects;
@Mixin(VillagerEntity.class)
public abstract class EntityVillagerMixin {
@Inject(method = "canGather", at = @At(value = "RETURN"), cancellable = true)
private void cobblemon$canGather(ItemStack stack, CallbackInfoReturnable<Boolean> ci) {
final VillagerEntity villager = (VillagerEntity) (Object) this;
if(ci.getReturnValue() == false && Objects.equals(villager.getVillagerData().getProfession(), VillagerProfession.FARMER) && villager.getInventory().canInsert(stack)) {
if(VillagerGatherableItems.INSTANCE.getVillagerGatherableItems().contains(stack.getItem()))
ci.setReturnValue(true);
}
}
}
@@ -8,20 +8,36 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.api.scheduling.ScheduledTaskTracker;
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.GameRenderer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GameRenderer.class)
public class GameRendererMixin {
@Unique
long lastTime = -1;
@Inject(
method = "render",
at = @At(value = "TAIL")
)
public void render(float tickDelta, long startTime, boolean tick, CallbackInfo ci) {
ScheduledTaskTracker.INSTANCE.update();
MinecraftClient client = MinecraftClient.getInstance();
long newTime = System.currentTimeMillis();
// Don't play scheduled animations when the game is paused
if (client.isPaused()) {
lastTime = newTime;
return;
}
if (lastTime != -1) {
ClientTaskTracker.INSTANCE.update((newTime - lastTime) / 1000F);
}
lastTime = newTime;
}
}
@@ -0,0 +1,69 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.CobblemonBakingOverrides;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import net.minecraft.client.color.block.BlockColors;
import net.minecraft.client.render.model.ModelLoader;
import net.minecraft.client.render.model.UnbakedModel;
import net.minecraft.client.render.model.json.JsonUnbakedModel;
import net.minecraft.client.util.ModelIdentifier;
import net.minecraft.util.Identifier;
import net.minecraft.util.profiler.Profiler;
import org.slf4j.Logger;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.InjectionPoint;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.points.BeforeStringInvoke;
@Mixin(ModelLoader.class)
public abstract class ModelLoaderMixin {
@Shadow protected abstract void addModel(ModelIdentifier modelId);
@Final
@Shadow
private Map<Identifier, UnbakedModel> unbakedModels;
@Final
@Shadow
private static Logger LOGGER;
@Inject(
method = "<init>",
at = @At("TAIL")
)
public void init(BlockColors blockColors,
Profiler profiler,
Map jsonUnbakedModels,
Map blockStates,
CallbackInfo ci) {
CobblemonBakingOverrides.INSTANCE.getModels().forEach(bakingOverride -> {
try {
this.unbakedModels.put(bakingOverride.getModelIdentifier(), this.loadModelFromJson(bakingOverride.getModelLocation()));
this.addModel(bakingOverride.getModelIdentifier());
} catch (IOException e) {
LOGGER.error("Error loading a Cobblemon BakedModel:", e);
throw new RuntimeException(e);
}
});
}
@Shadow
private JsonUnbakedModel loadModelFromJson(Identifier id) throws IOException {
return null;
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.sound.battle.BattleMusicController;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.sound.MusicTracker;
import net.minecraft.client.sound.SoundInstance;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MusicTracker.class)
public class MusicTrackerMixin {
@Shadow @Final
private MinecraftClient client;
@Shadow @Nullable
private SoundInstance current;
/** Freezes the tracker while a BattleMusicInstance is in progress. Current SoundInstance being tracked will be paused by BattleMusicHandler. */
@Inject(method = "tick()V", at = @At("HEAD"), cancellable = true)
public void tick(CallbackInfo cb) {
if (this.client.getSoundManager().isPlaying(BattleMusicController.INSTANCE.getMusic())) cb.cancel();
}
}
@@ -8,36 +8,77 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.access.SoundManagerDuck;
import net.minecraft.client.sound.SoundInstance;
import net.minecraft.client.sound.SoundManager;
import com.cobblemon.mod.common.client.sound.battle.BattleMusicController;
import com.cobblemon.mod.common.duck.SoundManagerDuck;
import com.cobblemon.mod.common.duck.SoundSystemDuck;
import net.minecraft.client.sound.*;
import net.minecraft.sound.SoundCategory;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(SoundManager.class)
public class SoundManagerMixin implements SoundManagerDuck {
public abstract class SoundManagerMixin implements SoundManagerDuck {
private boolean[] blockedCategories = new boolean[SoundCategory.values().length];
@Shadow
public abstract boolean isPlaying(SoundInstance sound);
@Override
public void toggleCategories(SoundCategory... categories) {
for (SoundCategory category : categories) {
int index = category.ordinal();
boolean blocked = blockedCategories[index];
blockedCategories[index] = !blocked;
}
@Shadow @Final
private SoundSystem soundSystem;
// Never-ending ambient loops are a special exception (they are only initiated once) and should always be paused if filtered.
private boolean isAmbientLoop(SoundInstance sound) {
return sound instanceof BiomeEffectSoundPlayer.MusicLoop || sound instanceof AmbientSoundLoops.Underwater;
}
private boolean filterCondition(SoundInstance sound) {
return !isAmbientLoop(sound) &&
this.isPlaying(BattleMusicController.INSTANCE.getMusic()) &&
BattleMusicController.INSTANCE.getFilteredCategories().contains(sound.getCategory());
}
private boolean ambientLoopCondition(SoundInstance sound) {
return isAmbientLoop(sound) &&
this.isPlaying(BattleMusicController.INSTANCE.getMusic()) &&
BattleMusicController.INSTANCE.getFilteredCategories().contains(sound.getCategory());
}
/** Pauses the queried SoundInstance(s). If id is null, will pause all sounds belonging to the SoundCategory. */
@Override
public void pauseSounds(@Nullable Identifier id, @Nullable SoundCategory category) {
((SoundSystemDuck)soundSystem).pauseSounds(id, category);
}
/** Resumes the queried SoundInstance(s). If id is null, will resume all sounds belonging to the SoundCategory. */
@Override
public void resumeSounds(@Nullable Identifier id, @Nullable SoundCategory category) {
((SoundSystemDuck)soundSystem).resumeSounds(id, category);
}
/** Blocks filtered sounds from being played while a BattleMusicInstance is in progress. */
@Inject(method = "play(Lnet/minecraft/client/sound/SoundInstance;)V", at = @At("HEAD"), cancellable = true)
public void play(SoundInstance sound, CallbackInfo cb) {
if (blockedCategories[sound.getCategory().ordinal()]) cb.cancel();
public void playStart(SoundInstance sound, CallbackInfo cb) {
if (filterCondition(sound)) cb.cancel();
}
@Inject(method = "play(Lnet/minecraft/client/sound/SoundInstance;I)V", at = @At("HEAD"), cancellable = true)
public void play(SoundInstance sound, int delay, CallbackInfo cb) {
if (blockedCategories[sound.getCategory().ordinal()]) cb.cancel();
public void playStart(SoundInstance sound, int delay, CallbackInfo cb) {
if (filterCondition(sound)) cb.cancel();
}
/** Pauses ambient loops while a BattleMusicInstance is in progress. */
@Inject(method = "play(Lnet/minecraft/client/sound/SoundInstance;)V", at = @At("TAIL"))
public void playEnd(SoundInstance sound, CallbackInfo cb) {
if (ambientLoopCondition(sound)) this.pauseSounds(sound.getId(), SoundCategory.AMBIENT);
}
@Inject(method = "play(Lnet/minecraft/client/sound/SoundInstance;I)V", at = @At("TAIL"))
public void playEnd(SoundInstance sound, int delay, CallbackInfo cb) {
if (ambientLoopCondition(sound)) this.pauseSounds(sound.getId(), SoundCategory.AMBIENT);
}
}
@@ -8,31 +8,102 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.sound.battle.BattleMusicController;
import com.cobblemon.mod.common.duck.SoundSystemDuck;
import com.google.common.collect.Multimap;
import net.minecraft.client.sound.SoundInstance;
import net.minecraft.client.sound.SoundSystem;
import net.minecraft.client.sound.*;
import net.minecraft.sound.SoundCategory;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map;
@Mixin(SoundSystem.class)
public abstract class SoundSystemMixin {
public abstract class SoundSystemMixin implements SoundSystemDuck {
@Shadow
private boolean started;
@Shadow
private Multimap<SoundCategory, SoundInstance> sounds;
@Shadow @Final
private Map<SoundInstance, Channel.SourceManager> sources;
@Shadow
protected abstract void stop(SoundInstance sound);
@Shadow
public abstract boolean isPlaying(SoundInstance sound);
/** Resumes the Source belonging to the queried SoundInstance. */
private void resume(SoundInstance sound) {
Channel.SourceManager sourceManager = this.sources.get(sound);
if (this.started && sourceManager != null) sourceManager.run(Source::resume);
}
/** Pauses the Source belonging to the queried SoundInstance. */
private void pause(SoundInstance sound) {
Channel.SourceManager sourceManager = this.sources.get(sound);
if (this.started && sourceManager != null) sourceManager.run(Source::pause);
}
/** Resumes the SoundInstance(s) queried by id and/or category. */
@Override
public void resumeSounds(@Nullable Identifier id, @Nullable SoundCategory category) {
if (category != null) {
this.sounds.get(category).forEach((sound) -> {
if (id == null || sound.getId().equals(id)) resume(sound);
});
} else if (id == null) {
this.sources.keySet().forEach(this::resume);
} else {
this.sources.keySet().forEach(sound -> {
if (sound.getId().equals(id)) resume(sound);
});
}
}
/** Pauses the SoundInstances queried by id and/or category. */
@Override
public void pauseSounds(@Nullable Identifier id, @Nullable SoundCategory category) {
if (category != null) {
this.sounds.get(category).forEach((sound) -> {
if (id == null || sound.getId().equals(id)) pause(sound);
});
} else if (id == null) {
this.sources.keySet().forEach(this::pause);
} else {
this.sources.keySet().forEach(sound -> {
if (sound.getId().equals(id)) pause(sound);
});
}
}
/** Allows stopping all SoundInstances that belong to a queried category. */
@Inject(method = "stopSounds(Lnet/minecraft/util/Identifier;Lnet/minecraft/sound/SoundCategory;)V", at = @At("HEAD"), cancellable = true)
public void stopSounds(@Nullable Identifier id, @Nullable SoundCategory category, CallbackInfo cb) {
if (id == null && category != null) {
sounds.get(category).forEach(this::stop);
this.sounds.get(category).forEach(this::stop);
cb.cancel();
}
}
/** Different behavior for resuming SoundInstances while a BattleMusicInstance is being played. We do not want to resume filtered sounds. */
@Inject(method = "resumeAll()V", at = @At("HEAD"), cancellable = true)
public void resumeAll(CallbackInfo cb) {
if (this.isPlaying(BattleMusicController.INSTANCE.getMusic())) {
this.sounds.values().forEach(sound -> {
if (sound == BattleMusicController.INSTANCE.getMusic() || !BattleMusicController.INSTANCE.getFilteredCategories().contains(sound.getCategory())) {
resume(sound);
}
});
cb.cancel();
}
}
@@ -1,22 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.accessor;
import net.minecraft.client.sound.MusicTracker;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(MusicTracker.class)
public interface MusicTrackerAccessor {
@Accessor("timeUntilNextSong")
int timeUntilNextSong();
@Accessor("timeUntilNextSong")
void setTimeUntilNextSong(int timeUntilNextSong);
}
@@ -0,0 +1,29 @@
/*
* 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
import net.minecraft.client.MinecraftClient
import net.minecraft.client.render.model.BakedModel
import net.minecraft.client.util.ModelIdentifier
import net.minecraft.util.Identifier
/**
* Contains information for forcing a model to be baked
*
* @param modelLocation The location of the model
* @param modelIdentifier The identifier that the BakedModel will be registered to
*/
data class BakingOverride(
val modelLocation: Identifier,
val modelIdentifier: ModelIdentifier
) {
fun getModel(): BakedModel {
return MinecraftClient.getInstance().bakedModelManager.getModel(modelIdentifier)
}
}
@@ -41,7 +41,8 @@ import com.cobblemon.mod.common.api.pokemon.stats.EvCalculator
import com.cobblemon.mod.common.api.pokemon.stats.Generation8EvCalculator
import com.cobblemon.mod.common.api.pokemon.stats.StatProvider
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.scheduling.ScheduledTaskTracker
import com.cobblemon.mod.common.api.scheduling.ServerRealTimeTaskTracker
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.CobblemonSpawningProspector
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
@@ -54,6 +55,7 @@ import com.cobblemon.mod.common.api.storage.adapter.flatfile.FileStoreAdapter
import com.cobblemon.mod.common.api.storage.adapter.flatfile.JSONStoreAdapter
import com.cobblemon.mod.common.api.storage.adapter.flatfile.NBTStoreAdapter
import com.cobblemon.mod.common.api.storage.factory.FileBackedPokemonStoreFactory
import com.cobblemon.mod.common.api.storage.molang.NbtMoLangDataStoreFactory
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
import com.cobblemon.mod.common.api.storage.player.PlayerDataStoreManager
@@ -67,6 +69,7 @@ import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.ShowdownThread
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.command.argument.DialogueArgumentType
import com.cobblemon.mod.common.command.argument.MoveArgumentType
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
import com.cobblemon.mod.common.command.argument.PokemonArgumentType
@@ -153,6 +156,7 @@ object Cobblemon {
val bestSpawner = BestSpawner
val battleRegistry = BattleRegistry
var storage = PokemonStoreManager()
var molangData = NbtMoLangDataStoreFactory
lateinit var playerData: PlayerDataStoreManager
lateinit var starterConfig: StarterConfig
val dataProvider: DataProvider = CobblemonDataProvider
@@ -260,9 +264,15 @@ object Cobblemon {
ifDedicatedServer {
isDedicatedServer = true
PlatformEvents.SERVER_TICK_POST.subscribe { ScheduledTaskTracker.update() }
}
PlatformEvents.SERVER_TICK_POST.subscribe {
ServerTaskTracker.update(1/20F)
ServerRealTimeTaskTracker.update()
}
PlatformEvents.SERVER_TICK_PRE.subscribe {
ServerRealTimeTaskTracker.update()
}
PlatformEvents.SERVER_STARTING.subscribe { event ->
val server = event.server
playerData = PlayerDataStoreManager().also { it.setup(server) }
@@ -371,8 +381,9 @@ object Cobblemon {
battleRegistry.startBattle(
BattleFormat.GEN_9_SINGLES,
BattleSide(PokemonBattleActor(UUID.randomUUID(), BattlePokemon(Pokemon().initialize()), -1F)),
BattleSide(PokemonBattleActor(UUID.randomUUID(), BattlePokemon(Pokemon().initialize()), -1F))
).apply { mute = true }
BattleSide(PokemonBattleActor(UUID.randomUUID(), BattlePokemon(Pokemon().initialize()), -1F)),
true
).ifSuccessful { it.mute = true }
}
}
@@ -477,6 +488,7 @@ object Cobblemon {
this.implementation.registerCommandArgument(cobblemonResource("move"), MoveArgumentType::class, ConstantArgumentSerializer.of(MoveArgumentType::move))
this.implementation.registerCommandArgument(cobblemonResource("party_slot"), PartySlotArgumentType::class, ConstantArgumentSerializer.of(PartySlotArgumentType::partySlot))
this.implementation.registerCommandArgument(cobblemonResource("pokemon_store"), PokemonStoreArgumentType::class, ConstantArgumentSerializer.of(PokemonStoreArgumentType::pokemonStore))
this.implementation.registerCommandArgument(cobblemonResource("dialogue"), DialogueArgumentType::class, ConstantArgumentSerializer.of(DialogueArgumentType::dialogue))
}
}
@@ -0,0 +1,21 @@
/*
* 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
import net.minecraft.entity.ai.brain.Activity
object CobblemonActivities {
val activities = mutableListOf<Activity>()
val BATTLING_ACTIVITY = Activity("pokemon_battling")
fun register(activity: Activity): Activity {
activities.add(activity)
return activity
}
}
@@ -8,6 +8,14 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
import com.cobblemon.mod.common.block.entity.PCBlockEntity
import com.cobblemon.mod.common.block.entity.PokemonPastureBlockEntity
import com.cobblemon.mod.common.block.entity.fossil.FossilCompartmentBlockEntity
import com.cobblemon.mod.common.block.entity.fossil.FossilMultiblockEntity
import com.cobblemon.mod.common.block.entity.fossil.FossilTubeBlockEntity
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
import com.cobblemon.mod.common.block.entity.*
import com.cobblemon.mod.common.platform.PlatformRegistry
import net.minecraft.block.entity.BlockEntityType
@@ -23,9 +31,13 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
@JvmField
val HEALING_MACHINE: BlockEntityType<HealingMachineBlockEntity> = this.create("healing_machine", BlockEntityType.Builder.create(::HealingMachineBlockEntity, CobblemonBlocks.HEALING_MACHINE).build(null))
@JvmField
val PC: BlockEntityType<PCBlockEntity> = this.create("pc", BlockEntityType.Builder.create(::PCBlockEntity, CobblemonBlocks.PC).build(null))
@JvmField
val BERRY = this.create("berry", BlockEntityType.Builder.create(::BerryBlockEntity, *CobblemonBlocks.berries().values.toTypedArray()).build(null))
@JvmField
val PASTURE: BlockEntityType<PokemonPastureBlockEntity> = this.create("pasture", BlockEntityType.Builder.create(::PokemonPastureBlockEntity, CobblemonBlocks.PASTURE).build(null))
@JvmField
@@ -33,4 +45,24 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
@JvmField
val HANGING_SIGN: BlockEntityType<CobblemonHangingSignBlockEntity> = this.create("hanging_sign", BlockEntityType.Builder.create(::CobblemonHangingSignBlockEntity, CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN).build(null))
}
@JvmField
val FOSSIL_MULTIBLOCK: BlockEntityType<FossilMultiblockEntity> = this.create("fossil_multiblock",
BlockEntityType.Builder.create({ pos, state -> FossilMultiblockEntity(pos, state, FossilMultiblockBuilder(pos)) },
CobblemonBlocks.FOSSIL_MONITOR
).build(null)
)
@JvmField
val FOSSIL_TUBE: BlockEntityType<FossilTubeBlockEntity> = this.create("fossil_tube",
BlockEntityType.Builder.create({ pos, state -> FossilTubeBlockEntity(pos, state, FossilMultiblockBuilder(pos)) },
CobblemonBlocks.FOSSIL_TUBE
).build(null)
)
@JvmField
val FOSSIL_COMPARTMENT: BlockEntityType<FossilCompartmentBlockEntity> = this.create("fossil_compartment",
BlockEntityType.Builder.create({ pos, state -> FossilCompartmentBlockEntity(pos, state, FossilMultiblockBuilder(pos)) },
CobblemonBlocks.FOSSIL_COMPARTMENT
).build(null)
)
}
@@ -11,6 +11,9 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.apricorn.Apricorn
import com.cobblemon.mod.common.block.*
import com.cobblemon.mod.common.block.MintBlock.MintType
import com.cobblemon.mod.common.block.fossilmachine.FossilCompartmentBlock
import com.cobblemon.mod.common.block.fossilmachine.FossilMonitorBlock
import com.cobblemon.mod.common.block.fossilmachine.FossilTubeBlock
import com.cobblemon.mod.common.mixin.invoker.DoorBlockInvoker
import com.cobblemon.mod.common.mixin.invoker.PressurePlateBlockInvoker
import com.cobblemon.mod.common.mixin.invoker.StairsBlockInvoker
@@ -153,6 +156,52 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
@JvmField
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).mapColor(MapColor.DARK_GREEN).burnable().noCollision().breakInstantly().sounds(BlockSoundGroup.CROP)))
@JvmField
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
@JvmField
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
@JvmField
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
@JvmField
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
@JvmField
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
@JvmField
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
@JvmField
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
@JvmField
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
@JvmField
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
@JvmField
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
@JvmField
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
@JvmField
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
@JvmField
val TUMBLESTONE_BLOCK = this.create("tumblestone_block", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
))
@JvmField
val SKY_TUMBLESTONE_BLOCK = this.create("sky_tumblestone_block", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
))
@JvmField
val BLACK_TUMBLESTONE_BLOCK = this.create("black_tumblestone_block", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
))
@JvmField
val BLACK_APRICORN = apricornBlock("black_apricorn", Apricorn.BLACK)
@JvmField
@@ -168,6 +217,42 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
@JvmField
val YELLOW_APRICORN = apricornBlock("yellow_apricorn", Apricorn.YELLOW)
@JvmField
val FOSSIL_MONITOR = create(
"fossil_monitor",
FossilMonitorBlock(
AbstractBlock.Settings.create()
.mapColor(MapColor.IRON_GRAY)
.sounds(BlockSoundGroup.METAL)
.pistonBehavior(PistonBehavior.BLOCK)
.strength(2f)
.luminance { if (it.get(FossilMonitorBlock.PROGRESS) > 0) 15 else 0 }
)
)
@JvmField
val FOSSIL_COMPARTMENT = create(
"fossil_compartment",
FossilCompartmentBlock(
AbstractBlock.Settings.create()
.mapColor(MapColor.IRON_GRAY)
.sounds(BlockSoundGroup.METAL)
.pistonBehavior(PistonBehavior.BLOCK)
.strength(2f)
.nonOpaque()
)
)
@JvmField
val FOSSIL_TUBE = create(
"fossil_tube",
FossilTubeBlock(
AbstractBlock.Settings.create()
.mapColor(MapColor.IRON_GRAY)
.sounds(BlockSoundGroup.METAL)
.pistonBehavior(PistonBehavior.BLOCK)
.strength(2f)
.nonOpaque()
)
)
@JvmField
val HEALING_MACHINE = create(
"healing_machine",
@@ -243,6 +328,15 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
private fun apricornBlock(name: String, apricorn: Apricorn): ApricornBlock = this.create(name, ApricornBlock(AbstractBlock.Settings.create().mapColor(apricorn.mapColor()).ticksRandomly().strength(Blocks.OAK_LOG.hardness, Blocks.OAK_LOG.blastResistance).sounds(BlockSoundGroup.WOOD).nonOpaque(), apricorn))
private fun tumblestoneBlock(name: String, stage: Int, height: Int, xzOffset: Int, nextStage: Block?) : Block {
return this.create(name, TumblestoneBlock(AbstractBlock.Settings.create()
.pistonBehavior(PistonBehavior.DESTROY)
.nonOpaque()
.strength(1.5F)
.sounds(CobblemonSounds.TUMBLESTONE_SOUNDS),
stage, height, xzOffset, nextStage))
}
private val berries = mutableMapOf<Identifier, BerryBlock>()
val AGUAV_BERRY = this.berryBlock("aguav")
@@ -46,5 +46,8 @@ object CobblemonCommands {
ClearPartyCommand.register(dispatcher)
ClearPCCommand.register(dispatcher)
PokemonRestartCommand.register(dispatcher)
RandomBattleCommand.register(dispatcher)
BedrockParticleCommand.register(dispatcher)
OpenDialogueCommand.register(dispatcher)
}
}
@@ -28,7 +28,6 @@ import com.cobblemon.mod.common.item.berry.PortionHealingBerryItem
import com.cobblemon.mod.common.item.berry.StatusCuringBerryItem
import com.cobblemon.mod.common.item.berry.VolatileCuringBerryItem
import com.cobblemon.mod.common.item.interactive.*
import com.cobblemon.mod.common.item.interactive.PotionItem
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
@@ -37,7 +36,13 @@ import net.minecraft.block.Block
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.effect.StatusEffectInstance
import net.minecraft.entity.effect.StatusEffects
import net.minecraft.item.*
import net.minecraft.item.BlockItem
import net.minecraft.item.FoodComponent
import net.minecraft.item.HangingSignItem
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.item.SignItem
import net.minecraft.item.StewItem
import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.registry.RegistryKey
@@ -116,6 +121,38 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val MASTER_BALL = pokeBallItem(PokeBalls.MASTER_BALL)
@JvmField
val CHERISH_BALL = pokeBallItem(PokeBalls.CHERISH_BALL)
@JvmField
val ANCIENT_POKE_BALL = pokeBallItem(PokeBalls.ANCIENT_POKE_BALL)
@JvmField
val ANCIENT_CITRINE_BALL = pokeBallItem(PokeBalls.ANCIENT_CITRINE_BALL)
@JvmField
val ANCIENT_VERDANT_BALL = pokeBallItem(PokeBalls.ANCIENT_VERDANT_BALL)
@JvmField
val ANCIENT_AZURE_BALL = pokeBallItem(PokeBalls.ANCIENT_AZURE_BALL)
@JvmField
val ANCIENT_ROSEATE_BALL = pokeBallItem(PokeBalls.ANCIENT_ROSEATE_BALL)
@JvmField
val ANCIENT_SLATE_BALL = pokeBallItem(PokeBalls.ANCIENT_SLATE_BALL)
@JvmField
val ANCIENT_IVORY_BALL = pokeBallItem(PokeBalls.ANCIENT_IVORY_BALL)
@JvmField
val ANCIENT_GREAT_BALL = pokeBallItem(PokeBalls.ANCIENT_GREAT_BALL)
@JvmField
val ANCIENT_ULTRA_BALL = pokeBallItem(PokeBalls.ANCIENT_ULTRA_BALL)
@JvmField
val ANCIENT_FEATHER_BALL = pokeBallItem(PokeBalls.ANCIENT_FEATHER_BALL)
@JvmField
val ANCIENT_WING_BALL = pokeBallItem(PokeBalls.ANCIENT_WING_BALL)
@JvmField
val ANCIENT_JET_BALL = pokeBallItem(PokeBalls.ANCIENT_JET_BALL)
@JvmField
val ANCIENT_HEAVY_BALL = pokeBallItem(PokeBalls.ANCIENT_HEAVY_BALL)
@JvmField
val ANCIENT_LEADEN_BALL = pokeBallItem(PokeBalls.ANCIENT_LEADEN_BALL)
@JvmField
val ANCIENT_GIGATON_BALL = pokeBallItem(PokeBalls.ANCIENT_GIGATON_BALL)
@JvmField
val ANCIENT_ORIGIN_BALL = pokeBallItem(PokeBalls.ANCIENT_ORIGIN_BALL)
@JvmField
val VIVICHOKE = compostableItem("vivichoke")
@@ -191,6 +228,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val APRICORN_HANGING_SIGN = this.create("apricorn_hanging_sign", HangingSignItem(CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN, Item.Settings().maxCount(16)))
@JvmField
val FOSSIL_TUBE = blockItem("fossil_tube", CobblemonBlocks.FOSSIL_TUBE)
@JvmField
val FOSSIL_COMPARTMENT = blockItem("fossil_compartment", CobblemonBlocks.FOSSIL_COMPARTMENT)
@JvmField
val FOSSIL_MONITOR = blockItem("fossil_monitor", CobblemonBlocks.FOSSIL_MONITOR)
@JvmField
val HEALING_MACHINE = blockItem("healing_machine", CobblemonBlocks.HEALING_MACHINE)
@JvmField
@@ -553,16 +596,28 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
// Held Items
@JvmField
val ABILITY_SHIELD = heldItem("ability_shield")
@JvmField
val ABSORB_BULB = heldItem("absorb_bulb")
@JvmField
val AIR_BALLOON = heldItem("air_balloon")
@JvmField
val ASSAULT_VEST = heldItem("assault_vest")
@JvmField
val BIG_ROOT = blockItem("big_root", CobblemonBlocks.BIG_ROOT)
@JvmField
val BINDING_BAND = heldItem("binding_band")
@JvmField
val BLACK_BELT = heldItem("black_belt")
@JvmField
val BLACK_GLASSES = heldItem("black_glasses")
@JvmField
val BLACK_SLUDGE = heldItem("black_sludge")
@JvmField
val BLUNDER_POLICY = heldItem("blunder_policy")
@JvmField
val CELL_BATTERY = heldItem("cell_battery")
@JvmField
val CHARCOAL = heldItem("charcoal_stick", remappedName = "charcoal")
@JvmField
val CHOICE_BAND = heldItem("choice_band")
@@ -573,26 +628,64 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val CLEANSE_TAG = heldItem("cleanse_tag")
@JvmField
val CLEAR_AMULET = heldItem("clear_amulet")
@JvmField
val COVERT_CLOAK = heldItem("covert_cloak")
@JvmField
val DESTINY_KNOT = heldItem("destiny_knot")
@JvmField
val DRAGON_FANG = heldItem("dragon_fang")
@JvmField
val EJECT_BUTTON = heldItem("eject_button")
@JvmField
val EJECT_PACK = heldItem("eject_pack")
@JvmField
val EVERSTONE = heldItem("everstone")
@JvmField
val EVIOLITE = heldItem("eviolite")
@JvmField
val EXP_SHARE = heldItem("exp_share")
@JvmField
val EXPERT_BELT = heldItem("expert_belt")
@JvmField
val FAIRY_FEATHER = heldItem("fairy_feather")
@JvmField
val FLAME_ORB = heldItem("flame_orb")
@JvmField
val FLOAT_STONE = heldItem("float_stone")
@JvmField
val FOCUS_BAND = heldItem("focus_band")
@JvmField
val FOCUS_SASH = heldItem("focus_sash")
@JvmField
val GRIP_CLAW = heldItem("grip_claw")
@JvmField
val HARD_STONE = heldItem("hard_stone")
@JvmField
val HEAVY_DUTY_BOOTS = heldItem("heavy_duty_boots")
@JvmField
val IRON_BALL = heldItem("iron_ball")
@JvmField
val LAGGING_TAIL = heldItem("lagging_tail")
@JvmField
val LEFTOVERS = heldItem("leftovers")
@JvmField
val LIFE_ORB = heldItem("life_orb")
@JvmField
val LIGHT_BALL = heldItem("light_ball")
@JvmField
val LIGHT_CLAY = heldItem("light_clay")
@JvmField
val LOADED_DICE = heldItem("loaded_dice")
@JvmField
val LUCKY_EGG = heldItem("lucky_egg")
@JvmField
val LUMINOUS_MOSS = heldItem("luminous_moss")
@JvmField
val MAGNET = heldItem("magnet")
@JvmField
val METRONOME = heldItem("metronome")
@JvmField
val MIRACLE_SEED = heldItem("miracle_seed")
@JvmField
val MUSCLE_BAND = heldItem("muscle_band")
@@ -603,14 +696,42 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val POISON_BARB = heldItem("poison_barb")
@JvmField
val POWER_ANKLET = heldItem("power_anklet")
@JvmField
val POWER_BAND = heldItem("power_band")
@JvmField
val POWER_BELT = heldItem("power_belt")
@JvmField
val POWER_BRACER = heldItem("power_bracer")
@JvmField
val POWER_LENS = heldItem("power_lens")
@JvmField
val POWER_WEIGHT = heldItem("power_weight")
@JvmField
val PROTECTIVE_PADS = heldItem("protective_pads")
@JvmField
val PUNCHING_GLOVE = heldItem("punching_glove")
@JvmField
val QUICK_CLAW = heldItem("quick_claw")
@JvmField
val RED_CARD = heldItem("red_card")
@JvmField
val RING_TARGET = heldItem("ring_target")
@JvmField
val ROCKY_HELMET = heldItem("rocky_helmet")
@JvmField
val ROOM_SERVICE = heldItem("room_service")
@JvmField
val SAFETY_GOGGLES = heldItem("safety_goggles")
@JvmField
val SCOPE_LENS = heldItem("scope_lens")
@JvmField
val SHARP_BEAK = heldItem("sharp_beak")
@JvmField
val SHED_SHELL = heldItem("shed_shell")
@JvmField
val SHELL_BELL = heldItem("shell_bell")
@JvmField
val SILK_SCARF = heldItem("silk_scarf")
@JvmField
val SILVER_POWDER = heldItem("silver_powder")
@@ -619,10 +740,28 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val SPELL_TAG = heldItem("spell_tag")
@JvmField
val SMOKE_BALL = heldItem("smoke_ball")
@JvmField
val STICKY_BARB = heldItem("sticky_barb")
@JvmField
val TERRAIN_EXTENDER = heldItem("terrain_extender")
@JvmField
val THROAT_SPRAY = heldItem("throat_spray")
@JvmField
val TOXIC_ORB = heldItem("toxic_orb")
@JvmField
val TWISTED_SPOON = heldItem("twisted_spoon")
@JvmField
val UTILITY_UMBRELLA = heldItem("utility_umbrella")
@JvmField
val WEAKNESS_POLICY = heldItem("weakness_policy")
@JvmField
val WIDE_LENS = heldItem("wide_lens")
@JvmField
val WISE_GLASSES = heldItem("wise_glasses")
@JvmField
val ZOOM_LENS = heldItem("zoom_lens")
@JvmField
val MENTAL_HERB = compostableHeldItem("mental_herb", null, 1F)
@JvmField
val MIRROR_HERB = compostableHeldItem("mirror_herb", null, 1F)
@@ -637,21 +776,13 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val QUICK_POWDER = heldItem("quick_powder")
@JvmField
val DESTINY_KNOT = heldItem("destiny_knot")
val DAMP_ROCK = heldItem("damp_rock")
@JvmField
val EVERSTONE = heldItem("everstone")
val HEAT_ROCK = heldItem("heat_rock")
@JvmField
val POWER_ANKLET = heldItem("power_anklet")
val SMOOTH_ROCK = heldItem("smooth_rock")
@JvmField
val POWER_BAND = heldItem("power_band")
@JvmField
val POWER_BELT = heldItem("power_belt")
@JvmField
val POWER_BRACER = heldItem("power_bracer")
@JvmField
val POWER_LENS = heldItem("power_lens")
@JvmField
val POWER_WEIGHT = heldItem("power_weight")
val ICY_ROCK = heldItem("icy_rock")
// Mulch
@JvmField
@@ -673,6 +804,90 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val SURPRISE_MULCH = mulchItem("surprise_mulch", MulchVariant.SURPRISE)
// Archaeology
@JvmField
val ARMOR_FOSSIL = noSettingsItem("armor_fossil")
@JvmField
val BIRD_FOSSIL = noSettingsItem("bird_fossil")
@JvmField
val CLAW_FOSSIL = noSettingsItem("claw_fossil")
@JvmField
val COVER_FOSSIL = noSettingsItem("cover_fossil")
@JvmField
val DINO_FOSSIL = noSettingsItem("dino_fossil")
@JvmField
val DOME_FOSSIL = noSettingsItem("dome_fossil")
@JvmField
val DRAKE_FOSSIL = noSettingsItem("drake_fossil")
@JvmField
val FISH_FOSSIL = noSettingsItem("fish_fossil")
@JvmField
val HELIX_FOSSIL = noSettingsItem("helix_fossil")
@JvmField
val JAW_FOSSIL = noSettingsItem("jaw_fossil")
@JvmField
val OLD_AMBER_FOSSIL = noSettingsItem("old_amber_fossil")
@JvmField
val PLUME_FOSSIL = noSettingsItem("plume_fossil")
@JvmField
val ROOT_FOSSIL = noSettingsItem("root_fossil")
@JvmField
val SAIL_FOSSIL = noSettingsItem("sail_fossil")
@JvmField
val SKULL_FOSSIL = noSettingsItem("skull_fossil")
@JvmField
val BYGONE_SHERD = noSettingsItem("bygone_sherd")
@JvmField
val CAPTURE_SHERD = noSettingsItem("capture_sherd")
@JvmField
val DOME_SHERD = noSettingsItem("dome_sherd")
@JvmField
val HELIX_SHERD = noSettingsItem("helix_sherd")
@JvmField
val NOSTALGIC_SHERD = noSettingsItem("nostalgic_sherd")
@JvmField
val TUMBLESTONE = this.create("tumblestone", TumblestoneItem(Item.Settings(), CobblemonBlocks.SMALL_BUDDING_TUMBLESTONE))
@JvmField
val BLACK_TUMBLESTONE = this.create("black_tumblestone", TumblestoneItem(Item.Settings(), CobblemonBlocks.SMALL_BUDDING_BLACK_TUMBLESTONE))
@JvmField
val SKY_TUMBLESTONE = this.create("sky_tumblestone", TumblestoneItem(Item.Settings(), CobblemonBlocks.SMALL_BUDDING_SKY_TUMBLESTONE))
@JvmField
val SMALL_BUDDING_TUMBLESTONE = blockItem("small_budding_tumblestone", CobblemonBlocks.SMALL_BUDDING_TUMBLESTONE)
@JvmField
val MEDIUM_BUDDING_TUMBLESTONE = blockItem("medium_budding_tumblestone", CobblemonBlocks.MEDIUM_BUDDING_TUMBLESTONE)
@JvmField
val LARGE_BUDDING_TUMBLESTONE = blockItem("large_budding_tumblestone", CobblemonBlocks.LARGE_BUDDING_TUMBLESTONE)
@JvmField
val TUMBLESTONE_CLUSTER = blockItem("tumblestone_cluster", CobblemonBlocks.TUMBLESTONE_CLUSTER)
@JvmField
val SMALL_BUDDING_SKY_TUMBLESTONE = blockItem("small_budding_sky_tumblestone", CobblemonBlocks.SMALL_BUDDING_SKY_TUMBLESTONE)
@JvmField
val MEDIUM_BUDDING_SKY_TUMBLESTONE = blockItem("medium_budding_sky_tumblestone", CobblemonBlocks.MEDIUM_BUDDING_SKY_TUMBLESTONE)
@JvmField
val LARGE_BUDDING_SKY_TUMBLESTONE = blockItem("large_budding_sky_tumblestone", CobblemonBlocks.LARGE_BUDDING_SKY_TUMBLESTONE)
@JvmField
val SKY_TUMBLESTONE_CLUSTER = blockItem("sky_tumblestone_cluster", CobblemonBlocks.SKY_TUMBLESTONE_CLUSTER)
@JvmField
val SMALL_BUDDING_BLACK_TUMBLESTONE = blockItem("small_budding_black_tumblestone", CobblemonBlocks.SMALL_BUDDING_BLACK_TUMBLESTONE)
@JvmField
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = blockItem("medium_budding_black_tumblestone", CobblemonBlocks.MEDIUM_BUDDING_BLACK_TUMBLESTONE)
@JvmField
val LARGE_BUDDING_BLACK_TUMBLESTONE = blockItem("large_budding_black_tumblestone", CobblemonBlocks.LARGE_BUDDING_BLACK_TUMBLESTONE)
@JvmField
val BLACK_TUMBLESTONE_CLUSTER = blockItem("black_tumblestone_cluster", CobblemonBlocks.BLACK_TUMBLESTONE_CLUSTER)
@JvmField
val TUMBLESTONE_BLOCK = blockItem("tumblestone_block", CobblemonBlocks.TUMBLESTONE_BLOCK)
@JvmField
val SKY_TUMBLESTONE_BLOCK = blockItem("sky_tumblestone_block", CobblemonBlocks.SKY_TUMBLESTONE_BLOCK)
@JvmField
val BLACK_TUMBLESTONE_BLOCK = blockItem("black_tumblestone_block", CobblemonBlocks.BLACK_TUMBLESTONE_BLOCK)
// Misc
@JvmField
val POKEMON_MODEL = this.create("pokemon_model", PokemonItem())
@@ -37,6 +37,8 @@ import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandl
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
@@ -81,7 +83,11 @@ import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.PropertiesCompletionRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import com.cobblemon.mod.common.net.messages.client.fossil.FossilRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.fossil.NaturalMaterialRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonPasturedPacket
@@ -129,6 +135,8 @@ import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonS
import com.cobblemon.mod.common.net.messages.server.callback.party.PartySelectCancelledPacket
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyMoveSelectCancelledPacket
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyPokemonMoveSelectedPacket
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
import com.cobblemon.mod.common.net.messages.server.dialogue.InputToDialoguePacket
import com.cobblemon.mod.common.net.messages.server.pasture.PasturePokemonPacket
import com.cobblemon.mod.common.net.messages.server.pasture.UnpastureAllPokemonPacket
import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPacket
@@ -162,6 +170,8 @@ import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSe
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartySelectCancelledHandler
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyMoveSelectCancelledHandler
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyPokemonMoveSelectedHandler
import com.cobblemon.mod.common.net.serverhandling.dialogue.EscapeDialogueHandler
import com.cobblemon.mod.common.net.serverhandling.dialogue.InputToDialogueHandler
import com.cobblemon.mod.common.net.serverhandling.evolution.AcceptEvolutionHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.PasturePokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpastureAllPokemonHandler
@@ -233,6 +243,7 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(PokemonStateUpdatePacket.ID, PokemonStateUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(TetheringUpdatePacket.ID, TetheringUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(TradeableUpdatePacket.ID, TradeableUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(OriginalTrainerUpdatePacket.ID, OriginalTrainerUpdatePacket::decode, PokemonUpdatePacketHandler())
// Evolution start
this.createClientBound(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler())
@@ -298,6 +309,8 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler)
this.createClientBound(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(NaturalMaterialRegistrySyncPacket.ID, NaturalMaterialRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(FossilRegistrySyncPacket.ID, FossilRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
// Effects
this.createClientBound(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler)
@@ -334,6 +347,10 @@ object CobblemonNetwork : NetworkManager {
// Party move select packets
this.createClientBound(OpenPartyMoveCallbackPacket.ID, OpenPartyMoveCallbackPacket::decode, OpenPartyMoveCallbackHandler)
// Dialogue packets
this.createClientBound(DialogueClosedPacket.ID, DialogueClosedPacket::decode, DialogueClosedHandler)
this.createClientBound(DialogueOpenedPacket.ID, DialogueOpenedPacket::decode, DialogueOpenedHandler)
}
override fun registerServerBound() {
@@ -399,6 +416,10 @@ object CobblemonNetwork : NetworkManager {
// Party move select packets
this.createServerBound(PartyPokemonMoveSelectedPacket.ID, PartyPokemonMoveSelectedPacket::decode, PartyPokemonMoveSelectedHandler)
this.createServerBound(PartyMoveSelectCancelledPacket.ID, PartyMoveSelectCancelledPacket::decode, PartyMoveSelectCancelledHandler)
// Dialogue packets
this.createServerBound(EscapeDialoguePacket.ID, EscapeDialoguePacket::decode, EscapeDialogueHandler)
this.createServerBound(InputToDialoguePacket.ID, InputToDialoguePacket::decode, InputToDialogueHandler)
}
private inline fun <reified T : NetworkPacket<T>> createClientBound(identifier: Identifier, noinline decoder: (PacketByteBuf) -> T, handler: ClientNetworkPacketHandler<T>) {
@@ -14,6 +14,7 @@ import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.sound.SoundEvent
object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Registry<SoundEvent>>, SoundEvent>() {
@@ -88,6 +89,57 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
@JvmField
val MULCH_REMOVE = this.create("mulch.remove")
@JvmField
val FOSSIL_MACHINE_ACTIVATE = this.create("fossilmachine.activate")
@JvmField
val FOSSIL_MACHINE_ACTIVE_LOOP = this.create("fossilmachine.active_loop")
@JvmField
val FOSSIL_MACHINE_ASSEMBLE = this.create("fossilmachine.assemble")
@JvmField
val FOSSIL_MACHINE_DNA_FULL = this.create("fossilmachine.dna_full")
@JvmField
val FOSSIL_MACHINE_FINISHED = this.create("fossilmachine.finished")
@JvmField
val FOSSIL_MACHINE_INSERT_DNA = this.create("fossilmachine.insert_dna")
@JvmField
val FOSSIL_MACHINE_INSERT_DNA_SMALL = this.create("fossilmachine.insert_dna_small")
@JvmField
val FOSSIL_MACHINE_INSERT_FOSSIL = this.create("fossilmachine.insert_fossil")
@JvmField
val FOSSIL_MACHINE_RETRIEVE_FOSSIL = this.create("fossilmachine.retrieve_fossil")
@JvmField
val FOSSIL_MACHINE_RETRIEVE_POKEMON = this.create("fossilmachine.retrieve_pokemon")
@JvmField
val FOSSIL_MACHINE_UNPROTECTED = this.create("fossilmachine.unprotected")
@JvmField
val TUMBLESTONE_BREAK = this.create("tumblestone.break")
@JvmField
val TUMBLESTONE_BLOCK_BREAK = this.create("tumblestone.block_break")
@JvmField
val TUMBLESTONE_HIT = this.create("tumblestone.hit")
@JvmField
val TUMBLESTONE_PLACE = this.create("tumblestone.place")
@JvmField
val TUMBLESTONE_STEP = this.create("tumblestone.step")
@JvmField
val TUMBLESTONE_SOUNDS = BlockSoundGroup(1f, 1f,
TUMBLESTONE_BREAK,
TUMBLESTONE_STEP,
TUMBLESTONE_PLACE,
TUMBLESTONE_HIT,
TUMBLESTONE_STEP
)
@JvmField
val TUMBLESTONE_BLOCK_SOUNDS = BlockSoundGroup(1f, 1f,
TUMBLESTONE_BLOCK_BREAK,
TUMBLESTONE_STEP,
TUMBLESTONE_PLACE,
TUMBLESTONE_HIT,
TUMBLESTONE_STEP
)
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.of(cobblemonResource(name)))
}
@@ -99,6 +99,13 @@ object CobblemonCriteria {
)
)
val RESURRECT_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("resurrect_pokemon"),
PickStarterCriterionCondition::class.java
)
)
val TRADE_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("trade_pokemon"),
@@ -46,9 +46,9 @@ import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.getPlayer
import java.io.File
import java.util.UUID
import java.util.concurrent.ConcurrentLinkedQueue
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import java.util.concurrent.ConcurrentLinkedDeque
/**
* Individual battle instance
@@ -69,6 +69,7 @@ open class PokemonBattle(
side1.battle = this
side2.battle = this
this.actors.forEach { actor ->
actor.battle = this
actor.pokemonList.forEach { battlePokemon ->
battlePokemon.effectedPokemon.evolutionProxy.current().progress()
.filterIsInstance<LastBattleCriticalHitsEvolutionProgress>()
@@ -95,6 +96,7 @@ open class PokemonBattle(
val chatLog = mutableListOf<Text>()
var started = false
var ended = false
var simulation = false
// TEMP battle showcase stuff
var announcingRules = false
var turn: Int = 1
@@ -102,7 +104,7 @@ open class PokemonBattle(
var dispatchResult = GO
val dispatches = ConcurrentLinkedQueue<BattleDispatch>()
val dispatches = ConcurrentLinkedDeque<BattleDispatch>()
val afterDispatches = mutableListOf<() -> Unit>()
val captureActions = mutableListOf<BattleCaptureAction>()
@@ -204,7 +206,8 @@ open class PokemonBattle(
fun end() {
ended = true
this.actors.forEach { actor ->
// award XP if it is not a simulation battle
if (simulation == false) this.actors.forEach { actor ->
val faintedPokemons = actor.pokemonList.filter { it.health <= 0 }
actor.getSide().getOppositeSide().actors.forEach { opponent ->
val opponentNonFaintedPokemons = opponent.pokemonList.filter { it.health > 0 }
@@ -252,7 +255,6 @@ open class PokemonBattle(
}
}
sendUpdate(BattleEndPacket())
sendUpdate(BattleMusicPacket(null))
BattleRegistry.closeBattle(this)
}
@@ -319,6 +321,12 @@ open class PokemonBattle(
fun dispatch(dispatcher: () -> DispatchResult) {
dispatches.add(BattleDispatch { dispatcher() })
}
fun dispatchToFront(dispatcher: () -> DispatchResult) {
dispatches.addFirst(BattleDispatch { dispatcher() })
}
fun dispatchGo(dispatcher: () -> Unit) {
@@ -350,6 +358,10 @@ open class PokemonBattle(
dispatches.add(dispatcher)
}
fun dispatchToFront(dispatcher: BattleDispatch) {
dispatches.addFirst(dispatcher)
}
fun doWhenClear(action: () -> Unit) {
afterDispatches.add(action)
}
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.battles.model.ai.BattleAI
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.battles.PassActionResponse
import com.cobblemon.mod.common.battles.ai.StrongBattleAI
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.exception.IllegalActionChoiceException
import com.cobblemon.mod.common.net.messages.client.battle.BattleMakeChoicePacket
@@ -20,7 +21,7 @@ import java.util.UUID
abstract class AIBattleActor(
gameId: UUID,
pokemonList: List<BattlePokemon>,
val battleAI: BattleAI
val battleAI: BattleAI = StrongBattleAI(0)
) : BattleActor(gameId, pokemonList.toMutableList()) {
override fun sendUpdate(packet: NetworkPacket<*>) {
super.sendUpdate(packet)
@@ -0,0 +1,125 @@
/*
* 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.dialogue
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.MoStruct
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.dialogue.input.ActiveInput
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.scheduling.ServerRealTimeTaskTracker
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
import java.util.UUID
import net.minecraft.server.network.ServerPlayerEntity
/**
* An active instance of [Dialogue]. Bound to a player. These all need to be created through DialogueManager
* or things will go to shit.
*
* @author Hiroku
* @since December 27th, 2023
*/
class ActiveDialogue(var playerEntity: ServerPlayerEntity, var dialogueReference: Dialogue) {
val dialogueId = UUID.randomUUID()
val runtime = MoLangRuntime().setup()
var currentPage = dialogueReference.pages[0]
val playerStruct = playerEntity.asMoLangValue()
var activeInput = ActiveInput(this, currentPage.input)
init {
runtime.environment.getQueryStruct().addFunctions(
mapOf(
"dialogue" to java.util.function.Function { _ -> toMoLangStruct() },
"player" to java.util.function.Function { _ -> playerStruct }
)
)
}
val currentPageIndex
get() = dialogueReference.pages.indexOf(currentPage)
fun setPage(value: MoValue) {
val page = if (value is StringValue) {
dialogueReference.pages.find { it.id == value.value }
?: return Cobblemon.LOGGER.error("Dialogue requested page ${value.value} but it doesn't exist")
} else {
val pageNum = value.asDouble().toInt()
if (pageNum < 0 || pageNum >= dialogueReference.pages.size) {
return Cobblemon.LOGGER.error("Dialogue requested page $pageNum but it doesn't exist")
} else {
dialogueReference.pages[pageNum]
}
}
setPage(page)
}
fun isActive() = DialogueManager.activeDialogues[playerEntity.uuid] == this
fun incrementPage() {
setPage(currentPageIndex + 1)
}
fun setPage(page: DialoguePage) {
currentPage = page
activeInput = ActiveInput(this, currentPage.input)
val deadline = currentPage.input.timeout?.duration
val inputId = activeInput.inputId
if (deadline != null && deadline > 0) {
ServerRealTimeTaskTracker.after(seconds = deadline) {
if (inputId == activeInput.inputId && isActive()) {
activeInput.dialogueInput.timeout?.action?.invoke(this, null)
}
}
}
playerEntity.sendPacket(DialogueOpenedPacket(this, false))
}
fun setPage(index: Int) {
if (index == dialogueReference.pages.size) {
return close()
}
if (index < 0 || index > dialogueReference.pages.size) {
return Cobblemon.LOGGER.error("Dialogue requested page $index but it doesn't exist")
}
setPage(dialogueReference.pages[index])
}
fun toMoLangStruct(): MoStruct {
return QueryStruct(
hashMapOf(
"current_page" to java.util.function.Function { _ -> currentPage.toMoLangStruct(this) },
"current_page_number" to java.util.function.Function { _ -> DoubleValue(currentPageIndex) },
"next_page" to java.util.function.Function { _ -> incrementPage() },
"set_page" to java.util.function.Function { args -> setPage(args[0]) },
"close" to java.util.function.Function { _ -> close() },
"input" to java.util.function.Function { params -> activeInput.handle(if (params.params.size > 0) params.get<MoValue>(0).asString() else "") },
)
).addStandardFunctions()
}
fun close() {
DialogueManager.stopDialogue(playerEntity)
}
fun escape() {
val action = currentPage.escapeAction ?: dialogueReference.escapeAction
action(this, null)
}
}
@@ -0,0 +1,50 @@
/*
* 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.dialogue
import com.cobblemon.mod.common.api.molang.ExpressionLike
/**
* A dialogue that could be sent to players.
*
* @author Hiroku
* @since December 27th, 2023
*/
class Dialogue(
val pages: List<DialoguePage> = mutableListOf(),
val escapeAction: DialogueAction = FunctionDialogueAction { dialogue, _ -> dialogue.close() },
val speakers: Map<String, DialogueSpeaker> = emptyMap()
) {
companion object {
fun of(
pages: Iterable<DialoguePage>,
escapeAction: ExpressionLike,
speakers: Map<String, DialogueSpeaker>
): Dialogue {
return Dialogue(
pages = pages.toList(),
escapeAction = ExpressionLikeDialogueAction(escapeAction),
speakers = speakers
)
}
fun of(
pages: Iterable<DialoguePage>,
escapeAction: (ActiveDialogue) -> Unit,
speakers: Map<String, DialogueSpeaker>
): Dialogue {
val dialogue = Dialogue(
pages = pages.toList(),
escapeAction = FunctionDialogueAction { activeDialogue, _ -> escapeAction(activeDialogue) },
speakers = speakers
)
return dialogue
}
}
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.dialogue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.util.resolve
/**
* Something that can happen in the context of an [ActiveDialogue]. The input field is nullable because I'm reusing
* this for dialogue input timeouts which don't have an input. It'll be fine.
*
* @author Hiroku
* @since December 29th, 2023
*/
interface DialogueAction {
companion object {
@JvmStatic
val types = mutableMapOf<String, Class<out DialogueAction>>()
}
operator fun invoke(dialogue: ActiveDialogue, input: String? = null)
}
class FunctionDialogueAction(val consumer: (ActiveDialogue, String?) -> Unit) : DialogueAction {
override fun invoke(dialogue: ActiveDialogue, input: String?) {
consumer(dialogue, input)
}
}
class ExpressionLikeDialogueAction(val expression: ExpressionLike) : DialogueAction {
override fun invoke(dialogue: ActiveDialogue, input: String?) {
if (input != null) {
dialogue.runtime.environment.setSimpleVariable("selected_option", StringValue(input))
}
dialogue.runtime.resolve(expression)
}
}
@@ -0,0 +1,46 @@
/*
* 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.dialogue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.util.cobblemonResource
import java.util.UUID
import net.minecraft.util.Identifier
/**
* Something that produces a dialogue's renderable face. This is sealed because the client has very particular handling for this.
*
* @author Hiroku
* @since January 1st, 2024
*/
sealed interface DialogueFaceProvider {
companion object {
val types = mutableMapOf(
"player" to PlayerDialogueFaceProvider::class.java,
"standard" to ArtificialDialogueFaceProvider::class.java,
"expression" to ExpressionLikeDialogueFaceProvider::class.java
)
}
}
class ArtificialDialogueFaceProvider(
val modelType: String = "",
val identifier: Identifier = cobblemonResource("bulbasaur"),
val aspects: Set<String> = setOf()
) : DialogueFaceProvider
class ReferenceDialogueFaceProvider(
val entityId: Int
): DialogueFaceProvider
class PlayerDialogueFaceProvider(val playerId: UUID) : DialogueFaceProvider
class ExpressionLikeDialogueFaceProvider(
val providerExpression: ExpressionLike
): DialogueFaceProvider
@@ -0,0 +1,41 @@
/*
* 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.dialogue
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
import com.cobblemon.mod.common.util.activeDialogue
import java.util.UUID
import net.minecraft.server.network.ServerPlayerEntity
/**
* Manages the active dialogues for players. Map is indexed by player UUID.
* You really need to make sure any dialogues you start with a player go through this otherwise
* the player won't be able to close the dialogue.
*
* @author Hiroku
* @since December 27th, 2023
*/
object DialogueManager {
val activeDialogues = mutableMapOf<UUID, ActiveDialogue>()
fun startDialogue(playerEntity: ServerPlayerEntity, dialogue: Dialogue) {
val activeDialogue = ActiveDialogue(playerEntity, dialogue)
activeDialogues[playerEntity.uuid] = activeDialogue
val packet = DialogueOpenedPacket(activeDialogue, includeFaces = true)
playerEntity.sendPacket(packet)
}
fun stopDialogue(playerEntity: ServerPlayerEntity) {
val activeDialogue = playerEntity.activeDialogue ?: return
DialogueClosedPacket(activeDialogue.dialogueId).sendToPlayer(playerEntity)
activeDialogues.remove(activeDialogue.dialogueId)
}
}
@@ -0,0 +1,71 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.dialogue
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.struct.MoStruct
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.dialogue.input.DialogueInput
import com.cobblemon.mod.common.api.dialogue.input.DialogueNoInput
import com.google.gson.JsonArray
import net.minecraft.text.MutableText
/**
* A page of a dialogue. This has lines of text and also some kind of input expected from the player.
*
* @author Hiroku
* @since December 27th, 2023
*/
class DialoguePage(
var id: String = "",
var speaker: String? = null,
var lines: MutableList<DialogueText> = mutableListOf(),
var input: DialogueInput = DialogueNoInput(),
var clientActions: MutableList<Expression> = mutableListOf(),
var escapeAction: DialogueAction? = null,
) {
companion object {
@JvmOverloads
fun of(
/** The ID is optional, but if you want to be able to jump to this page from other pages then you probably want to set this. */
id: String = "",
speaker: String? = null,
lines: Iterable<MutableText>,
input: DialogueInput = DialogueNoInput(),
clientActions: Iterable<Expression> = emptyList(),
/** The thing to do when the player presses ESC while on this page. If null, falls back to the same property on [Dialogue]. */
escapeAction: ((ActiveDialogue) -> Unit)? = null,
): DialoguePage {
return DialoguePage(
id = id,
speaker = speaker,
lines = lines.map { WrappedDialogueText(it) }.toMutableList(),
input = input,
clientActions = clientActions.toMutableList(),
escapeAction = escapeAction?.let { func -> FunctionDialogueAction { activeDialogue, _ -> func(activeDialogue) } }
)
}
}
fun toMoLangStruct(activeDialogue: ActiveDialogue): MoStruct {
return QueryStruct(
hashMapOf(
"id" to java.util.function.Function { _ -> StringValue(id) },
"input" to java.util.function.Function { _ -> activeDialogue.activeInput.struct },
"lines" to java.util.function.Function { _ ->
val array = JsonArray()
lines.forEach { array.add(it(activeDialogue).string) }
MoValue.of(array)
}
)
)
}
}
@@ -0,0 +1,35 @@
/*
* 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.dialogue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
/**
* A predicate loaded for a particular dialogue. Tests arbitrary conditions about the dialogue context.
*
* @author Hiroku
* @since December 29th, 2023
*/
interface DialoguePredicate {
companion object {
val types = mutableMapOf<String, Class<out DialoguePredicate>>()
}
operator fun invoke(dialogue: ActiveDialogue): Boolean
}
class ExpressionLikeDialoguePredicate(val expression: ExpressionLike = "true".asExpressionLike()) : DialoguePredicate {
override fun invoke(dialogue: ActiveDialogue) = dialogue.runtime.resolveBoolean(expression)
}
class FunctionDialoguePredicate(val predicate: (ActiveDialogue) -> Boolean = { true }) : DialoguePredicate {
override fun invoke(dialogue: ActiveDialogue) = predicate(dialogue)
}
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.dialogue
import com.cobblemon.mod.common.api.text.text
import net.minecraft.text.MutableText
/**
* A kind of speaker in a dialogue. They include a name and a face, each optional.
*
* @author Hiroku
* @since January 1st, 2024
*/
class DialogueSpeaker(
val name: DialogueText? = null,
val face: DialogueFaceProvider? = null
) {
fun of(name: MutableText = "".text(), face: DialogueFaceProvider? = null) = DialogueSpeaker(WrappedDialogueText(name), face)
}
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.dialogue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveString
import net.minecraft.text.MutableText
/**
* Some kind of text-resolving property. This is to hide where sometimes we want literals,
* sometimes we want the text to come from a function, and sometimes we want it to be the
* product of a MoLang script.
*
* @author Hiroku
* @since December 29th, 2023
*/
interface DialogueText {
companion object {
val types = mutableMapOf<String, Class<out DialogueText>>(
"expression" to ExpressionLikeDialogueText::class.java,
)
}
operator fun invoke(activeDialogue: ActiveDialogue): MutableText
}
class FunctionDialogueText(val function: (ActiveDialogue) -> MutableText = { "".text() }) : DialogueText {
override fun invoke(activeDialogue: ActiveDialogue) = function(activeDialogue)
}
class WrappedDialogueText(val text: MutableText = "".text()) : DialogueText {
override fun invoke(activeDialogue: ActiveDialogue) = text.copy()
}
class ExpressionLikeDialogueText(val expression: ExpressionLike = "''".asExpressionLike()) : DialogueText {
override fun invoke(activeDialogue: ActiveDialogue): MutableText {
return activeDialogue.runtime.resolveString(expression).text()
}
}
@@ -0,0 +1,72 @@
/*
* 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.dialogue
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.dialogue.input.DialogueInput
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.util.adapters.DialogueActionAdapter
import com.cobblemon.mod.common.util.adapters.DialogueFaceProviderAdapter
import com.cobblemon.mod.common.util.adapters.DialogueInputAdapter
import com.cobblemon.mod.common.util.adapters.DialoguePredicateAdapter
import com.cobblemon.mod.common.util.adapters.DialogueTextAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.TextAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.MutableText
import net.minecraft.text.Text
import net.minecraft.util.Identifier
/**
* Registry for dialogue data.
*
* @see Dialogue
* @since December 29th, 2023
* @author Hiroku
*/
object Dialogues : JsonDataRegistry<Dialogue> {
override val id = cobblemonResource("dialogues")
override val type = ResourceType.SERVER_DATA
override val observable = SimpleObservable<Dialogues>()
val dialogues = mutableMapOf<Identifier, Dialogue>()
/** If you need custom adapters registered, subscribe to this and register them. */
val gsonObservable: SimpleObservable<GsonBuilder> = SimpleObservable()
override fun sync(player: ServerPlayerEntity) {}
override val gson = GsonBuilder()
.registerTypeAdapter(DialogueAction::class.java, DialogueActionAdapter)
.registerTypeAdapter(DialoguePredicate::class.java, DialoguePredicateAdapter)
.registerTypeAdapter(DialogueInput::class.java, DialogueInputAdapter)
.registerTypeAdapter(DialogueFaceProvider::class.java, DialogueFaceProviderAdapter)
.registerTypeAdapter(DialogueText::class.java, DialogueTextAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(MutableText::class.java, TextAdapter)
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.also { gsonObservable.emit(it) }
.create()
override val typeToken = TypeToken.get(Dialogue::class.java)
override val resourcePath = "dialogues"
override fun reload(data: Map<Identifier, Dialogue>) {
dialogues.putAll(data)
observable.emit(this)
}
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.dialogue.input
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.dialogue.ActiveDialogue
import com.cobblemon.mod.common.util.server
import java.util.UUID
/**
* An active input waiting for the player as part of an [ActiveDialogue]. The purpose of this is to
* maintain some data integrity and track how long it's taken to choose an action.
*
* @author Hiroku
* @since December 27th, 2023
*/
class ActiveInput(
val activeDialogue: ActiveDialogue,
val dialogueInput: DialogueInput
) {
val inputId = UUID.randomUUID()
val startTime = server()!!.overworld.time
val struct = toMoLangStruct()
fun handle(input: String) {
val secondsToChoose = (server()!!.overworld.time - startTime) / 20F
activeDialogue.runtime.environment.setSimpleVariable("seconds_taken_to_input", DoubleValue(secondsToChoose))
dialogueInput.handle(this, input)
}
fun toMoLangStruct(): MoValue {
return dialogueInput.toMoLangStruct(this)
}
}
@@ -0,0 +1,43 @@
/*
* 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.dialogue.input
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.dialogue.DialogueAction
import com.cobblemon.mod.common.api.dialogue.FunctionDialogueAction
/**
* Input that has no input but has an action that will run after a delay.
*
* @author Hiroku
* @since December 27th, 2023
*/
class DialogueAutoContinueInput : DialogueInput {
val delay = 5F
/** Whether the player can click to move on in a hurry or if they must wait. */
var allowSkip = true
var showTimer = false
val action: DialogueAction = FunctionDialogueAction { dialogue, _ -> dialogue.incrementPage() }
override var timeout: DialogueTimeout?
get() = DialogueTimeout(duration = delay, showTimer = showTimer, action)
set(_) {}
override fun toMoLangStruct(activeInput: ActiveInput) = QueryStruct(hashMapOf())
override fun handle(activeInput: ActiveInput, value: String) {
if (!allowSkip) {
return Cobblemon.LOGGER.warn("A no-skip dialogue received input from ${activeInput.activeDialogue.playerEntity.gameProfile.name}, is this person a hacker or something")
}
action.invoke(activeInput.activeDialogue)
}
}
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.dialogue.input
import com.bedrockk.molang.runtime.struct.MoStruct
import com.cobblemon.mod.common.api.dialogue.DialoguePage
/**
* A type of input to [DialoguePage]s. This is sealed because the client needs to know about it. Sorry.
*
* @author Hiroku
* @since December 27th, 2023
*/
sealed interface DialogueInput {
var timeout: DialogueTimeout?
fun toMoLangStruct(activeInput: ActiveInput): MoStruct
fun handle(activeInput: ActiveInput, value: String)
}
@@ -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.api.dialogue.input
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.cobblemon.mod.common.api.dialogue.DialogueAction
import com.cobblemon.mod.common.api.dialogue.FunctionDialogueAction
/**
* An input type that's really just waiting for the player to click or something.
*
* @author Hiroku
* @since December 27th, 2023
*/
class DialogueNoInput(
var action: DialogueAction = FunctionDialogueAction { dialogue, _ -> dialogue.incrementPage() }
) : DialogueInput {
override var timeout: DialogueTimeout?
get() = null
set(_) {}
override fun toMoLangStruct(activeInput: ActiveInput) = VariableStruct()
override fun handle(activeInput: ActiveInput, value: String) {
action(activeInput.activeDialogue)
}
}
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.dialogue.input
import com.cobblemon.mod.common.api.dialogue.DialogueAction
import com.cobblemon.mod.common.api.dialogue.DialoguePredicate
import com.cobblemon.mod.common.api.dialogue.DialogueText
import com.cobblemon.mod.common.api.dialogue.FunctionDialogueAction
import com.cobblemon.mod.common.api.dialogue.FunctionDialoguePredicate
import com.cobblemon.mod.common.api.dialogue.WrappedDialogueText
/**
* A choosable option as part of a [DialogueOptionSetInput].
*
* @author Hiroku
* @since December 27th, 2023
*/
class DialogueOption(
var text: DialogueText = WrappedDialogueText(),
/** The value is a unique identifier so the client can tell the server what they selected. Don't forget this. */
var value: String = "",
/** The action to run when they click it. */
var action: DialogueAction = FunctionDialogueAction { _, _ -> },
val isVisible: DialoguePredicate = FunctionDialoguePredicate(),
val isSelectable: DialoguePredicate = FunctionDialoguePredicate()
)
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.dialogue.input
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.dialogue.ActiveDialogue
/**
* A type of [DialogueInput] that has the player choose from a list of options.
*
* @author Hiroku
* @since December 27th, 2023
*/
class DialogueOptionSetInput(
var options: MutableList<DialogueOption>,
override var timeout: DialogueTimeout? = null,
/** Whether the buttons should be stacked vertically. */
var vertical: Boolean = false,
) : DialogueInput {
// JSON uses this. Basically so that the main constructor can force people to specify options
constructor() : this(mutableListOf(), null, false)
fun getVisibleOptions(activeDialogue: ActiveDialogue) = options.filter { it.isVisible(activeDialogue) }
override fun toMoLangStruct(activeInput: ActiveInput) = QueryStruct(hashMapOf())
override fun handle(activeInput: ActiveInput, value: String) {
val option = options.firstOrNull { it.value == value }
if (option != null) {
if (!option.isSelectable(activeInput.activeDialogue)) {
Cobblemon.LOGGER.warn("Dialogue option $value is not selectable but ${activeInput.activeDialogue.playerEntity.gameProfile.name} selected it anyway")
activeInput.activeDialogue.close()
} else if (!option.isVisible(activeInput.activeDialogue)) {
Cobblemon.LOGGER.warn("Dialogue option $value is not visible but ${activeInput.activeDialogue.playerEntity.gameProfile.name} selected it anyway")
activeInput.activeDialogue.close()
}
option.action(activeInput.activeDialogue, value)
} else {
Cobblemon.LOGGER.warn("No option with value $value found in dialogue option set")
}
}
}
@@ -0,0 +1,29 @@
/*
* 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.dialogue.input
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.dialogue.DialogueAction
import com.cobblemon.mod.common.api.dialogue.FunctionDialogueAction
/**
* A text input for dialogues where the player can enter free text.
*
* @author Hiroku
* @since December 27th, 2023
*/
class DialogueTextInput : DialogueInput {
override var timeout: DialogueTimeout? = null
val action: DialogueAction = FunctionDialogueAction { dialogue, _ -> dialogue.setPage(dialogue.currentPageIndex + 1) }
override fun toMoLangStruct(activeInput: ActiveInput) = QueryStruct(hashMapOf())
override fun handle(activeInput: ActiveInput, value: String) {
action.invoke(activeInput.activeDialogue, value)
}
}
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.dialogue.input
import com.cobblemon.mod.common.api.dialogue.DialogueAction
import com.cobblemon.mod.common.api.dialogue.FunctionDialogueAction
/**
* A deadline for dialogue input. This is used to have something occur if the player spends
* too long on one page of dialogue.
*
* @author Hiroku
* @since December 27th, 2023
*/
class DialogueTimeout(
var duration: Float = 10F,
var showTimer: Boolean = true,
var action: DialogueAction = FunctionDialogueAction { dialogue, _ -> dialogue.close() }
)
@@ -31,14 +31,14 @@ import net.minecraft.util.math.Vec3d
* @author Hiroku
* @since July 24th, 2022
*/
class ItemDropEntry : DropEntry {
open class ItemDropEntry : DropEntry {
override val percentage = 100F
override val quantity = 1
val quantityRange: IntRange? = null
open val quantityRange: IntRange? = null
override val maxSelectableTimes = 1
val dropMethod: ItemDropMethod? = null
val item = Identifier("minecraft:fish")
val nbt: NbtCompound? = null
open val dropMethod: ItemDropMethod? = null
open val item = Identifier("minecraft:fish")
open val nbt: NbtCompound? = null
override fun drop(entity: LivingEntity?, world: ServerWorld, pos: Vec3d, player: ServerPlayerEntity?) {
val item = world.registryManager.get(RegistryKeys.ITEM).get(item) ?: return LOGGER.error("Unable to load drop item: $item")
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.entity
import net.minecraft.entity.Entity
import net.minecraft.entity.data.TrackedData
/**
* Represents a delegation of a portion of an entity's logic to a particular side.
@@ -16,4 +17,5 @@ import net.minecraft.entity.Entity
interface EntitySideDelegate<T : Entity> {
fun initialize(entity: T) {}
fun tick(entity: T) {}
fun onTrackedDataSet(data: TrackedData<*>) {}
}
@@ -9,26 +9,19 @@
package com.cobblemon.mod.common.api.events.battles
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.events.Cancelable
import com.cobblemon.mod.common.battles.BattleFormat
import net.minecraft.text.MutableText
/**
* Event fired before a [PokemonBattle] is started. Canceling this event prevents the battle from being
* created and launched.
*
* @property reason The text used to inform the participants why the battle was canceled. Keep null for a default error.
*
* @author Segfault Guy
* @since March 26th 2023
*/
data class BattleStartedPreEvent (
val participants: Collection<BattleActor>,
val format: BattleFormat,
val isPvP: Boolean,
val isPvN: Boolean,
val isPvW: Boolean
) : Cancelable()
data class BattleStartedPreEvent (override val battle: PokemonBattle, var reason: MutableText? = null) : BattleEvent, Cancelable()
/**
* Event fired after a [PokemonBattle] starts.
@@ -36,8 +29,4 @@ data class BattleStartedPreEvent (
* @author Segfault Guy
* @since March 26th 2023
*/
data class BattleStartedPostEvent (
override val battle: PokemonBattle
) : BattleEvent
data class BattleStartedPostEvent (override val battle: PokemonBattle) : BattleEvent
@@ -0,0 +1,80 @@
/*
* 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.fossil
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.pokemon.evolution.predicate.NbtItemPredicate
import net.minecraft.item.ItemStack
import net.minecraft.registry.Registries
import net.minecraft.text.MutableText
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.util.StringIdentifiable
class Fossil(
identifier: Identifier,
val result: PokemonProperties,
val fossils: List<NbtItemPredicate>
): StringIdentifiable {
@Transient
var identifier: Identifier = identifier
internal set
override fun asString(): String {
return identifier.toString()
}
/**
* Gets the name of the fossil.
* @return The name of the fossil.
*/
fun getName(): MutableText = Text.translatable("${identifier.namespace}.fossil.${identifier.path}.name")
/**
* Whether the fossil ingredients match this fossil.
* @param ingredients The ingredients to check.
* @return True if the ingredients match, false otherwise.
*/
fun matchesIngredients(ingredients: List<ItemStack>): Boolean {
if (this.fossils.size != ingredients.size) {
return false
}
return this.fossils.all { fossil ->
ingredients.any { fossil.item.fits(it.item, Registries.ITEM) && fossil.nbt.test(it) }
}
}
/**
* Whether the fossil ingredients match a subset of this fossil.
* @param ingredients The ingredients to check.
* @return True if the ingredients are a subset, false otherwise.
*/
fun matchesIngredientsSubSet(ingredients: List<ItemStack>): Boolean {
if (this.fossils.size < ingredients.size) {
return false
}
return ingredients.all { ingredient ->
ingredients.count { item -> ingredient.itemMatches(item.registryEntry) } <=
this.fossils.count { fossil -> fossil.item.fits(ingredient.item, Registries.ITEM) && fossil.nbt.test(ingredient) }
}
}
/**
* Whether the [ItemStack] is an ingredient for this fossil.
* @param itemStack The [ItemStack] to check.
* @return True if the [ItemStack] is an ingredient, false otherwise.
*/
fun isIngredient(itemStack: ItemStack): Boolean {
return this.fossils.any { it.item.fits(itemStack.item, Registries.ITEM) && it.nbt.test(itemStack) }
}
}
@@ -0,0 +1,107 @@
/*
* 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.fossil
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.fossil.FossilRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.evolution.adapters.NbtItemPredicateAdapter
import com.cobblemon.mod.common.pokemon.evolution.predicate.NbtItemPredicate
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.ItemLikeConditionAdapter
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
object Fossils: JsonDataRegistry<Fossil> {
override val id: Identifier = cobblemonResource("fossils")
override val type: ResourceType = ResourceType.SERVER_DATA
override val observable = SimpleObservable<Fossils>()
override val gson = GsonBuilder()
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(NbtItemPredicate::class.java, NbtItemPredicateAdapter)
.create()
override val typeToken: TypeToken<Fossil> = TypeToken.get(Fossil::class.java)
override val resourcePath: String = "fossils"
private val fossils = hashMapOf<Identifier, Fossil>()
override fun reload(data: Map<Identifier, Fossil>) {
this.fossils.clear()
data.forEach { (identifier, fossil) ->
try {
fossil.identifier = identifier
this.fossils[identifier] = fossil
} catch (e: Exception) {
Cobblemon.LOGGER.error("Skipped loading the {} fossil", identifier, e)
}
}
Cobblemon.LOGGER.info("Loaded {} fossils", this.fossils.size)
this.observable.emit(this)
}
override fun sync(player: ServerPlayerEntity) {
FossilRegistrySyncPacket(this.all()).sendToPlayer(player)
}
/**
* Gets all loaded [Fossil]s.
*/
fun all() = this.fossils.values.toList()
/**
* Gets a [Fossil] by its [Identifier].
* @param identifier The identifier of the fossil.
* @return The [Fossil] if loaded, otherwise null.
*/
fun getByIdentifier(identifier: Identifier): Fossil? = this.fossils[identifier]
/**
* Looks for a [Fossil] that matches a [ItemStack].
* @param fossilStacks The fossil [ItemStack]'s.
* @return The [Fossil] if found, otherwise null.
*/
fun getFossilByItemStacks(fossilStacks: List<ItemStack>): Fossil? {
return this.all().firstOrNull { it.matchesIngredients(fossilStacks) }
}
/**
* Looks for a [Fossil] that is a superset of [ItemStack].
* @param fossilStacks The fossil [ItemStack]'s.
* @return The [Fossil] if found to be a superset, otherwise null.
*/
fun getSubFossilByItemStacks(fossilStacks: List<ItemStack>): Fossil? {
return this.all().firstOrNull { it.matchesIngredientsSubSet(fossilStacks) }
}
/**
* Checks if a [ItemStack] is a fossil ingredient.
* @param itemStack The ingredient [ItemStack].
* @return true if it's a fossil ingredient, otherwise false.
*/
fun isFossilIngredient(itemStack: ItemStack): Boolean {
return this.all().any { it.isIngredient(itemStack) }
}
}
@@ -0,0 +1,19 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.fossil
import com.cobblemon.mod.common.registry.ItemTagCondition
import net.minecraft.util.Identifier
data class NaturalMaterial(
val content: Int = 0,
val item: Identifier?,
val tag: ItemTagCondition? = null,
val returnItem: Identifier? = null
)
@@ -0,0 +1,90 @@
/*
* 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.fossil
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.fossil.NaturalMaterialRegistrySyncPacket
import com.cobblemon.mod.common.registry.ItemTagCondition
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.ItemLikeConditionAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.item.ItemStack
import net.minecraft.registry.Registries
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
object NaturalMaterials : JsonDataRegistry<List<NaturalMaterial>>{
override val id = cobblemonResource("natural_materials")
override val type = ResourceType.SERVER_DATA
override val observable = SimpleObservable<NaturalMaterials>()
override val typeToken: TypeToken<List<NaturalMaterial>> =
TypeToken.getParameterized(List::class.java, NaturalMaterial::class.java) as TypeToken<List<NaturalMaterial>>
override val resourcePath = "natural_materials"
override val gson: Gson = GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.registerTypeAdapter(ItemTagCondition::class.java, ItemLikeConditionAdapter)
.create()
private val itemMap = mutableMapOf<Identifier, NaturalMaterial>()
private val tagMap = mutableMapOf<ItemTagCondition, NaturalMaterial>()
override fun sync(player: ServerPlayerEntity) {
NaturalMaterialRegistrySyncPacket(this.itemMap.values.toList() + this.tagMap.values.toList()).sendToPlayer(player)
}
override fun reload(data: Map<Identifier, List<NaturalMaterial>>) {
data.forEach { entry ->
entry.value.forEach {
itemMap.remove(it.item)
if (it.item != null) {
itemMap[it.item] = it
}
if (it.tag != null) {
tagMap[it.tag] = it
}
}
}
}
fun isNaturalMaterial(item: ItemStack): Boolean {
val itemId = Registries.ITEM.getId(item.item)
return itemId in itemMap.keys || tagMap.keys.any {
it.fits(item.item, Registries.ITEM)
}
}
fun getContent(item: ItemStack): Int? {
val itemId = Registries.ITEM.getId(item.item)
if (itemId in itemMap.keys) {
return itemMap[itemId]?.content
}
val tag = tagMap.keys.firstOrNull { it.fits(item.item, Registries.ITEM) }
if (tag != null) {
return tagMap[tag]?.content
}
return null
}
fun getReturnItem(item: ItemStack): Identifier? {
val itemId = Registries.ITEM.getId(item.item)
if (itemId in itemMap.keys) {
return itemMap[itemId]?.returnItem
}
val tag = tagMap.keys.firstOrNull { it.fits(item.item, Registries.ITEM) }
if (tag != null) {
return tagMap[tag]?.returnItem
}
return null
}
}
@@ -196,6 +196,8 @@ fun drawPortraitPokemon(
val context = RenderContext()
PokemonModelRepository.getTextureNoSubstitute(species.resourceIdentifier, aspects, 0f).let { it -> context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, species.getForm(aspects).baseScale)
context.put(RenderContext.SPECIES, species.resourceIdentifier)
context.put(RenderContext.ASPECTS, aspects)
val renderType = model.getLayer(texture)
@@ -206,10 +208,12 @@ fun drawPortraitPokemon(
if (state == null) {
model.setupAnimStateless(setOf(PoseType.PORTRAIT, PoseType.PROFILE))
} else {
val originalPose = state.currentPose
model.getPose(PoseType.PORTRAIT)?.let { state.setPose(it.poseName) }
state.timeEnteredPose = 0F
state.updatePartialTicks(partialTicks)
model.setupAnimStateful(null, state, 0F, 0F, 0F, 0F, 0F)
originalPose?.let { state.setPose(it) }
}
matrixStack.push()
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.molang
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.MoValue
/**
* An object that can be given a [MoLangRuntime] to produce a single [MoValue]. This abstracts
* the use of simple and complex [Expression]s as MoLang can be a single line or multiple.
*
* @author Hiroku
* @since October 22nd, 2023
*/
interface ExpressionLike {
/** Produces a [MoValue] for a [MoLangRuntime] to supply an environment. */
fun resolve(runtime: MoLangRuntime): MoValue
fun resolveDouble(runtime: MoLangRuntime) = resolve(runtime).asDouble()
fun resolveFloat(runtime: MoLangRuntime) = resolveDouble(runtime).toFloat()
fun resolveString(runtime: MoLangRuntime) = resolve(runtime).asString()
fun resolveInt(runtime: MoLangRuntime) = resolveDouble(runtime).toInt()
fun resolveBoolean(runtime: MoLangRuntime) = resolveDouble(runtime) == 1.0
fun resolveObject(runtime: MoLangRuntime) = resolve(runtime) as ObjectValue<*>
}
@@ -0,0 +1,23 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.molang
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.util.resolve
/**
* An [ExpressionLike] that produces MoValues by evaluating a series of [Expression]s.
*
* @author Hiroku
* @since October 22nd, 2023
*/
class ListExpression(val exprs: List<Expression>): ExpressionLike {
override fun resolve(runtime: MoLangRuntime) = exprs.resolve(runtime)
}
@@ -0,0 +1,167 @@
/*
* 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.molang
import com.bedrockk.molang.runtime.MoLangEnvironment
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.MoParams
import com.bedrockk.molang.runtime.struct.ArrayStruct
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.util.isInt
import com.cobblemon.mod.common.util.itemRegistry
import net.minecraft.block.Block
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtDouble
import net.minecraft.nbt.NbtElement
import net.minecraft.nbt.NbtList
import net.minecraft.nbt.NbtString
import net.minecraft.registry.Registry
import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.registry.tag.TagKey
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.world.World
import net.minecraft.world.biome.Biome
import net.minecraft.world.dimension.DimensionType
/**
* Holds a bunch of useful MoLang trickery that can be used or extended in API
*
* @author Hiroku
* @since October 2nd, 2023
*/
object MoLangFunctions {
val generalFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>(
"is_int" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().isInt()) },
"is_number" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toDoubleOrNull() != null) },
"to_number" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toDoubleOrNull() ?: 0.0) },
"to_int" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toIntOrNull() ?: 0) },
"to_string" to java.util.function.Function { params -> StringValue(params.get<MoValue>(0).asString()) }
)
val biomeFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val worldFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val dimensionTypeFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val blockFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val playerFunctions = mutableListOf<(ServerPlayerEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ player ->
hashMapOf(
"username" to java.util.function.Function { _ -> StringValue(player.gameProfile.name) },
"uuid" to java.util.function.Function { _ -> StringValue(player.gameProfile.id.toString()) },
"data" to java.util.function.Function { _ -> return@Function Cobblemon.molangData.load(player.uuid) },
"save_data" to java.util.function.Function { _ -> Cobblemon.molangData.save(player.uuid) },
"main_held_item" to java.util.function.Function { _ -> player.world.itemRegistry.getEntry(player.mainHandStack.item).asMoLangValue(RegistryKeys.ITEM) },
"off_held_item" to java.util.function.Function { _ -> player.world.itemRegistry.getEntry(player.offHandStack.item).asMoLangValue(RegistryKeys.ITEM) },
"face" to java.util.function.Function { _ -> ObjectValue(PlayerDialogueFaceProvider(player.uuid)) }
)
}
)
fun RegistryEntry<Biome>.asBiomeMoLangValue() = asMoLangValue(RegistryKeys.BIOME).addFunctions(biomeFunctions)
fun RegistryEntry<World>.asWorldMoLangValue() = asMoLangValue(RegistryKeys.WORLD).addFunctions(worldFunctions)
fun RegistryEntry<Block>.asBlockMoLangValue() = asMoLangValue(RegistryKeys.BLOCK).addFunctions(blockFunctions)
fun RegistryEntry<DimensionType>.asDimensionTypeMoLangValue() = asMoLangValue(RegistryKeys.DIMENSION_TYPE).addFunctions(dimensionTypeFunctions)
fun ServerPlayerEntity.asMoLangValue(): ObjectValue<ServerPlayerEntity> {
val value = ObjectValue(
obj = this,
stringify = { it.entityName }
)
value.addFunctions(playerFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
}
fun <T> RegistryEntry<T>.asMoLangValue(key: RegistryKey<Registry<T>>): ObjectValue<RegistryEntry<T>> {
val value = ObjectValue(
obj = this,
stringify = { it.key.get().value.toString() }
)
value.functions.put("is_in") {
val tag = TagKey.of(key, Identifier(it.getString(0).replace("#", "")))
return@put DoubleValue(if (value.obj.isIn(tag)) 1.0 else 0.0)
}
value.functions.put("is_of") {
val identifier = Identifier(it.getString(0))
return@put DoubleValue(if (value.obj.matchesId(identifier)) 1.0 else 0.0)
}
return value
}
fun QueryStruct.addStandardFunctions(): QueryStruct {
functions.putAll(generalFunctions)
return this
}
fun <T : QueryStruct> T.addFunctions(functions: Map<String, java.util.function.Function<MoParams, Any>>): T {
this.functions.putAll(functions)
return this
}
fun MoLangEnvironment.getQueryStruct(name: String = "query") = structs.getOrPut(name) { QueryStruct(hashMapOf()) } as QueryStruct
fun MoLangRuntime.setup(): MoLangRuntime {
environment.getQueryStruct().addStandardFunctions()
return this
}
fun writeMoValueToNBT(value: MoValue): NbtElement? {
return when (value) {
is DoubleValue -> NbtDouble.of(value.value)
is StringValue -> NbtString.of(value.value)
is ArrayStruct -> {
val list = value.map.values
val nbtList = NbtList()
list.mapNotNull(::writeMoValueToNBT).forEach(nbtList::add)
nbtList
}
is VariableStruct -> {
val nbt = NbtCompound()
value.map.forEach { (key, value) ->
val element = writeMoValueToNBT(value) ?: return@forEach
nbt.put(key, element)
}
nbt
}
else -> null
}
}
fun readMoValueFromNBT(nbt: NbtElement): MoValue {
return when (nbt) {
is NbtDouble -> DoubleValue(nbt.doubleValue())
is NbtString -> StringValue(nbt.asString())
is NbtList -> {
val array = ArrayStruct(hashMapOf())
var index = 0
nbt.forEach { element ->
val value = readMoValueFromNBT(element)
array.setDirectly("$index", value)
index++
}
array
}
is NbtCompound -> {
val variable = VariableStruct(hashMapOf())
nbt.keys.toList().forEach { key ->
val value = readMoValueFromNBT(nbt[key]!!)
variable.map[key] = value
}
variable
}
else -> null
} ?: throw IllegalArgumentException("Invalid NBT element type: ${nbt.type}")
}
}
@@ -0,0 +1,28 @@
/*
* 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.molang
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.MoValue
/**
* A [MoValue] that just wraps around some other object value. It's for good and valid reasons.
*
* @author Hiroku
* @since October 2nd, 2023
*/
class ObjectValue<T>(
var obj: T,
val stringify: (T) -> String = { it.toString() },
val doublify: (T) -> Double = { throw NotImplementedError("Doublify not implemented for this object value, are you doing weird molang") }
) : QueryStruct(hashMapOf()) {
override fun value() = this
override fun asDouble() = doublify(obj)
override fun asString() = stringify(obj)
}
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.molang
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.util.resolve
/**
* A simple implementation of [ExpressionLike] that is just a single [Expression]. This
* is as opposed to [ListExpression]s.
*
* @author Hiroku
* @since October 22nd, 2023
*/
class SingleExpression(val expr: Expression) : ExpressionLike {
override fun resolve(runtime: MoLangRuntime) = runtime.resolve(expr)
}
@@ -13,7 +13,9 @@ import com.cobblemon.mod.common.api.moves.categories.DamageCategory
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.battles.MoveTarget
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.util.lang
import com.google.gson.JsonObject
import com.google.gson.annotations.SerializedName
import net.minecraft.text.MutableText
@@ -45,7 +47,8 @@ open class MoveTemplate(
val pp: Int,
val priority: Int,
val critRatio: Double,
val effectChances: Array<Double>
val effectChances: Array<Double>,
val flags: Set<String>
) {
val displayName: MutableText
@@ -65,7 +68,8 @@ open class MoveTemplate(
pp = 5,
priority = 0,
critRatio = 0.0,
effectChances = emptyArray()
effectChances = emptyArray(),
flags = emptySet()
)
companion object {
@@ -92,4 +96,9 @@ open class MoveTemplate(
template = this
)
}
/**
* Returns this move's showdown data as a [JsonObject]
*/
fun showdownJson() = ShowdownService.service.getMoves()[num].asJsonObject
}
@@ -72,7 +72,22 @@ object Moves : DataRegistry {
effectChances += secondaryMember.get("chance").asDouble
}
}
val move = MoveTemplate(id, num, elementalType, damageCategory, power, target, accuracy, pp, priority, critRatio, effectChances.toTypedArray())
val flags = mutableSetOf<String>()
jsMove["flags"].asJsonObject.asMap().forEach { flags.add(it.key) }
val move = MoveTemplate(
id,
num,
elementalType,
damageCategory,
power,
target,
accuracy,
pp,
priority,
critRatio,
effectChances.toTypedArray(),
flags
)
this.register(move)
} catch (e: Exception) {
Cobblemon.LOGGER.error("Caught exception trying to resolve the move '{}'", id, e)
@@ -6,10 +6,10 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.access;
package com.cobblemon.mod.common.api.moves.tags
import net.minecraft.sound.SoundCategory;
import com.cobblemon.mod.common.api.moves.MoveTemplate
public interface SoundManagerDuck {
void toggleCategories(SoundCategory... categories);
}
class MoveTag(
val moves: List<MoveTemplate>
)
@@ -0,0 +1,55 @@
/*
* 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.moves.tags
import com.cobblemon.mod.common.api.moves.Moves
object MoveTags {
private val moves = Moves.all()
val PRIORITY_ZERO = MoveTag(moves.filter { it.priority == 0 })
/* Showdown flags */
val PROTECT = MoveTag(moves.filter { it.flags.contains("protect") })
val CONTACT = MoveTag(moves.filter { it.flags.contains("contact") })
val DISTANCE = MoveTag(moves.filter { it.flags.contains("distance") })
val ALLY_ANIM = MoveTag(moves.filter { it.flags.contains("allyanim") })
val FAIL_ENCORE = MoveTag(moves.filter { it.flags.contains("failencore") })
val FAIL_COPYCAT = MoveTag(moves.filter { it.flags.contains("failcopycat") })
val REFLECTABLE = MoveTag(moves.filter { it.flags.contains("reflectable") })
val BITE = MoveTag(moves.filter { it.flags.contains("bite") })
val NON_SKY = MoveTag(moves.filter { it.flags.contains("nonsky") })
val GRAVITY = MoveTag(moves.filter { it.flags.contains("gravity") })
val POWDER = MoveTag(moves.filter { it.flags.contains("powder") })
val PLEDGE_COMBO = MoveTag(moves.filter { it.flags.contains("pledgecombo") })
val MIRROR = MoveTag(moves.filter { it.flags.contains("mirror") })
val SNATCH = MoveTag(moves.filter { it.flags.contains("snatch") })
val SLICING = MoveTag(moves.filter { it.flags.contains("slicing") })
val WIND = MoveTag(moves.filter { it.flags.contains("wind") })
val NO_SLEEP_TALK = MoveTag(moves.filter { it.flags.contains("nosleeptalk") })
val FAIL_INSTRUCT = MoveTag(moves.filter { it.flags.contains("failinstruct") })
val PULSE = MoveTag(moves.filter { it.flags.contains("pulse") })
val RECHARGE = MoveTag(moves.filter { it.flags.contains("recharge") })
val SOUND = MoveTag(moves.filter { it.flags.contains("sound") })
val PUNCH = MoveTag(moves.filter { it.flags.contains("punch") })
val FUTURE_MOVE = MoveTag(moves.filter { it.flags.contains("futuremove") })
val MUST_PRESSURE = MoveTag(moves.filter { it.flags.contains("mustpressure") })
val HEAL = MoveTag(moves.filter { it.flags.contains("heal") })
val BULLET = MoveTag(moves.filter { it.flags.contains("bullet") })
val BYPASS_SUB = MoveTag(moves.filter { it.flags.contains("bypasssub") })
val DANCE = MoveTag(moves.filter { it.flags.contains("dance") })
val NO_ASSIST = MoveTag(moves.filter { it.flags.contains("noassist") })
val FAIL_MIMIC = MoveTag(moves.filter { it.flags.contains("failmimic") })
val FAIL_ME_FIRST = MoveTag(moves.filter { it.flags.contains("failmefirst") })
val CANT_USE_TWICE = MoveTag(moves.filter { it.flags.contains("cantusetwice") })
val CHARGE = MoveTag(moves.filter { it.flags.contains("charge") })
val DEFROST = MoveTag(moves.filter { it.flags.contains("defrost") })
val NO_PARENTAL_BOND = MoveTag(moves.filter { it.flags.contains("noparentalbond") })
}
@@ -0,0 +1,82 @@
/*
* 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.multiblock
import com.cobblemon.mod.common.block.entity.fossil.FossilMultiblockEntity
import net.minecraft.block.*
import net.minecraft.block.entity.BlockEntity
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemStack
import net.minecraft.server.world.ServerWorld
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
/**
* A block that can be part of a [MultiblockStructure]
*/
abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(properties) {
override fun onPlaced(
world: World,
pos: BlockPos,
state: BlockState,
placer: LivingEntity?,
itemStack: ItemStack?
) {
if (world is ServerWorld) {
val multiblockEntity = world.getBlockEntity(pos) as MultiblockEntity
multiblockEntity.multiblockBuilder?.validate(world)
}
super.onPlaced(world, pos, state, placer, itemStack)
}
override fun onUse(
state: BlockState,
world: World,
pos: BlockPos,
player: PlayerEntity,
hand: Hand,
hit: BlockHitResult
): ActionResult {
if(hand == Hand.OFF_HAND) {
return ActionResult.SUCCESS
}
val entity = world.getBlockEntity(pos) as MultiblockEntity
if (entity.multiblockStructure != null) {
return entity.multiblockStructure!!.onUse(state, world, pos, player, hand, hit)
}
return super.onUse(state, world, pos, player, hand, hit)
}
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
super.onBreak(world, pos, state, player)
if (!world.isClient) {
val entity = world.getBlockEntity(pos)
if (entity is MultiblockEntity && entity.multiblockStructure != null) {
entity.multiblockStructure!!.onBreak(world, pos, state, player)
}
}
}
override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? {
return createMultiBlockEntity(pos, state)
}
@Deprecated("Deprecated in Java")
override fun getRenderType(state: BlockState?): BlockRenderType {
return BlockRenderType.MODEL
}
abstract fun createMultiBlockEntity(pos: BlockPos, state: BlockState): FossilMultiblockEntity
}
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.multiblock
import com.cobblemon.mod.common.api.multiblock.MultiblockStructure
import com.cobblemon.mod.common.api.multiblock.builder.MultiblockStructureBuilder
import com.cobblemon.mod.common.util.DataKeys
import net.minecraft.block.BlockState
import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtHelper
import net.minecraft.network.listener.ClientPlayPacketListener
import net.minecraft.network.packet.Packet
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket
import net.minecraft.util.math.BlockPos
/**
* Multiblock entities are kind of complicated. Basically every multiblock entity should have a MultiBlockStructureBuilder
* that is checked when this block entity is created. The multiblock entity also contains a reference to a MultiblockStructure
* which is shared between all MultiblockEntities in the structure. Finally, every Multiblock contains the location of the
* block that controls this blockentities associated structure
*/
abstract class MultiblockEntity(
type: BlockEntityType<*>,
pos: BlockPos,
state: BlockState,
var multiblockBuilder: MultiblockStructureBuilder?
) : BlockEntity(type, pos, state) {
abstract var multiblockStructure: MultiblockStructure?
abstract var masterBlockPos: BlockPos?
override fun toUpdatePacket(): Packet<ClientPlayPacketListener>? {
return BlockEntityUpdateS2CPacket.create(this)
}
override fun toInitialChunkDataNbt(): NbtCompound {
val result = NbtCompound()
writeNbt(result)
return result
}
override fun writeNbt(nbt: NbtCompound) {
super.writeNbt(nbt)
//Used for checking build conditions in multiblocks (Dont count a block if it has the FORMED flag)
nbt.putBoolean(DataKeys.FORMED, masterBlockPos != null)
if (multiblockStructure != null && multiblockStructure!!.controllerBlockPos == pos) {
nbt.put(DataKeys.MULTIBLOCK_STORAGE, multiblockStructure!!.writeToNbt())
}
else if (masterBlockPos != null) {
nbt.put(DataKeys.CONTROLLER_BLOCK, NbtHelper.fromBlockPos(masterBlockPos))
}
}
abstract override fun readNbt(nbt: NbtCompound)
}
@@ -0,0 +1,46 @@
/*
* 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.multiblock
import net.minecraft.block.BlockState
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.nbt.NbtCompound
import net.minecraft.server.world.ServerWorld
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.util.math.random.Random
import net.minecraft.world.World
interface MultiblockStructure {
val controllerBlockPos: BlockPos
fun onUse(
blockState: BlockState,
world: World,
blockPos: BlockPos,
player: PlayerEntity,
interactionHand: Hand,
blockHitResult: BlockHitResult
): ActionResult
fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?)
fun tick(world: World)
fun syncToClient(world: World)
fun markDirty(world: World)
fun writeToNbt(): NbtCompound
fun getComparatorOutput(state: BlockState, world: World?, pos: BlockPos?): Int {
return 0
}
fun onTriggerEvent(state: BlockState?, world: ServerWorld?, pos: BlockPos?, random: Random?)
}
@@ -0,0 +1,39 @@
/*
* 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.multiblock.builder
import com.cobblemon.mod.common.api.multiblock.condition.MultiblockCondition
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.Box
import net.minecraft.util.shape.VoxelShape
/**
* Represents an area that has a potential to form a MultiBlockStructure
* @property boundingBox The box that each condition checks in
* @property conditions The [MultiblockCondition]s that must be met for the multiblock to form. All must be true.
*
* @author Apion
* @since August 24, 2023
*/
interface MultiblockStructureBuilder {
val boundingBox: VoxelShape
val conditions: List<MultiblockCondition>
fun validate(world: ServerWorld): Boolean {
conditions.forEach {
if (!it.test(world, boundingBox)) {
return false
}
}
form(world)
return true
}
fun form(world: ServerWorld)
}
@@ -0,0 +1,21 @@
/*
* 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.multiblock.condition
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.Box
import net.minecraft.util.shape.VoxelShape
class AndCondition(val conditionOne: MultiblockCondition, val conditionTwo: MultiblockCondition) :
MultiblockCondition {
override fun test(world: ServerWorld, box: VoxelShape): Boolean {
return conditionOne.test(world, box) and conditionTwo.test(world, box)
}
}
@@ -0,0 +1,46 @@
/*
* 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.multiblock.condition
import com.cobblemon.mod.common.util.blockPositionsAsList
import net.minecraft.predicate.BlockPredicate
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape
/**
* Checks that a block adjacent to another block matches a predicate. Basically we find a block that
* matched relToBlock, then we look in each direction to see if there is an adjacent block that matches
* target block.
*
* @param relToBlock The [BlockPredicate] for the main block. The block that matches this predicate is the block whose adjacent blocks we check
* @param targetBlock The [BlockPredicate] that is checked for blocks adjacent to relToBlock
* @param directionsToCheck The [Direction]s that we check from the target relToBlock
*
* @author Apion
* @since August 24th, 2023
*/
class BlockRelativeCondition(
val relToBlock: BlockPredicate,
val targetBlock: BlockPredicate,
val directionsToCheck: Array<Direction> = Direction.values()
) : MultiblockCondition {
override fun test(world: ServerWorld, box: VoxelShape): Boolean {
val relToBlockBlockPositions = box.blockPositionsAsList().filter { relToBlock.test(world, it) }
relToBlockBlockPositions.forEach { pos ->
directionsToCheck.forEach {
if (targetBlock.test(world, pos.offset(it))) {
return true
}
}
}
return false
}
}
@@ -0,0 +1,20 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.multiblock.condition
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.Box
import net.minecraft.util.shape.VoxelShape
/**
*
*/
interface MultiblockCondition {
fun test(world: ServerWorld, box: VoxelShape): Boolean
}
@@ -0,0 +1,23 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.multiblock.condition
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.Box
import net.minecraft.util.shape.VoxelShape
class OrCondition(
val conditionOne: MultiblockCondition,
val conditionTwo: MultiblockCondition
) : MultiblockCondition {
override fun test(world: ServerWorld, box: VoxelShape): Boolean {
return conditionOne.test(world, box) or conditionTwo.test(world, box)
}
}
@@ -27,11 +27,14 @@ object CobblemonPermissions {
val HEAL_POKEMON_SELF = this.create("$HEAL_POKEMON_BASE.self", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val HEAL_POKEMON_OTHER = this.create("$HEAL_POKEMON_BASE.other", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
private const val LEVEL_UP_BASE = "${COMMAND_PREFIX}levelup"
val LEVEL_UP_SELF = this.create("$LEVEL_UP_BASE.self", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val LEVEL_UP_OTHER = this.create("$LEVEL_UP_BASE.other", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val OPEN_STARTER_SCREEN = this.create("${COMMAND_PREFIX}openstarterscreen", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val BEDROCK_PARTICLE = this.create("${COMMAND_PREFIX}bedrockparticle", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val OPEN_DIALOGUE = this.create("${COMMAND_PREFIX}opendialogue", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
private const val POKEMON_EDIT_BASE = "${COMMAND_PREFIX}pokemonedit"
val POKEMON_EDIT_SELF = this.create("$POKEMON_EDIT_BASE.self", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@@ -67,6 +70,9 @@ object CobblemonPermissions {
val CLEAR_PARTY = this.create("${COMMAND_PREFIX}clearparty", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val CLEAR_PC = this.create("${COMMAND_PREFIX}clearpc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
// Test for starting a random battle with AI Player
val RANDOM_BATTLE = this.create("${COMMAND_PREFIX}randombattle", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
fun all(): Iterable<Permission> = this.permissions
private fun create(node: String, level: PermissionLevel): Permission {
@@ -121,6 +121,38 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
get() = this.byName("dream_ball")
val BEAST_BALL
get() = this.byName("beast_ball")
val ANCIENT_POKE_BALL
get() = this.byName("ancient_poke_ball")
val ANCIENT_CITRINE_BALL
get() = this.byName("ancient_citrine_ball")
val ANCIENT_VERDANT_BALL
get() = this.byName("ancient_verdant_ball")
val ANCIENT_AZURE_BALL
get() = this.byName("ancient_azure_ball")
val ANCIENT_ROSEATE_BALL
get() = this.byName("ancient_roseate_ball")
val ANCIENT_SLATE_BALL
get() = this.byName("ancient_slate_ball")
val ANCIENT_IVORY_BALL
get() = this.byName("ancient_ivory_ball")
val ANCIENT_GREAT_BALL
get() = this.byName("ancient_great_ball")
val ANCIENT_ULTRA_BALL
get() = this.byName("ancient_ultra_ball")
val ANCIENT_HEAVY_BALL
get() = this.byName("ancient_heavy_ball")
val ANCIENT_LEADEN_BALL
get() = this.byName("ancient_leaden_ball")
val ANCIENT_GIGATON_BALL
get() = this.byName("ancient_gigaton_ball")
val ANCIENT_FEATHER_BALL
get() = this.byName("ancient_feather_ball")
val ANCIENT_WING_BALL
get() = this.byName("ancient_wing_ball")
val ANCIENT_JET_BALL
get() = this.byName("ancient_jet_ball")
val ANCIENT_ORIGIN_BALL
get() = this.byName("ancient_origin_ball")
init {
createDefault("poke_ball")
@@ -157,6 +189,22 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
createDefault("park_ball", CatchRateModifiers.PARK)
createDefault("dream_ball", CatchRateModifiers.statusBoosting(4F, Statuses.SLEEP))
createDefault("beast_ball", LabelModifier(5F, true, CobblemonPokemonLabels.ULTRA_BEAST)/*, LabelModifier(0.1F, false, CobblemonPokemonLabels.ULTRA_BEAST))*/)
createDefault("ancient_poke_ball", ancient = true)
createDefault("ancient_citrine_ball", ancient = true)
createDefault("ancient_verdant_ball", ancient = true)
createDefault("ancient_azure_ball", ancient = true)
createDefault("ancient_roseate_ball", ancient = true)
createDefault("ancient_slate_ball", ancient = true)
createDefault("ancient_ivory_ball", ancient = true)
createDefault("ancient_great_ball", MultiplierModifier(1.5F), ancient = true)
createDefault("ancient_ultra_ball", MultiplierModifier(2F), ancient = true)
createDefault("ancient_heavy_ball", throwPower = 0.75f, ancient = true)
createDefault("ancient_leaden_ball", throwPower = 0.75f, ancient = true)
createDefault("ancient_gigaton_ball", throwPower = 0.75f, ancient = true)
createDefault("ancient_feather_ball", throwPower = 2.5f, ancient = true)
createDefault("ancient_wing_ball", throwPower = 2.5f, ancient = true)
createDefault("ancient_jet_ball", throwPower = 2.5f, ancient = true)
createDefault("ancient_origin_ball", ancient = true)
// Luxury ball effect
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(priority = Priority.LOWEST) { event ->
var increment = (event.newFriendship - event.pokemon.friendship).toFloat()
@@ -191,11 +239,13 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
effects: List<CaptureEffect> = emptyList(),
waterDragValue: Float = 0.8F,
model2d: Identifier = cobblemonResource(name),
model3d: Identifier = cobblemonResource("${name}_model")
model3d: Identifier = cobblemonResource("${name}_model"),
throwPower: Float = 1.25f,
ancient: Boolean = false
): PokeBall {
val identifier = cobblemonResource(name)
//val finalModifiers = if (appendUltraBeastPenalty) modifiers + listOf(LabelModifier(0.1F, true, CobblemonPokemonLabels.ULTRA_BEAST)) else modifiers
val pokeball = PokeBall(identifier, modifier, effects, waterDragValue, model2d, model3d)
val pokeball = PokeBall(identifier, modifier, effects, waterDragValue, model2d, model3d, throwPower, ancient)
this.defaults[identifier] = pokeball
return pokeball
}
@@ -8,8 +8,12 @@
package com.cobblemon.mod.common.api.pokemon
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
@@ -17,16 +21,9 @@ import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.EVs
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.IVs
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.pokemon.*
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.isInt
import com.cobblemon.mod.common.util.splitMap
import com.cobblemon.mod.common.util.*
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import kotlin.random.Random
@@ -39,6 +36,7 @@ import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.util.InvalidIdentifierException
import net.minecraft.world.World
import java.util.UUID
/**
* A grouping of typical, selectable properties for a Pokémon. This is serializable
@@ -95,6 +93,12 @@ open class PokemonProperties {
props.dmaxLevel = parseIntProperty(keyPairs, listOf("dmax_level", "dmax"))?.coerceIn(0, Cobblemon.config.maxDynamaxLevel)
props.gmaxFactor = parseBooleanProperty(keyPairs, listOf("gmax_factor", "gmax"))
props.tradeable = parseBooleanProperty(keyPairs, listOf("tradeable", "tradable"))
props.move1 = parseStringOfRegistry(keyPairs, listOf("move1", "m1")) { Moves.getByName(it)?.name }
props.move2 = parseStringOfRegistry(keyPairs, listOf("move2", "m2")) { Moves.getByName(it)?.name }
props.move3 = parseStringOfRegistry(keyPairs, listOf("move3", "m3")) { Moves.getByName(it)?.name }
props.move4 = parseStringOfRegistry(keyPairs, listOf("move4", "m4")) { Moves.getByName(it)?.name }
props.originalTrainerType = OriginalTrainerType.values().toList().parsePropertyOfCollection(keyPairs, listOf("originaltrainertype", "ottype"), labelsOptional = true) { it.name.lowercase() }
props.originalTrainer = parsePlayerProperty(keyPairs, listOf("originaltrainer", "ot"))
val maybeIVs = IVs()
val maybeEVs = EVs()
@@ -213,6 +217,21 @@ open class PokemonProperties {
}
}
/**
* Try and parse a Player reference, either as a Username (3 <= length <= 16) or a UUID (length == 36)
*/
private fun parsePlayerProperty(keyPairs: MutableList<Pair<String, String?>>, labels: Iterable<String>): String? {
val matchingKeyPair = getMatchedKeyPair(keyPairs, labels) ?: return null
keyPairs.remove(matchingKeyPair)
if (matchingKeyPair.second == null)
return null
val string = matchingKeyPair.second!!
return if (string.length in 3..16 || (string.length == 36 && isUuid(string)) ) string else null
}
private fun <T> Iterable<T>.parsePropertyOfCollection(
keyPairs: MutableList<Pair<String, String?>>,
labels: Iterable<String>,
@@ -260,6 +279,13 @@ open class PokemonProperties {
var dmaxLevel: Int? = null
var gmaxFactor: Boolean? = null
var tradeable: Boolean? = null
var move1: String? = null
var move2: String? = null
var move3: String? = null
var move4: String? = null
var originalTrainerType: OriginalTrainerType? = null
var originalTrainer: String? = null // Original Trainer by Username or UUID
var ivs: IVs? = null
var evs: EVs? = null
@@ -313,6 +339,32 @@ open class PokemonProperties {
dmaxLevel?.let { pokemon.dmaxLevel = it }
gmaxFactor?.let { pokemon.gmaxFactor = it }
tradeable?.let { pokemon.tradeable = it }
move1?.let { pokemon.setMove(it, 0) }
move2?.let { pokemon.setMove(it, 1) }
move3?.let { pokemon.setMove(it, 2) }
move4?.let { pokemon.setMove(it, 3) }
// If it has been explicitly set to NONE, clear the stored information, including the originalTrainer property
originalTrainerType?.let {
if (it == OriginalTrainerType.NONE) {
pokemon.removeOriginalTrainer()
originalTrainer = null
}
}
originalTrainer?.let { ot ->
val type = originalTrainerType ?: pokemon.originalTrainerType
when (type) {
OriginalTrainerType.PLAYER -> {
when (ot.length) {
in 3..16 -> server()?.userCache?.findByName(ot)?.get()?.id // OT is a Username
36 -> UUID.fromString(ot) // OT is a UUID
else -> null // OT is invalid
}?.let { uuid -> pokemon.setOriginalTrainer(uuid) }
}
OriginalTrainerType.NPC -> pokemon.setOriginalTrainer(ot)
else -> {}
}
pokemon.refreshOriginalTrainer()
}
pokemon.updateAspects()
}
@@ -353,6 +405,32 @@ open class PokemonProperties {
dmaxLevel?.let { pokemonEntity.pokemon.dmaxLevel = it }
gmaxFactor?.let { pokemonEntity.pokemon.gmaxFactor = it }
tradeable?.let { pokemonEntity.pokemon.tradeable = it }
move1?.let { pokemonEntity.pokemon.setMove(it, 0) }
move2?.let { pokemonEntity.pokemon.setMove(it, 1) }
move3?.let { pokemonEntity.pokemon.setMove(it, 2) }
move4?.let { pokemonEntity.pokemon.setMove(it, 3) }
// If it has been explicitly set to NONE, clear the stored information, including the originalTrainer property
originalTrainerType?.let {
if (it == OriginalTrainerType.NONE) {
pokemonEntity.pokemon.removeOriginalTrainer()
originalTrainer = null
}
}
originalTrainer?.let { ot ->
val type = originalTrainerType ?: pokemonEntity.pokemon.originalTrainerType
when (type) {
OriginalTrainerType.PLAYER -> {
when (ot.length) {
in 3..16 -> server()?.userCache?.findByName(ot)?.get()?.id // OT is a Username
36 -> UUID.fromString(ot) // OT is a UUID
else -> null // OT is invalid
}?.let { uuid -> pokemonEntity.pokemon.setOriginalTrainer(uuid) }
}
OriginalTrainerType.NPC -> pokemonEntity.pokemon.setOriginalTrainer(ot)
else -> {}
}
pokemonEntity.pokemon.refreshOriginalTrainer()
}
pokemonEntity.pokemon.updateAspects()
}
@@ -391,6 +469,12 @@ open class PokemonProperties {
dmaxLevel?.takeIf { it != pokemon.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != pokemon.gmaxFactor }?.let { return false }
tradeable?.takeIf { it != pokemon.tradeable }?.let { return false }
move1?.takeIf { move -> pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
move2?.takeIf { move -> pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
move3?.takeIf { move -> pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
move4?.takeIf { move -> pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
originalTrainer?.takeIf { it != pokemon.originalTrainer }?.let{ return false }
originalTrainerType?.takeIf { it != pokemon.originalTrainerType }?.let{ return false }
return customProperties.none { !it.matches(pokemon) }
}
@@ -427,6 +511,12 @@ open class PokemonProperties {
dmaxLevel?.takeIf { it != pokemonEntity.pokemon.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != pokemonEntity.pokemon.gmaxFactor }?.let { return false }
tradeable?.takeIf { it != pokemonEntity.pokemon.tradeable }?.let { return false }
move1?.takeIf { move -> pokemonEntity.pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
move2?.takeIf { move -> pokemonEntity.pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
move3?.takeIf { move -> pokemonEntity.pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
move4?.takeIf { move -> pokemonEntity.pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
originalTrainer?.takeIf { it != pokemonEntity.pokemon.originalTrainer }?.let{ return false }
originalTrainerType?.takeIf { it != pokemonEntity.pokemon.originalTrainerType }?.let{ return false }
return customProperties.none { !it.matches(pokemonEntity) }
}
@@ -471,13 +561,20 @@ open class PokemonProperties {
dmaxLevel?.takeIf { it != properties.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != properties.gmaxFactor }?.let { return false }
tradeable?.takeIf { it != properties.tradeable }?.let { return false }
move1?.takeIf { it != properties.move1 }?.let { return false }
move2?.takeIf { it != properties.move2 }?.let { return false }
move3?.takeIf { it != properties.move3 }?.let { return false }
move4?.takeIf { it != properties.move4 }?.let { return false }
originalTrainer?.takeIf { it != properties.originalTrainer }?.let{ return false }
originalTrainerType?.takeIf { it != properties.originalTrainerType }?.let{ return false }
return true
}
fun create(): Pokemon {
val pokemon = Pokemon()
apply(pokemon)
pokemon.initialize()
if (move1 == null) //if there is not a move1 or later specific moves stated then initialize
pokemon.initialize()
roll(pokemon)
return pokemon
}
@@ -518,6 +615,8 @@ open class PokemonProperties {
dmaxLevel?.let { nbt.putInt(DataKeys.POKEMON_DMAX_LEVEL, it) }
gmaxFactor?.let { nbt.putBoolean(DataKeys.POKEMON_GMAX_FACTOR, it) }
tradeable?.let { nbt.putBoolean(DataKeys.POKEMON_TRADEABLE, it) }
originalTrainerType?.let { nbt.putInt(DataKeys.POKEMON_ORIGINAL_TRAINER_TYPE, it.ordinal) }
originalTrainer?.let { nbt.putString(DataKeys.POKEMON_ORIGINAL_TRAINER, it) }
val custom = NbtList()
customProperties.map { NbtString.of(it.asString()) }.forEach { custom.add(it) }
nbt.put(DataKeys.POKEMON_PROPERTIES_CUSTOM, custom)
@@ -543,6 +642,8 @@ open class PokemonProperties {
dmaxLevel = if (tag.contains(DataKeys.POKEMON_DMAX_LEVEL)) tag.getInt(DataKeys.POKEMON_DMAX_LEVEL) else null
gmaxFactor = if (tag.contains(DataKeys.POKEMON_GMAX_FACTOR)) tag.getBoolean(DataKeys.POKEMON_GMAX_FACTOR) else null
tradeable = if (tag.contains(DataKeys.POKEMON_TRADEABLE)) tag.getBoolean(DataKeys.POKEMON_TRADEABLE) else null
originalTrainerType = if (tag.contains(DataKeys.POKEMON_ORIGINAL_TRAINER_TYPE)) OriginalTrainerType.valueOf(tag.getString(DataKeys.POKEMON_ORIGINAL_TRAINER_TYPE)) else null
originalTrainer = if (tag.contains(DataKeys.POKEMON_ORIGINAL_TRAINER)) tag.getString(DataKeys.POKEMON_ORIGINAL_TRAINER) else null
val custom = tag.getList(DataKeys.POKEMON_PROPERTIES_CUSTOM, NbtElement.STRING_TYPE.toInt())
// This is kinda gross
custom.forEach { customProperties.addAll(parse(it.asString()).customProperties) }
@@ -570,6 +671,8 @@ open class PokemonProperties {
dmaxLevel?.let { json.addProperty(DataKeys.POKEMON_DMAX_LEVEL, it) }
gmaxFactor?.let { json.addProperty(DataKeys.POKEMON_GMAX_FACTOR, it) }
tradeable?.let { json.addProperty(DataKeys.POKEMON_TRADEABLE, it) }
originalTrainerType?.let { json.addProperty(DataKeys.POKEMON_ORIGINAL_TRAINER_TYPE, it.name) }
originalTrainer?.let { json.addProperty(DataKeys.POKEMON_ORIGINAL_TRAINER, it) }
val custom = JsonArray()
customProperties.map { it.asString() }.forEach { custom.add(it) }
json.add(DataKeys.POKEMON_PROPERTIES_CUSTOM, custom)
@@ -596,6 +699,8 @@ open class PokemonProperties {
dmaxLevel = json.get(DataKeys.POKEMON_DMAX_LEVEL)?.asInt
gmaxFactor = json.get(DataKeys.POKEMON_GMAX_FACTOR)?.asBoolean
tradeable = json.get(DataKeys.POKEMON_TRADEABLE)?.asBoolean
originalTrainerType = json.get(DataKeys.POKEMON_ORIGINAL_TRAINER_TYPE)?.asString?.let { OriginalTrainerType.valueOf(it) }
originalTrainer = json.get(DataKeys.POKEMON_ORIGINAL_TRAINER)?.asString
val custom = json.get(DataKeys.POKEMON_PROPERTIES_CUSTOM)?.asJsonArray
// This is still kinda gross
custom?.forEach { customProperties.addAll(parse(it.asString).customProperties) }
@@ -626,10 +731,23 @@ open class PokemonProperties {
dmaxLevel?.let { pieces.add("dmax_level=$it") }
gmaxFactor?.let { pieces.add("gmax_factor=$it") }
tradeable?.let { pieces.add("tradeable=$it") }
originalTrainerType?.let { pieces.add("originaltrainertype=${it.name}") }
originalTrainer?.let { pieces.add("originaltrainer=$it") }
customProperties.forEach { pieces.add(it.asString()) }
return pieces.joinToString(separator)
}
fun asStruct(): VariableStruct {
val struct = VariableStruct()
species?.let { struct.setDirectly("species", StringValue(it)) }
level?.let { struct.setDirectly("level", DoubleValue(it)) }
shiny?.let { struct.setDirectly("shiny", DoubleValue(it)) }
// add more of the optional properties to the struct as doubles or strings
gender?.let { struct.setDirectly("gender", StringValue(it.name)) }
friendship?.let { struct.setDirectly("friendship", DoubleValue(it)) }
return struct
}
fun updateAspects() {
val aspects = mutableSetOf<String>()
AspectProvider.providers.forEach { aspects.addAll(it.provide(this)) }
@@ -33,6 +33,7 @@ import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.FormData
@@ -61,6 +62,7 @@ import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.math.Box
import net.minecraft.world.biome.Biome
import net.minecraft.world.gen.structure.Structure
object PokemonSpecies : JsonDataRegistry<Species> {
@@ -93,6 +95,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
.registerTypeAdapter(StatusEffect::class.java, RegistryElementAdapter<StatusEffect> { Registries.STATUS_EFFECT })
.registerTypeAdapter(NbtItemPredicate::class.java, NbtItemPredicateAdapter)
@@ -169,6 +172,9 @@ object PokemonSpecies : JsonDataRegistry<Species> {
*/
fun random(): Species = this.implemented.random()
// overloaded function to give random pokemon species of a certain type
fun random(type: ElementalType): Species = this.implemented.filter { it.primaryType == type || it.secondaryType == type }.random()
override fun reload(data: Map<Identifier, Species>) {
this.speciesByIdentifier.clear()
this.implemented.clear()
@@ -236,8 +242,8 @@ object PokemonSpecies : JsonDataRegistry<Species> {
"spd" to (form?.baseStats?.get(Stats.SPECIAL_DEFENCE) ?: species.baseStats[Stats.SPECIAL_DEFENCE] ?: 1),
"spe" to (form?.baseStats?.get(Stats.SPEED) ?: species.baseStats[Stats.SPEED] ?: 1)
)
val heightm = form?.height ?: species.height
val weightkg = form?.weight ?: species.weight
val heightm = (form?.height ?: species.height) / 10
val weightkg = (form?.weight ?: species.weight) / 10
// This is ugly, but we already have it hardcoded in the mod anyway
val maxHP = if (species.showdownId() == "shedinja") 1 else null
val canGigantamax: String? = if (form?.gigantamaxMove != null) form.gigantamaxMove.name else null
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.api.pokemon.effect
import com.cobblemon.mod.common.api.scheduling.after
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
import com.cobblemon.mod.common.pokemon.effects.LightSourceEffect
@@ -51,7 +51,7 @@ object ShoulderEffectRegistry {
fun onEffectEnd(player: ServerPlayerEntity) {
// Do this next tick so the client syncs correctly.
// While it is a ticks worth of downtime it's still 1/20th of a second, doubt they'll notice.
after(1, serverThread = true) { this.refreshEffects(player) }
ServerTaskTracker.momentarily { this.refreshEffects(player) }
}
private fun refreshEffects(player: ServerPlayerEntity) {
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.reactive
import com.cobblemon.mod.common.api.scheduling.taskBuilder
class ObservableSubscription<T>(
private val observable: Observable<T>,
private val handler: (T) -> Unit
@@ -19,24 +18,4 @@ class ObservableSubscription<T>(
observable.unsubscribe(this)
alive = false
}
/**
* Schedules a task for every [checkInterval] seconds which checks the given [condition], and if it is
* true it unsubscribes this [ObservableSubscription]. This exists predominantly for performance reasons,
* as releasing subscriptions when they are no longer necessary should be done as soon as possible to allow
* objects to be released.
*/
fun unsubscribeWhen(checkInterval: Float = 5F, condition: () -> Boolean): ObservableSubscription<T> {
// Maybe there should just be 1 task that has a list of the conditions and observables to check, I dunno what's better - Hiro
taskBuilder()
.interval(checkInterval)
.execute {
if (!alive || condition()) {
unsubscribe()
it.expire()
}
}
.build()
return this
}
}
@@ -0,0 +1,56 @@
/*
* 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.scheduling
/**
* An interface for which an implementation must provide a [SchedulingTracker] so that the standard scheduling
* functions can be executed.
*
* @author Hiroku
* @since November 5th, 2023
*/
interface Schedulable {
val schedulingTracker: SchedulingTracker
fun momentarily(action: () -> Unit): ScheduledTask = after(action = action)
fun after(seconds: Float = 0F, action: () -> Unit): ScheduledTask {
return schedulingTracker.addTask(
ScheduledTask(
action = { action() },
delaySeconds = seconds
)
)
}
fun lerp(seconds: Float = 0F, action: (Float) -> Unit): ScheduledTask {
var passed = 0F
if (seconds == 0F) {
action(1F)
return ScheduledTask.BLANK
}
action(passed / seconds)
return if (passed / seconds != 1F) {
taskBuilder().tracker(schedulingTracker).interval(0F).iterations(-1).execute { task ->
passed = task.secondsPassed
if (passed > seconds) {
passed = seconds
}
val ratio = passed / seconds
action(ratio)
if (passed >= seconds) {
task.expire()
}
}.build()
} else {
ScheduledTask.BLANK
}
}
fun taskBuilder(): ScheduledTask.Builder = ScheduledTask.Builder().tracker(schedulingTracker)
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.scheduling
import java.util.concurrent.CompletableFuture
import kotlin.math.max
/**
@@ -29,6 +30,11 @@ class ScheduledTask(
/** The number of times this task should iterate. */
val iterations: Int = 1
) {
companion object {
val BLANK = ScheduledTask({}, delaySeconds = 0F)
}
val future = CompletableFuture<Unit>()
var secondsPassed = 0F
var currentIteration: Int = 0
private set
var secondsRemaining: Float = 0F
@@ -38,13 +44,8 @@ class ScheduledTask(
var paused = false
set(value) {
field = value
if (!value) {
lastExecutedTime = System.currentTimeMillis()
}
}
var lastExecutedTime = System.currentTimeMillis()
init {
if (delaySeconds > 0) {
secondsRemaining = delaySeconds
@@ -55,13 +56,10 @@ class ScheduledTask(
return identifier?.let { "Task-$it" } ?: super.toString()
}
fun update() {
val now = System.currentTimeMillis()
val passed = now - lastExecutedTime
lastExecutedTime = now
fun update(deltaSeconds: Float) {
if (!expired && !paused) {
secondsRemaining = max(0F, secondsRemaining - passed / 1000F)
secondsPassed += deltaSeconds
secondsRemaining = max(0F, secondsRemaining - deltaSeconds)
if (secondsRemaining == 0F) {
action(this)
currentIteration++
@@ -76,6 +74,7 @@ class ScheduledTask(
fun expire() {
this.expired = true
future.complete(Unit)
}
class Builder {
@@ -91,7 +90,9 @@ class ScheduledTask(
private var iterations: Int = 1
private var identifier: String? = null
private var tracker: SchedulingTracker? = null
fun identifier(identifier: String): Builder {
this.identifier = identifier
return this
@@ -156,6 +157,11 @@ class ScheduledTask(
return iterations(-1)
}
fun tracker(schedulingTracker: SchedulingTracker): Builder {
this.tracker = schedulingTracker
return this
}
/**
* Builds the task using the properties of the task builder.
* This will add the task to [TaskTickListener].
@@ -172,7 +178,7 @@ class ScheduledTask(
intervalSeconds = interval,
iterations = iterations
)
ScheduledTaskTracker.addTask(task)
(tracker ?: ServerTaskTracker).addTask(task)
return task
}
}
@@ -8,23 +8,28 @@
package com.cobblemon.mod.common.api.scheduling
object ScheduledTaskTracker {
private val tasks = mutableListOf<ScheduledTask>()
fun clear() {
tasks.clear()
}
/**
* More precise than the [ServerTaskTracker], still runs on the server, and uses epoch time deltas for
* the scheduling delta so that it is working by real time and not by ticks. This is useful for some
* applications but is generally not what you want.
*/
object ServerRealTimeTaskTracker : Schedulable, SchedulingTracker() {
override val schedulingTracker = this
var lastTicked = System.currentTimeMillis()
fun update() {
for (task in tasks.toList()) {
task.update()
if (task.expired) {
tasks.remove(task)
}
}
val now = System.currentTimeMillis()
val delta = now - lastTicked
lastTicked = now
update(delta / 1000F)
}
}
fun addTask(task: ScheduledTask) {
tasks.add(task)
}
object ServerTaskTracker: Schedulable, SchedulingTracker() {
override val schedulingTracker = this
}
object ClientTaskTracker : Schedulable, SchedulingTracker() {
override val schedulingTracker = this
}
@@ -8,45 +8,27 @@
package com.cobblemon.mod.common.api.scheduling
import com.cobblemon.mod.common.util.runOnServer
@Deprecated("Use afterOnServer or afterOnClient; ambiguous side is not good for your health")
@JvmOverloads
fun after(ticks: Int = 0, seconds: Float = 0F, serverThread: Boolean = false, action: () -> Unit) {
ScheduledTaskTracker.addTask(
ScheduledTask(
action = { if (serverThread) runOnServer(action) else action() },
delaySeconds = ticks / 20F + seconds
)
)
val scheduler = if (serverThread) ServerTaskTracker else ClientTaskTracker
scheduler.after(seconds = seconds + ticks / 20F, action)
}
/**
* Same as [after] but the task is made to run on the main thread. This is for when the task
* Delayed task is created to run on the main thread. This is for when the task
* being completed after the delay does things like entity removal or other thread-unsafe actions.
*/
fun afterOnMain(ticks: Int = 0, seconds: Float = 0F, action: () -> Unit) = after(ticks, seconds, true, action)
@JvmOverloads
fun afterOnServer(ticks: Int = 0, seconds: Float = 0F, action: () -> Unit) = ServerTaskTracker.after(seconds + ticks / 20F, action)
@JvmOverloads
fun afterOnClient(ticks: Int = 0, seconds: Float, action: () -> Unit) = ClientTaskTracker.after(seconds + ticks / 20F, action)
@Deprecated("Use lerpOnServer or lerpOnClient, side-ambiguity causes problems now")
fun lerp(seconds: Float = 0F, serverThread: Boolean = false, action: (Float) -> Unit) = (if (serverThread) ServerTaskTracker else ClientTaskTracker).lerp(seconds, action = action)
fun lerp(seconds: Float = 0F, serverThread: Boolean = false, action: (Float) -> Unit) {
val startedTime = System.currentTimeMillis()
var passed = 0F
if (seconds == 0F) {
if (serverThread) runOnServer { action(1F) } else action(1F)
return
}
action(passed / seconds)
if (passed / seconds != 1F) {
taskBuilder().interval(0F).iterations(-1).execute { task ->
passed = (System.currentTimeMillis() - startedTime)/1000F
if (passed > seconds) {
passed = seconds
}
val ratio = passed / seconds
if (serverThread) runOnServer { action(ratio) } else action(ratio)
if (passed >= seconds) {
task.expire()
}
}.build()
}
}
@JvmOverloads
fun lerpOnServer(seconds: Float = 0F, action: (Float) -> Unit) = ServerTaskTracker.lerp(seconds = seconds, action = action)
@JvmOverloads
fun lerpOnClient(seconds: Float = 0F, action: (Float) -> Unit) = ClientTaskTracker.lerp(seconds = seconds, action = action)
fun taskBuilder() = ScheduledTask.Builder()
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.scheduling
/**
* A data block for scheduled tasks. This is to neatly contain the data for an
* implementation of [Schedulable].
*
* @author Hiroku
* @since November 5th, 2023
*/
open class SchedulingTracker {
private val tasks = mutableListOf<ScheduledTask>()
fun clear() {
tasks.clear()
}
fun update(deltaSeconds: Float) {
for (task in tasks.toList()) {
task.update(deltaSeconds)
if (task.expired) {
tasks.remove(task)
}
}
}
fun addTask(task: ScheduledTask): ScheduledTask {
tasks.add(task)
return task
}
}
@@ -20,6 +20,7 @@ import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
import net.minecraft.util.math.ChunkSectionPos.getSectionCoord
import net.minecraft.util.math.Vec3d
import net.minecraft.world.LightType
import net.minecraft.world.chunk.Chunk
import net.minecraft.world.chunk.ChunkStatus
@@ -69,7 +70,7 @@ object CobblemonSpawningProspector : SpawningProspector {
.map { it.pos }
val defaultState = Blocks.STONE.defaultState
val defaultBlockData = WorldSlice.BlockData(defaultState, 0)
val defaultBlockData = WorldSlice.BlockData(defaultState, 0, 0)
val blocks = Array(area.length) { Array(height) { Array(area.width) { defaultBlockData } } }
val skyLevel = Array(area.length) { Array(area.width) { world.topY } }
@@ -77,6 +78,7 @@ object CobblemonSpawningProspector : SpawningProspector {
val chunks = mutableMapOf<Pair<Int, Int>, Chunk?>()
val yRange = (baseY until baseY + height).reversed()
val lightingProvider = world.lightingProvider
for (x in area.baseX until area.baseX + area.length) {
for (z in area.baseZ until area.baseZ + area.width) {
val query = chunks.computeIfAbsent(Pair(getSectionCoord(x), getSectionCoord(z))) {
@@ -85,16 +87,16 @@ object CobblemonSpawningProspector : SpawningProspector {
var canSeeSky = world.isSkyVisibleAllowingSea(pos.set(x, yRange.first, z))
for (y in yRange) {
val skyLight = lightingProvider.get(LightType.SKY).getLightLevel(pos.set(x, y, z))
val state = query.getBlockState(pos.set(x, y, z))
blocks[x - area.baseX][y - baseY][z - area.baseZ] = WorldSlice.BlockData(
state = state,
light = world.getLightLevel(pos)
light = world.getLightLevel(pos),
skyLight = skyLight
)
if (canSeeSky) {
skyLevel[x - area.baseX][z - area.baseZ] = y
}
if (state.fluidState.isEmpty && !state.isIn(CobblemonBlockTags.SEES_SKY)) {
canSeeSky = false
}
@@ -0,0 +1,84 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.rules.SpawnRule
import com.cobblemon.mod.common.api.spawning.rules.component.FilterRuleComponent
import com.cobblemon.mod.common.api.spawning.rules.component.LocationRuleCalculator
import com.cobblemon.mod.common.api.spawning.rules.component.SpawnRuleComponent
import com.cobblemon.mod.common.api.spawning.rules.component.WeightTweakRuleComponent
import com.cobblemon.mod.common.api.spawning.rules.selector.ConditionalSpawningContextSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.ExpressionSpawnDetailSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.ExpressionSpawningContextSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawnDetailSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawningContextSelector
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.SpawnDetailSelectorAdapter
import com.cobblemon.mod.common.util.adapters.SpawnRuleComponentAdapter
import com.cobblemon.mod.common.util.adapters.SpawningConditionAdapter
import com.cobblemon.mod.common.util.adapters.SpawningContextSelectorAdapter
import com.cobblemon.mod.common.util.adapters.TextAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import net.minecraft.util.Identifier
/**
* All of the [SpawnRule]s that will be applied as global [SpawningInfluence]s.
*
* @author Hiroku
* @since September 30th, 2023
*/
object CobblemonSpawnRules : JsonDataRegistry<SpawnRule> {
override val gson = GsonBuilder()
.registerTypeAdapter(SpawnRuleComponent::class.java, SpawnRuleComponentAdapter)
.registerTypeAdapter(SpawnDetailSelector::class.java, SpawnDetailSelectorAdapter)
.registerTypeAdapter(SpawningContextSelector::class.java, SpawningContextSelectorAdapter)
.registerTypeAdapter(SpawningCondition::class.java, SpawningConditionAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(Text::class.java, TextAdapter)
.create()
override val typeToken = TypeToken.get(SpawnRule::class.java)
override val resourcePath = "spawn_rules"
init {
SpawnRuleComponent.register<WeightTweakRuleComponent>("weight")
SpawnRuleComponent.register<FilterRuleComponent>("filter")
SpawnRuleComponent.register<LocationRuleCalculator>("location")
SpawnDetailSelector.register<ExpressionSpawnDetailSelector>("expression")
SpawningContextSelector.register<ExpressionSpawningContextSelector>("expression")
SpawningContextSelector.register<ConditionalSpawningContextSelector>("conditional")
}
val rules = mutableMapOf<Identifier, SpawnRule>()
override fun reload(data: Map<Identifier, SpawnRule>) {
rules.clear()
rules.putAll(data)
data.forEach { (id, value) -> value.id = id }
observable.emit(this)
}
override val id: Identifier = cobblemonResource("spawn_rules")
override val type: ResourceType = ResourceType.SERVER_DATA
override val observable = SimpleObservable<CobblemonSpawnRules>()
override fun sync(player: ServerPlayerEntity) {}
}
@@ -44,7 +44,8 @@ class WorldSlice(
) {
class BlockData(
val state: BlockState,
val light: Int
val light: Int,
val skyLight: Int
)
val length = blocks.size
@@ -83,6 +84,15 @@ class WorldSlice(
}
fun getLight(position: BlockPos, elseLight: Int = 0) = getLight(position.x, position.y, position.z, elseLight)
fun getSkyLight(x: Int, y: Int, z: Int, elseLight: Int = 0): Int {
return if (!isInBounds(x, y, z)) {
elseLight
} else {
getBlockData(x, y, z).skyLight
}
}
fun getSkyLight(position: BlockPos, elseLight: Int = 0) = getSkyLight(position.x, position.y, position.z, elseLight)
fun skySpaceAbove(x: Int, y: Int, z: Int): Int {
return if (!isInBounds(x, y, z) || skyLevel[x - baseX][z - baseZ] > y) {
0
@@ -55,5 +55,5 @@ interface AppendageCondition {
fun getAppendages(spawningCondition: SpawningCondition<*>) = appendages.filter { it.spawningConditionFits(spawningCondition) }.map { it.clazz }
}
fun fits(ctx: SpawningContext, detail: SpawnDetail): Boolean
fun fits(ctx: SpawningContext): Boolean
}
@@ -26,8 +26,8 @@ abstract class AreaTypeSpawningCondition<T : AreaSpawningContext> : SpawningCond
var maxHeight: Int? = null
var neededNearbyBlocks: MutableList<RegistryLikeCondition<Block>>? = null
override fun fits(ctx: T, detail: SpawnDetail): Boolean {
if (!super.fits(ctx, detail)) {
override fun fits(ctx: T): Boolean {
if (!super.fits(ctx)) {
return false
} else if (minHeight != null && ctx.height < minHeight!!) {
return false
@@ -30,11 +30,11 @@ class CompositeSpawningCondition {
var conditions = mutableListOf<SpawningCondition<*>>()
var anticonditions = mutableListOf<SpawningCondition<*>>()
fun satisfiedBy(ctx: SpawningContext, detail: SpawnDetail): Boolean {
return if (conditions.isNotEmpty() && conditions.none { it.isSatisfiedBy(ctx, detail) }) {
fun satisfiedBy(ctx: SpawningContext): Boolean {
return if (conditions.isNotEmpty() && conditions.none { it.isSatisfiedBy(ctx) }) {
false
} else {
!(anticonditions.isNotEmpty() && anticonditions.any { it.isSatisfiedBy(ctx, detail) })
!(anticonditions.isNotEmpty() && anticonditions.any { it.isSatisfiedBy(ctx) })
}
}
}
@@ -24,8 +24,8 @@ import net.minecraft.block.Block
abstract class GroundedTypeSpawningCondition<T : GroundedSpawningContext> : AreaTypeSpawningCondition<T>() {
var neededBaseBlocks: MutableList<RegistryLikeCondition<Block>>? = null
override fun fits(ctx: T, detail: SpawnDetail): Boolean {
return if (!super.fits(ctx, detail)) {
override fun fits(ctx: T): Boolean {
return if (!super.fits(ctx)) {
false
} else if (minHeight != null && ctx.height < minHeight!!) {
return false
@@ -11,8 +11,6 @@ package com.cobblemon.mod.common.api.spawning.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.MoonPhaseRange
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.spawning.condition.ListCheckMode.ALL
import com.cobblemon.mod.common.api.spawning.condition.ListCheckMode.ANY
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.util.Merger
@@ -52,11 +50,11 @@ abstract class SpawningCondition<T : SpawningContext> {
var maxZ: Float? = null
var minLight: Int? = null
var maxLight: Int? = null
var minSkyLight: Int? = null
var maxSkyLight: Int? = null
var isRaining: Boolean? = null
var isThundering: Boolean? = null
var timeRange: TimeRange? = null
var labels: MutableList<String>? = null
var labelMode = ANY
var structures: MutableList<Either<Identifier, TagKey<Structure>>>? = null
@Transient
@@ -65,15 +63,15 @@ abstract class SpawningCondition<T : SpawningContext> {
abstract fun contextClass(): Class<out T>
fun contextMatches(ctx: SpawningContext) = contextClass().isAssignableFrom(ctx::class.java)
fun isSatisfiedBy(ctx: SpawningContext, detail: SpawnDetail): Boolean {
fun isSatisfiedBy(ctx: SpawningContext): Boolean {
return if (contextMatches(ctx)) {
fits(ctx as T, detail)
fits(ctx as T)
} else {
false
}
}
protected open fun fits(ctx: T, detail: SpawnDetail): Boolean {
protected open fun fits(ctx: T): Boolean {
if (ctx.position.x < minX.orMin() || ctx.position.x > maxX.orMax()) {
return false
} else if (ctx.position.y < minY.orMin() || ctx.position.y > maxY.orMax()) {
@@ -88,6 +86,8 @@ abstract class SpawningCondition<T : SpawningContext> {
return false
} else if (ctx.light > maxLight.orMax() || ctx.light < minLight.orMin()) {
return false
} else if (ctx.skyLight > maxSkyLight.orMax() || ctx.skyLight < minSkyLight.orMin()) {
return false
} else if (timeRange != null && !timeRange!!.contains((ctx.world.timeOfDay % 24000).toInt())) {
return false
} else if (canSeeSky != null && canSeeSky != ctx.canSeeSky) {
@@ -96,14 +96,7 @@ abstract class SpawningCondition<T : SpawningContext> {
return false
} else if (isThundering != null && ctx.world.isThundering != isThundering!!) {
return false
} else if (labels != null && labels!!.isNotEmpty() &&
(
(labelMode == ANY && labels!!.none { it in detail.labels }) ||
(labelMode == ALL && labels!!.any { it !in detail.labels })
)
) {
return false
} else if (appendages.any { !it.fits(ctx, detail) }) {
} else if (appendages.any { !it.fits(ctx) }) {
return false
} else if (structures != null && structures!!.isNotEmpty() &&
structures!!.let { structures ->
@@ -123,7 +116,6 @@ abstract class SpawningCondition<T : SpawningContext> {
open fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
dimensions = merger.merge(dimensions, other.dimensions)?.toMutableList()
biomes = merger.merge(biomes, other.biomes)?.toMutableSet()
labels = merger.merge(labels, other.labels)?.toMutableList()
moonPhase = merger.mergeSingle(moonPhase, other.moonPhase)
canSeeSky = merger.mergeSingle(canSeeSky, other.canSeeSky)
minX = merger.mergeSingle(minX, other.minX)
@@ -134,6 +126,8 @@ abstract class SpawningCondition<T : SpawningContext> {
maxZ = merger.mergeSingle(maxZ, other.maxZ)
minLight = merger.mergeSingle(minLight, other.minLight)
maxLight = merger.mergeSingle(maxLight, other.maxLight)
minSkyLight = merger.mergeSingle(minSkyLight, other.minSkyLight)
maxSkyLight = merger.mergeSingle(maxSkyLight, other.maxSkyLight)
timeRange = merger.mergeSingle(timeRange, other.timeRange)
structures = merger.merge(structures, other.structures)?.toMutableList()
}
@@ -27,8 +27,8 @@ abstract class SubmergedTypeSpawningCondition<T : SubmergedSpawningContext> : Ar
var fluidIsSource: Boolean? = null
var fluid: RegistryLikeCondition<Fluid>? = null
override fun fits(ctx: T, detail: SpawnDetail): Boolean {
return if (!super.fits(ctx, detail)) {
override fun fits(ctx: T): Boolean {
return if (!super.fits(ctx)) {
false
} else if (minHeight != null && ctx.height < minHeight!!) {
return false
@@ -26,8 +26,8 @@ abstract class SurfaceTypeSpawningCondition<T : SurfaceSpawningContext> : AreaTy
var maxDepth: Int? = null
var fluid: RegistryLikeCondition<Fluid>? = null
override fun fits(ctx: T, detail: SpawnDetail): Boolean {
return if (!super.fits(ctx, detail)) {
override fun fits(ctx: T): Boolean {
return if (!super.fits(ctx)) {
false
} else if (minDepth != null && ctx.depth < minDepth!!) {
false
@@ -49,7 +49,8 @@ interface AreaContextResolver {
pos.set(x, y, z)
val vec = pos.toVec3d()
if (slice.nearbyEntityPositions.none { it.isInRange(vec, config.minimumDistanceBetweenEntities) && it != slice.cause.entity }) {
val fittedContextCalculator = contextCalculators.firstOrNull { it.fits(input) }
val fittedContextCalculator = contextCalculators
.firstOrNull { calc -> calc.fits(input) && input.spawner.influences.none { !it.isAllowedPosition(input.world, input.position, calc) } }
if (fittedContextCalculator != null) {
val context = fittedContextCalculator.calculate(input)
if (context != null) {
@@ -30,6 +30,7 @@ open class AreaSpawningContext(
override val world: ServerWorld,
override val position: BlockPos,
override val light: Int,
override val skyLight: Int,
override val canSeeSky: Boolean,
override val influences: MutableList<SpawningInfluence>,
/** Space above. */
@@ -27,12 +27,13 @@ abstract class FlooredSpawningContext(
world: ServerWorld,
position: BlockPos,
light: Int,
skyLight: Int,
canSeeSky: Boolean,
influences: MutableList<SpawningInfluence>,
height: Int,
nearbyBlocks: List<BlockState>,
slice: WorldSlice
) : AreaSpawningContext(cause, world, position, light, canSeeSky, influences, height, nearbyBlocks, slice) {
) : AreaSpawningContext(cause, world, position, light, skyLight, canSeeSky, influences, height, nearbyBlocks, slice) {
/** The block that the spawning is occurring on. */
val baseBlock = slice.getBlockState(position.x, position.y, position.z)
}
@@ -48,12 +49,13 @@ open class GroundedSpawningContext(
world: ServerWorld,
position: BlockPos,
light: Int,
skyLight: Int,
canSeeSky: Boolean,
influences: MutableList<SpawningInfluence>,
height: Int,
nearbyBlocks: List<BlockState>,
slice: WorldSlice
) : FlooredSpawningContext(cause, world, position, light, canSeeSky, influences, height, nearbyBlocks, slice)
) : FlooredSpawningContext(cause, world, position, light, skyLight, canSeeSky, influences, height, nearbyBlocks, slice)
/**
* A spawning context that occurs at the bottom of a body of water.
@@ -66,12 +68,13 @@ open class SeafloorSpawningContext(
world: ServerWorld,
position: BlockPos,
light: Int,
skyLight: Int,
canSeeSky: Boolean,
influences: MutableList<SpawningInfluence>,
height: Int,
nearbyBlocks: List<BlockState>,
slice: WorldSlice
) : FlooredSpawningContext(cause, world, position, light, canSeeSky, influences, height, nearbyBlocks, slice) {
) : FlooredSpawningContext(cause, world, position, light, skyLight, canSeeSky, influences, height, nearbyBlocks, slice) {
override fun isSafeSpace(world: ServerWorld, pos: BlockPos, state: BlockState) = state.fluidState.isIn(FluidTags.WATER)
}
@@ -86,12 +89,13 @@ open class LavafloorSpawningContext(
world: ServerWorld,
position: BlockPos,
light: Int,
skyLight: Int,
canSeeSky: Boolean,
influences: MutableList<SpawningInfluence>,
height: Int,
nearbyBlocks: List<BlockState>,
slice: WorldSlice
) : FlooredSpawningContext(cause, world, position, light, canSeeSky, influences, height, nearbyBlocks, slice) {
) : FlooredSpawningContext(cause, world, position, light, skyLight, canSeeSky, influences, height, nearbyBlocks, slice) {
override fun isSafeSpace(world: ServerWorld, pos: BlockPos, state: BlockState) = state.fluidState.isIn(FluidTags.LAVA)
}
@@ -100,10 +104,11 @@ open class SurfaceSpawningContext(
world: ServerWorld,
position: BlockPos,
light: Int,
skyLight: Int,
canSeeSky: Boolean,
influences: MutableList<SpawningInfluence>,
height: Int,
val depth: Int,
nearbyBlocks: List<BlockState>,
slice: WorldSlice
) : FlooredSpawningContext(cause, world, position, light, canSeeSky, influences, height, nearbyBlocks, slice)
) : FlooredSpawningContext(cause, world, position, light, skyLight, canSeeSky, influences, height, nearbyBlocks, slice)
@@ -8,6 +8,9 @@
package com.cobblemon.mod.common.api.spawning.context
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.condition.BasicSpawningCondition
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
@@ -61,6 +64,8 @@ abstract class SpawningContext {
abstract val position: BlockPos
/** The light level at this location. */
abstract val light: Int
/** The sky light level at this location. (15 - The distance to the nearest block that would be illuminated by the sun */
abstract val skyLight: Int
/** Whether or not the sky is visible at this location. */
abstract val canSeeSky: Boolean
/** A list of [SpawningInfluence]s that apply due to this specific context. */
@@ -80,6 +85,9 @@ abstract class SpawningContext {
val biomeName: Identifier
get() = this.biomeRegistry.getId(biome)!!
private val struct = VariableStruct()
private var structCompiled = false
class StructureChunkCache {
val missingTags = mutableSetOf<TagKey<Structure>>()
val foundTags = mutableSetOf<TagKey<Structure>>()
@@ -159,4 +167,21 @@ abstract class SpawningContext {
}
return weight
}
fun getOrSetupStruct(): VariableStruct {
if (structCompiled) {
return struct
}
struct.setDirectly("light", DoubleValue(light.toDouble()))
struct.setDirectly("x", DoubleValue(position.x.toDouble()))
struct.setDirectly("y", DoubleValue(position.y.toDouble()))
struct.setDirectly("z", DoubleValue(position.z.toDouble()))
struct.setDirectly("moon_phase", DoubleValue(moonPhase.toDouble()))
struct.setDirectly("world", ObjectValue(world.registryManager.get(RegistryKeys.WORLD).getEntry(world)))
struct.setDirectly("biome", ObjectValue(biomeRegistry.getEntry(biome)))
structCompiled = true
return struct
}
}
@@ -26,13 +26,14 @@ open class SubmergedSpawningContext(
world: ServerWorld,
position: BlockPos,
light: Int,
skyLight: Int,
canSeeSky: Boolean,
influences: MutableList<SpawningInfluence>,
height: Int,
val depth: Int,
nearbyBlocks: List<BlockState>,
slice: WorldSlice
) : AreaSpawningContext(cause, world, position, light, canSeeSky, influences, height, nearbyBlocks, slice) {
) : AreaSpawningContext(cause, world, position, light, skyLight, canSeeSky, influences, height, nearbyBlocks, slice) {
val fluid = slice.getBlockState(position.x, position.y, position.z).fluidState
override fun isSafeSpace(world: ServerWorld, pos: BlockPos, state: BlockState) = state.fluidState.fluid == fluid.fluid
@@ -45,6 +45,8 @@ interface AreaSpawningContextCalculator<O : AreaSpawningContext> : SpawningConte
= input.slice.horizontalSpace(input.position.x + offsetX, input.position.y + offsetY, input.position.z + offsetZ, condition, maximum)
fun getLight(input: AreaSpawningInput, elseLight: Int = 0): Int
= input.slice.getLight(input.position.x, input.position.y + 1, input.position.z, elseLight)
fun getSkyLight(input: AreaSpawningInput, elseLight: Int = 0): Int
= input.slice.getSkyLight(input.position.x, input.position.y + 1, input.position.z, elseLight)
fun getCanSeeSky(input: AreaSpawningInput): Boolean = input.slice.canSeeSky(input.position.x, input.position.y + 1, input.position.z)
fun getSkySpaceAbove(input: AreaSpawningInput): Int = input.slice.skySpaceAbove(input.position.x, input.position.y, input.position.z)
fun getNearbyBlocks(
@@ -50,6 +50,7 @@ interface FlooredSpawningContextCalculator<T : FlooredSpawningContext> : AreaSpa
*/
object GroundedSpawningContextCalculator : FlooredSpawningContextCalculator<GroundedSpawningContext> {
// TODO expand base condition
override val name = "grounded"
override val baseCondition: (BlockState) -> Boolean = isSolidCondition
override val surroundingCondition: (BlockState) -> Boolean = isAirCondition
@@ -59,6 +60,7 @@ object GroundedSpawningContextCalculator : FlooredSpawningContextCalculator<Grou
world = input.world,
position = input.position.toImmutable(),
light = getLight(input),
skyLight = getSkyLight(input),
canSeeSky = getCanSeeSky(input),
influences = input.spawner.copyInfluences(),
height = getHeight(input, surroundingCondition, config.maxVerticalSpace, offsetY = 1),
@@ -76,6 +78,7 @@ object GroundedSpawningContextCalculator : FlooredSpawningContextCalculator<Grou
* @since February 7th, 2022
*/
object SeafloorSpawningContextCalculator : FlooredSpawningContextCalculator<SeafloorSpawningContext> {
override val name = "seafloor"
override val baseCondition: (BlockState) -> Boolean = isSolidCondition
override val surroundingCondition: (BlockState) -> Boolean = isWaterCondition
@@ -85,6 +88,7 @@ object SeafloorSpawningContextCalculator : FlooredSpawningContextCalculator<Seaf
world = input.world,
position = input.position.toImmutable(),
light = getLight(input),
skyLight = getSkyLight(input),
canSeeSky = getCanSeeSky(input),
influences = input.spawner.copyInfluences(),
height = getHeight(input, surroundingCondition, config.maxVerticalSpace, offsetY = 1),
@@ -102,6 +106,7 @@ object SeafloorSpawningContextCalculator : FlooredSpawningContextCalculator<Seaf
* @since February 7th, 2022
*/
object LavafloorSpawningContextCalculator : FlooredSpawningContextCalculator<LavafloorSpawningContext> {
override val name = "lavafloor"
override val baseCondition: (BlockState) -> Boolean = isSolidCondition
override val surroundingCondition: (BlockState) -> Boolean = isLavaCondition
@@ -111,6 +116,7 @@ object LavafloorSpawningContextCalculator : FlooredSpawningContextCalculator<Lav
world = input.world,
position = input.position.toImmutable(),
light = getLight(input),
skyLight = getSkyLight(input),
canSeeSky = getCanSeeSky(input),
influences = input.spawner.copyInfluences(),
height = getHeight(input, surroundingCondition, config.maxVerticalSpace, offsetY = 1),
@@ -129,6 +135,7 @@ object LavafloorSpawningContextCalculator : FlooredSpawningContextCalculator<Lav
* @since December 15th, 2022
*/
object SurfaceSpawningContextCalculator : FlooredSpawningContextCalculator<SurfaceSpawningContext> {
override val name = "surface"
override val baseCondition: (BlockState) -> Boolean = { !it.fluidState.isEmpty }
override val surroundingCondition: (BlockState) -> Boolean = isAirCondition
@@ -138,6 +145,7 @@ object SurfaceSpawningContextCalculator : FlooredSpawningContextCalculator<Surfa
world = input.world,
position = input.position.toImmutable(),
light = getLight(input),
skyLight = getSkyLight(input),
canSeeSky = getCanSeeSky(input),
influences = input.spawner.copyInfluences(),
height = getHeight(input, surroundingCondition, config.maxVerticalSpace / 2, offsetY = 1),
@@ -48,6 +48,8 @@ interface SpawningContextCalculator<I : SpawningContextInput, O : SpawningContex
}
}
val name: String
/** Tries creating a [SpawningContext] from the given input. Returning null should be a last resort. */
fun calculate(input: I): O?
}
@@ -23,6 +23,7 @@ import net.minecraft.util.math.MathHelper.ceil
* @since February 7th, 2022
*/
object SubmergedSpawningContextCalculator : AreaSpawningContextCalculator<SubmergedSpawningContext> {
override val name = "submerged"
val fluidConditions = mutableListOf(
isWaterCondition,
isLavaCondition
@@ -45,6 +46,7 @@ object SubmergedSpawningContextCalculator : AreaSpawningContextCalculator<Submer
world = input.world,
position = input.position.toImmutable(),
light = getLight(input),
skyLight = getSkyLight(input),
canSeeSky = getCanSeeSky(input),
influences = input.spawner.copyInfluences(),
height = getHeight(input, fluidCondition, ceil(config.maxVerticalSpace / 2F)),
@@ -8,8 +8,8 @@
package com.cobblemon.mod.common.api.spawning.detail
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -50,6 +50,9 @@ class PokemonSpawnAction(
entity.pokemon.swapHeldItem(heldItem)
}
entity.drops = detail.drops
// Useful debug code in situations where you want to find spawns
// val fireworkRocketEntity = FireworkRocketEntity(ctx.world, ctx.position.x.toDouble(), ctx.position.y.toDouble() + 2, ctx.position.z.toDouble(), ItemStack(Items.FIREWORK_ROCKET))
// ctx.world.spawnEntity(fireworkRocketEntity)
return entity
}
}
@@ -69,7 +69,7 @@ class PokemonSpawnDetail : SpawnDetail() {
}
override fun autoLabel() {
super.autoLabel()
val pokemonStruct = pokemon.asStruct()
if (pokemon.species != null) {
val species = PokemonSpecies.getByIdentifier(pokemon.species!!.asIdentifierDefaultingNamespace())
if (species != null) {
@@ -87,6 +87,9 @@ class PokemonSpawnDetail : SpawnDetail() {
}
}
}
struct.setDirectly("pokemon", pokemonStruct)
super.autoLabel()
}
fun getDerivedLevelRange() = levelRange.let { levelRange ->
Loaded 100 of 2314 files, more files were not shown because too many files have changed in this diff. Show more