Compare commits

...
147 Commits
Author SHA1 Message Date
Hiroku d6c543e965 Fixed Porygon evolution 2023-05-28 01:04:26 +00:00
Hiroku 505eb06406 Merge branch '1.3.2-dupe-fix' into '1.3.2-dupe-fix'
Fix a crash introduced by issue !482

See merge request cable-mc/cobblemon!483
2023-05-28 00:59:56 +00:00
Neovitalism 83963d038d Clean my unused imports 2023-05-23 20:01:37 -04:00
Neovitalism cedcf1aacf Only force recall when the player teleports
Sadly teleporting the mon caused too many issues and crashes
2023-05-23 16:41:54 -04:00
Neovitalism 5613726acd Merge branch '1.3.2-dupe-fix' of https://gitlab.com/Neovitalism/cobblemon into 1.3.2-dupe-fix 2023-05-23 06:23:43 -04:00
Neovitalism 4be346d96d Make pokemon being manually recalled not trigger onStoppedTrackingBy 2023-05-23 06:23:27 -04:00
Neovitalism 29e6861f88 Merge branch '1.3.2-dupe-fix' into '1.3.2-dupe-fix'
# Conflicts:
#   common/src/main/kotlin/com/cobblemon/mod/common/util/EntityExtensions.kt
2023-05-23 01:42:03 +00:00
Neovitalism 6deb272ec3 Change to teleport since teleport loads the chunk 2023-05-22 21:33:36 -04:00
Neovitalism 18caae05ca Load the chunk the PokemonEntity is being recalled from 2023-05-21 20:35:28 -04:00
Hiroku 1bb4692273 Merge branch '1.3.2-dupe-fix' into '1.3.2-dupe-fix'
Change ticking the GoalSelector to our own teleportToOwner attempt

See merge request cable-mc/cobblemon!482
2023-05-21 12:32:40 +00:00
NeovitalismandHiroku 6a4bd4dfa6 Change ticking the GoalSelector to our own teleportToOwner attempt 2023-05-21 12:32:40 +00:00
Neovitalism 28ff02cc3e Merge branch '1.3.2-dupe-fix' of https://gitlab.com/Neovitalism/cobblemon into 1.3.2-dupe-fix 2023-05-21 08:00:53 -04:00
Neovitalism b8378bf11c Move teleport methods to EntityExtensions.kt
This also moves the larger part of the methods to TameableEntity instead of PokemonEntity, allowing for more vanilla use, as well. (Not that it's a huge change lol)
2023-05-21 08:00:49 -04:00
Neovitalism 8d8cf6d071 Merge branch '1.3.2-dupe-fix' into '1.3.2-dupe-fix'
# Conflicts:
#   common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
2023-05-20 20:30:37 +00:00
Neovitalism 6b2b9fb064 Change ticking the GoalSelector to our own teleportToOwner attempt
This just moves the methods we need instead of ticking the GoalSelector, this not only saves all goals from ticking but allows us to do custom logic when the teleport fails (recall)
2023-05-20 16:16:27 -04:00
Hiroku 2c151ddcec Merge branch '1.3.1' into '1.3.2-dupe-fix'
Fix owned PokemonEntity from not returning to the player when left in unloaded chunks

See merge request cable-mc/cobblemon!481
2023-05-20 12:03:43 +00:00
NeovitalismandHiroku 1b4584557d Fix owned PokemonEntity from not returning to the player when left in unloaded chunks 2023-05-20 12:03:42 +00:00
Neovitalism 6773084212 Fix owned PokemonEntity from not returning to the player when left in unloaded chunks
This just ticks the pokemon's goal once when the owning player stops loading the mon, so this works in unloaded chunks and does nothing different in loaded chunks
2023-05-19 14:00:50 -04:00
Paul fead7cc367 changelog for 1.3.1 [skip-ci] 2023-04-01 11:16:02 +10:00
Paul 622b06b68b 1.3.1 non-snapshot [skip-ci] 2023-04-01 11:04:28 +10:00
Hiroku af7645feb4 Merge branch 'ys/no-target-pokemon-fix' into 'main'
No target pokemon fix (for moves like Aromatherapy)

See merge request cable-mc/cobblemon!431
2023-04-01 00:38:43 +00:00
YaseenandHiroku ccf7218cfc No target pokemon fix (for moves like Aromatherapy) 2023-04-01 00:38:43 +00:00
Hiroku 9b767f5170 Merge branch 'aromatherapy_crash_&_link_cable_inconsistency' into 'main'
Aromatherapy crash & Link Cable fix

See merge request cable-mc/cobblemon!430
2023-03-31 23:07:30 +00:00
LiciousandHiroku fcb4861e94 Aromatherapy crash & Link Cable fix 2023-03-31 23:07:30 +00:00
Hiroku aa8b0d3fbb Merge branch 'server-client-resource-fix' into 'main'
server crash fix for resource reload fix

See merge request cable-mc/cobblemon!429
2023-03-31 23:07:19 +00:00
MoeBoy76 d4fa5255ec server crash fix for resource reload fix 2023-04-01 09:01:31 +11:00
Hiroku 4f3e874e70 Merge branch 'bucket-influence' into 'main'
Influences can now modify bucket weight

See merge request cable-mc/cobblemon!428
2023-03-31 13:16:16 +00:00
DeltricandHiroku 6df0f17798 Influences can now modify bucket weight 2023-03-31 13:16:16 +00:00
Paul a9e3115b46 Merge remote-tracking branch 'origin/main' 2023-03-31 22:48:12 +10:00
Paul bef8e064f1 1.3.1 in the Cobblemon object 2023-03-31 22:47:18 +10:00
Hiroku 79873c44e7 Merge branch 'GDWR/pokebox_command' into 'main'
Add `pokebox` and `pokeboxall` commands

Closes #169

See merge request cable-mc/cobblemon!401
2023-03-31 12:01:15 +00:00
Paul d4abf9f2b5 Merge remote-tracking branch 'home/main' into GDWR/pokebox_command
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
2023-03-31 22:00:00 +10:00
Hiroku fd8b6c50a5 Merge branch 'qol-switch-to-last-pokemon' into 'main'
Switch selected party Pokemon to the last Pokemon used after a battle

See merge request cable-mc/cobblemon!394
2023-03-31 11:35:10 +00:00
chandler05andHiroku 2b872d85cf Switch selected party Pokemon to the last Pokemon used after a battle 2023-03-31 11:35:10 +00:00
Hiroku d98908b91d Merge branch '206-instant-flee' into 'main'
Added check for fleeDistance == -1 in checkFlee()

Closes #206

See merge request cable-mc/cobblemon!418
2023-03-31 11:34:08 +00:00
selfdotandHiroku f592e6bc05 Added check for fleeDistance == -1 in checkFlee() 2023-03-31 11:34:07 +00:00
Hiroku dad86f3692 Merge branch 'GDWR/add_spawn_pokemon_from_pool' into 'main'
Add `SpawnPokemonFromPool` command

Closes #170

See merge request cable-mc/cobblemon!407
2023-03-31 11:22:54 +00:00
Hiroku 817b55407c Merge branch 'main' into 'GDWR/add_spawn_pokemon_from_pool'
# Conflicts:
#   CHANGELOG.md
2023-03-31 11:10:11 +00:00
Hiroku b2167a9902 Merge branch 'misc-fixes' into 'main'
Misc Fixes

See merge request cable-mc/cobblemon!425
2023-03-31 11:09:36 +00:00
TyzillionandHiroku d3f5ce42a8 Misc Fixes 2023-03-31 11:09:36 +00:00
Hiroku 0eababd67a Merge branch 'more-recipes-already' into 'main'
A few recipes

See merge request cable-mc/cobblemon!427
2023-03-31 09:22:30 +00:00
dhi_holoandHiroku 103f65800f A few recipes 2023-03-31 09:22:30 +00:00
Veraxiel 55925fc4a4 Update BestSpawnerConfig.kt 2023-03-31 08:19:47 +00:00
Paul 63d4176e86 Merge remote-tracking branch 'origin/main' 2023-03-31 13:56:08 +10:00
Hiroku 4709d0f766 Merge branch 'showdownInterpreter' into 'main'
add interpretation for yawn and fixed a bug in silent message interpretation

See merge request cable-mc/cobblemon!422
2023-03-31 03:55:05 +00:00
BobbyandHiroku e1ef1d9bb0 add interpretation for yawn and fixed a bug in silent message interpretation 2023-03-31 03:55:04 +00:00
Paul 5745b394b5 Updated all the translations 2023-03-31 13:53:51 +10:00
Paul 335f6f7104 Merge remote-tracking branch 'origin/main' 2023-03-31 11:33:41 +10:00
Hiroku 2de001f61c Merge branch 'mjkim__-main-patch-41166' into 'main'
Updated Korean Translations

See merge request cable-mc/cobblemon!424
2023-03-31 01:33:11 +00:00
KY4LUandHiroku 9739ed917a Updated Korean Translations 2023-03-31 01:33:11 +00:00
Paul e9e699c60b Merge remote-tracking branch 'origin/main' 2023-03-31 11:32:06 +10:00
Paul 4dd78d5755 Update Showdown with a merge from showdown/master and misc data order fix 2023-03-31 11:27:16 +10:00
Hiroku 1e67385f15 Merge branch 'GDWR/patch_friendship_based_attacks' into 'main'
Correctly format misc data in Showdown team packet

Closes #201

See merge request cable-mc/cobblemon!417
2023-03-31 01:16:35 +00:00
Hiroku f29a26869b Merge branch '191-investigate-crash-with-showdown-move-action-interpretation' into 'main'
Migrated HandleMoveInstruction to use BattleMessage

Closes #191

See merge request cable-mc/cobblemon!423
2023-03-31 01:08:01 +00:00
Hiroku aefdbfcce3 Merge branch 'ys/recall-shoulder-pokemon-on-heal-and-evolve' into 'main'
Recall shoulder pokemon on healer use

See merge request cable-mc/cobblemon!419
2023-03-31 00:13:33 +00:00
YaseenandHiroku 05bce3c058 Recall shoulder pokemon on healer use 2023-03-31 00:13:33 +00:00
Renaissance 8ba622a635 Migrated HandleMoveInstruction to use BattleMessage 2023-03-30 01:47:18 +00:00
GDWR 3b1e8f1624 Correctly format misc data in Showdown team packet 2023-03-29 20:04:52 +01:00
Village eae49b0f7e Merge branch 'main' of https://gitlab.com/cable-mc/pokemon-cobbled 2023-03-29 01:00:06 -04:00
Village 9af141e9cd fix lava particles 2023-03-29 00:59:04 -04:00
Hiroku 061a418c90 Merge branch 'showdownInterpreter' into 'main'
add interpretation for -activate -end and -damage instructions for the move wrap

See merge request cable-mc/cobblemon!414
2023-03-29 03:32:32 +00:00
Hiroku bb52d55c80 Merge branch 'feature/add_missing_extractors' into 'main'
feat: amend PokemonPropertyExtractors to match what PokemonProperties can parse from string

See merge request cable-mc/cobblemon!411
2023-03-28 23:56:34 +00:00
PolymetaandHiroku 416affb1c3 feat: amend PokemonPropertyExtractors to match what PokemonProperties can parse from string 2023-03-28 23:56:34 +00:00
RobertLudwick 7061d58333 add interpretation for -activate -end and -damage instructions for the move wrap 2023-03-28 18:41:12 -04:00
Nick b59aab557f Merge branch 'fix-benched-moves-sync' into 'main'
Fix benched moves not syncing correctly on the Pokemon DTO

See merge request cable-mc/cobblemon!413
2023-03-28 20:35:04 +00:00
LiciousandNick 1f98355bec Fix benched moves not syncing correctly on the Pokemon DTO 2023-03-28 20:35:03 +00:00
Licious17 ad6b63055c Add en_us description for all gen 9 moves and abilities. 2023-03-28 19:50:44 +01:00
GDWR 96611b04dc Add SpawnPokemonFromPool command 2023-03-28 19:26:30 +01:00
GDWR 49d1015104 Operate on the party pokemon backwards so the "lead" is kept 2023-03-28 19:25:14 +01:00
GDWR 0eeb992724 If pokebox command would move a Player's last poke while preventCompletePartyDeposit is set, stop and error 2023-03-28 19:25:14 +01:00
GDWR 9e9921098a Add Lang entries for error messages 2023-03-28 19:25:14 +01:00
GDWR 0b23e68786 Add pokebox and pokeboxall commands 2023-03-28 19:25:12 +01:00
Licious 0ce309c37c Merge branch 'thorium-mod-compatibility' into 'main'
Fix incompatibility with the Thorium mod.

See merge request cable-mc/cobblemon!408
2023-03-28 14:23:39 +00:00
Licious 75527fdf40 Fix incompatibility with the Thorium mod. 2023-03-28 14:23:39 +00:00
Licious 2389bdf25d Merge branch 'any_evolution-requirement' into 'main'
Add utility evolution requirement for any match of children requirements

See merge request cable-mc/cobblemon!405
2023-03-28 09:56:34 +00:00
Licious 24c9073c4c Add utility evolution requirement for any match of children requirements 2023-03-28 09:56:34 +00:00
Hiroku b33e2118b4 Merge branch 'showdown-unbundle-tweak' into 'main'
Don't run mkdirs on the zip target, but rather its parent. Fixes an IO exception

See merge request cable-mc/cobblemon!410
2023-03-28 09:01:26 +00:00
Nick 993ca68a5a Don't run mkdirs on the zip target, but rather its parent. Fixes an IO exception 2023-03-27 22:24:05 -07:00
Nick f0d47055c4 Merge branch 'transitive-access-wideners' into 'main'
Use transitive access wideners in favor of side-mod/plugin development

See merge request cable-mc/cobblemon!409
2023-03-28 04:57:49 +00:00
Nick bf331a7471 Use transitive access wideners in favor of side-mod/plugin development 2023-03-28 04:57:48 +00:00
Hiroku f74ba3dc77 Merge branch 'more-poke' into 'main'
Implement Nosepass & Slugma line

Closes #168

See merge request cable-mc/cobblemon!406
2023-03-27 23:49:40 +00:00
LiciousandHiroku 063b627043 Implement Nosepass & Slugma line 2023-03-27 23:49:39 +00:00
Licious17 85335d14ed The great shoulder nerf. 2023-03-27 23:08:16 +01:00
Licious 358b544583 Merge branch 'misc_texture_fixes' into 'main'
Misc Fixes

See merge request cable-mc/cobblemon!403
2023-03-27 19:06:49 +00:00
TyzillionandLicious ac945b4597 Misc Fixes 2023-03-27 19:06:48 +00:00
Paul 8733085232 Fixed all pokemon being lava resistant 2023-03-27 14:33:33 +10:00
Hiroku cbc484a611 Merge branch 'return-before-heal' into 'main'
Return Pokemon to their pokeballs when healing them in a healing machine

See merge request cable-mc/cobblemon!395
2023-03-27 03:35:27 +00:00
chandler05andHiroku e5412a9c3b Return Pokemon to their pokeballs when healing them in a healing machine 2023-03-27 03:35:27 +00:00
Hiroku 3ac325a22e Merge branch '192-disconnecting-from-dedicated-server-causes-pokeballs-to-not-finish-capture-attempt' into 'main'
Fix Pokeballs being able to go through portals & sometimes getting stuck on...

Closes #40 and #192

See merge request cable-mc/cobblemon!400
2023-03-27 03:34:51 +00:00
LiciousandHiroku 6775e30f87 Fix Pokeballs being able to go through portals & sometimes getting stuck on... 2023-03-27 03:34:51 +00:00
Paul 5d30990eb6 Clean duplicate pokemon from data on load 2023-03-27 13:30:38 +10:00
Paul c8917ff02e Fixed best-spawner-config.json being .json.json 2023-03-27 12:00:22 +10:00
Hiroku de151442cd Merge branch 'GDWR/sound_arrow_keys_in_pc' into 'main'
Make GUI click sound when using arrow keys to navigate PC

Closes #198

See merge request cable-mc/cobblemon!402
2023-03-27 00:22:35 +00:00
Paul b09e553557 Added ru_ru.json, applied selfdot's fix for pokemon navigation (they had merge conflicts) 2023-03-27 10:19:07 +10:00
GDWR 11e150e6aa Make GUI click sound when using arrow keys to navigate PC 2023-03-26 19:14:07 +01:00
Licious17 85afe85150 Minor tweak on TeachCommand 2023-03-26 18:29:38 +01:00
Licious 4f73361565 Merge branch 'GDWR/teach_command_bug' into 'main'
Make /teach correctly use player argument

Closes #181

See merge request cable-mc/cobblemon!388
2023-03-26 17:01:46 +00:00
GDWRandLicious 34d2de0319 Make /teach correctly use player argument 2023-03-26 17:01:46 +00:00
Test e8be1c9ccf Merge branch '196-apricorn-leaves-cannot-be-composted' into 'main'
Resolve "Apricorn Leaves cannot be Composted"

Closes #196

See merge request cable-mc/cobblemon!399
2023-03-26 14:50:12 +00:00
LiciousandTest 9c36b9449c Resolve "Apricorn Leaves cannot be Composted" 2023-03-26 14:50:12 +00:00
Licious17 1a7695f844 Fix data for Pokémon who have a gender requirement behind their evolution. 2023-03-26 13:53:16 +01:00
Hiroku 134ebe1c2f Merge branch 'exp_yield_fix' into 'main'
Fix for exp yield 0 Pokémon

Closes #184

See merge request cable-mc/cobblemon!393
2023-03-26 05:04:25 +00:00
MoeBoy76andHiroku 923ef08db4 Fix for exp yield 0 Pokémon 2023-03-26 05:04:25 +00:00
Licious17 c962aceed2 Backport fix to clientside Pokémon locating. 2023-03-25 22:44:51 +00:00
Licious ebeebeeee7 Merge branch 'GDWR/capture_poke_sprite_bug' into 'main'
Update state on client side when capture fails

Closes #188

See merge request cable-mc/cobblemon!391
2023-03-25 22:03:37 +00:00
GDWRandLicious 6d6867fb09 Update state on client side when capture fails 2023-03-25 22:03:37 +00:00
Licious e98084343d Merge branch 'pc-command-fix' into 'main'
Need to throw exception to stop flow

See merge request cable-mc/cobblemon!390
2023-03-25 11:34:37 +00:00
DeltricandLicious 19dad2daa7 Need to throw exception to stop flow 2023-03-25 11:34:36 +00:00
Hiroku f513475267 Merge branch 'spawns-and-drops' into 'main'
Added and fixed spawns and drops.

See merge request cable-mc/cobblemon!389
2023-03-25 11:24:40 +00:00
VeraxielandHiroku 80fbf4a300 Added and fixed spawns and drops. 2023-03-25 11:24:40 +00:00
Paul 6904e302ff Added fr_ca translation, updated ja_jp and removed dead line 2023-03-25 20:09:46 +10:00
Paul faf3858a5e Added th_th translation 2023-03-25 19:52:08 +10:00
Paul 501dd51e29 Merge remote-tracking branch 'origin/main' 2023-03-25 19:50:43 +10:00
Licious 7b23ce055c Merge branch '177-investigate-reload-preventing-moves-from-being-learnt' into 'main'
Resolve "Investigate /reload preventing moves from being learnt"

Closes #177

See merge request cable-mc/cobblemon!385
2023-03-25 09:13:06 +00:00
Licious ec8b9be4f6 Resolve "Investigate /reload preventing moves from being learnt" 2023-03-25 09:13:06 +00:00
Paul e62e08aafb Added ja_jp translation 2023-03-25 17:50:34 +10:00
Paul db6a60d3cb Fixed Pokémon not looking at each other in battle 2023-03-25 14:21:35 +10:00
Paul 94ca3808ed Maybe fixed particle effect issues where they overlap or occur after death 2023-03-25 14:21:00 +10:00
Hiroku 9eec283036 Merge branch 'feature/release_events' into 'main'
feat(api): implement ReleasePokemon pre and post event

See merge request cable-mc/cobblemon!380
2023-03-24 23:05:38 +00:00
PolymetaandHiroku 3a0c801ffd feat(api): implement ReleasePokemon pre and post event 2023-03-24 23:05:38 +00:00
Test ab795a4c6c Merge branch '178-disable-psysical-collision-on-1st-stage-apricorn-buds' into 'main'
Resolve "Disable psysical collision on 1st stage apricorn buds"

Closes #178

See merge request cable-mc/cobblemon!384
2023-03-24 18:49:54 +00:00
LiciousandTest 0db41c1f9b Resolve "Disable psysical collision on 1st stage apricorn buds" 2023-03-24 18:49:54 +00:00
Hiroku 2d315920f8 Merge branch 'icon-fix' into 'main'
Fixed Advancement Branch Icon

See merge request cable-mc/cobblemon!374
2023-03-24 06:56:39 +00:00
dhi_holoandHiroku 7449bc2377 Fixed Advancement Branch Icon 2023-03-24 06:56:39 +00:00
Paul 15065e621f Protect pokeballs from causing duplicate Pokemon 2023-03-24 16:22:19 +10:00
Hiroku 59fdd41c46 Merge branch 'Added-ShowdownInterpreter-Instructions' into 'main'
Added ShowdownInterpreter Instructions

Closes #179 and #165

See merge request cable-mc/cobblemon!383
2023-03-24 06:08:01 +00:00
RenaissanceandHiroku fa04e168ac Added ShowdownInterpreter Instructions 2023-03-24 06:08:00 +00:00
Test 94b28c025a Merge branch '176-prevent-pokemon-entity-travel-between-dimensions' into 'main'
Resolve "Prevent Pokémon entity travel between dimensions"

Closes #176

See merge request cable-mc/cobblemon!382
2023-03-23 22:13:10 +00:00
LiciousandTest 6ed4938028 Resolve "Prevent Pokémon entity travel between dimensions" 2023-03-23 22:13:10 +00:00
Licious 99d03b16b2 Merge branch 'teach-command-expansion' into 'main'
Expand teach command functionality

See merge request cable-mc/cobblemon!381
2023-03-22 20:31:57 +00:00
Licious f70f7fe354 Expand teach command functionality 2023-03-22 20:31:56 +00:00
Hiroku 878c31fac7 Merge branch 'held-item-full-inv-fix' into 'main'
Held item give/remove fixes

See merge request cable-mc/cobblemon!379
2023-03-22 11:03:57 +00:00
LiciousandHiroku 9945143dd9 Held item give/remove fixes 2023-03-22 11:03:56 +00:00
Licious17 770d248b3a Merge remote-tracking branch 'origin/main' 2023-03-21 23:07:03 +00:00
Licious17 345dd60e11 Fix lack of healing message with the black sludge 2023-03-21 23:06:53 +00:00
Licious 7b48ddf418 Merge branch 'Fixing-various-shinies' into 'main'
Fixed various shinies and a couple misc. bugs

See merge request cable-mc/cobblemon!377
2023-03-21 22:12:50 +00:00
JMMCPandLicious e8ea697caa Fixed various shinies and a couple misc. bugs 2023-03-21 22:12:50 +00:00
Licious 33021e2cb8 Merge branch 'json-bug-fixes' into 'main'
Fixes wrongly-formatted JSON files

See merge request cable-mc/cobblemon!378
2023-03-21 20:48:28 +00:00
MelonSpeedrunsandLicious d577687a87 Fixes wrongly-formatted JSON files 2023-03-21 20:48:28 +00:00
Hiroku e603238339 Merge branch 'misc-fixes' into 'main'
Misc fixes & additions

See merge request cable-mc/cobblemon!376
2023-03-20 21:32:57 +00:00
LiciousandHiroku a62f1fe560 Misc fixes & additions 2023-03-20 21:32:57 +00:00
Paul 96dd007db9 Merge remote-tracking branch 'origin/main' 2023-03-19 21:18:49 +10:00
Paul c339e36f9a Fixed custom Pokémon not being usable in battle by properly fixing ground type moves hitting flying types etc 2023-03-19 21:17:56 +10:00
Hiroku 2798487106 Merge branch 'modify-global-influences' into 'main'
Updated Global Influences

See merge request cable-mc/cobblemon!375
2023-03-19 11:05:41 +00:00
DeltricandHiroku 252139d07f Updated Global Influences 2023-03-19 11:05:40 +00:00
Paul e55c54fb24 Fixed log spam when rendering pokemon model items 2023-03-18 17:25:42 +10:00
Paul 74c4e2ae04 Fixed a crash when killing Dodrio 2023-03-18 14:10:59 +10:00
1317 changed files with 56148 additions and 14815 deletions
+94
View File
@@ -1,4 +1,97 @@
# Changelog
## [1.3.1 (March 31st, 2023)](#1-3-1)
### Additions
- Added Slugma, Magcargo, Nosepass, and Probopass.
- Elgyem family now drops Chorus Fruit, Geodude family now drops Black Augurite.
- Added missing spawn files for Golett and Bergmite family.
- Apricorns can now be smelted into dyes.
- Added animations to Staryu line and Porygon line.
- Added faint animations to Klink line.
- Add lava surface spawn preset.
- Added an ``any`` evolution requirement allowing you to define ``possibilities`` of other evolution requirements, for example, this allows you to create an evolution that requires the Pokémon to be shiny or a female.
- Added the `/spawnpokemonfrompool [amount]` or `/forcespawn [amount]` command to spawn Pokémon(s) in the surrounding area using the natural spawn rates/pool of that area, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.spawnpokemon` if a permission mod is present. On a successful execution of the command, the amount of Pokémon spawned will be the output.
- Added the `/pokebox` and `/pokeboxall` commands to move Pokemon(s) to the PC from a Player's party, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.pokebox` if a permission mod is present. On a successful execution of the command the output will be the number of pokemon moved to the Player's PC.
- Added the `/pc` command which opens up the PC UI the same way interacting with the block would, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.pc` if a permission mod is present.
### Changes
- You can now click the portraits of other Pokémon in the starter selection screen to navigate directly to them.
- You can now click the right and left arrow keys to navigate PC boxes.
- Link Cables will now require Pokémon to hold any held item normally required for their evolution.
- After a battle, the last Pokémon used now becomes the selected one in your party.
- The `/teach` command can now only allow the Pokémon to be given moves in their learnset, this can be controlled with the permission `cobblemon.command.teach.bypass`, to account for that change the base command now requires the permission `cobblemon.command.teach.base`, this change is meant only for people using a mod capable of providing permissions such as [LuckPerms](https://luckperms.net/).
- Apricorns will no longer collide with their block form when picked, this should improve the experience in automatic farms.
- Increased spawn chances for many Pokémon requiring specific blocks to be nearby.
- Put Cryogonal in more snowy biomes.
- Ditto as well as the Eevee, Gible, and Riolu families have been made more common.
- Lowered spawn rate of Gyarados on the surface of water.
- Apricorn leaves can now be used in the [Composter](https://minecraft.fandom.com/wiki/Composter) block, these have the same chance to raise the compost pile the Minecraft leaves do.
- Updated Gengar's model and texture.
- Updated Swinub line model and animations.
- Tweaked portrait frames for the Pidgey line and for Walking Wake.
- Changed all buff shoulder effects to only give a level 1 buff instead of level 2.
- Made Weavile a little bigger.
- Changed the recipes for Mystic Water, Miracle Seed, and Charcoal Stick to utilise the evolution stones, as well as Never-Melt Ice having an alternate recipe using the Ice Stone.
- Replaced the `Failed to handle` battle messages to `Missing interpretation` to make it more clear that mechanics do work just still pending dedicated messages.
### Fixes
- Fixed killing a Dodrio killing your game. Dodrio will never look the same to us.
- Fixed non-Fire-type Pokémon being immune to lava.
- Fixed custom Pokémon not being usable in battle, properly. A last minute fix caused this to break again; what are these devs not paid for?
- Fixed being locked in an endless healing queue if you broke the healing machine during use.
- Fixed an issue with the experience calculation when the Exp. Share is held.
- Fixed Friendship-based attacks not using friendship values from your Pokémon.
- Fixed Link Cables consuming held items they shouldn't due to not validating the held item of a Pokémon.
- Fixed a crash when Aromatherapy cured the status of party members.
- Fixed moves learnt on evolution not being given when said evolution happens. If you were affected by this issue your existing Pokémon will now be able to relearn those moves.
- Fixed console spam when rendering Pokémon model items.
- Fixed battle messages for 50+ moves and abilities and items.
- Fixed the possible duplicate when capturing Pokémon (probably, this one's hard to reproduce to confirm it's fixed).
- Previously duplicated Pokémon are cleaned from PCs and parties on restart.
- Fixed an issue with some particle effects applying after a Pokémon has died or on top of the wrong Pokémon when using specific mods.
- Fixed Pokémon not looking at each other in battle.
- Fixed Experience Candy and Experience Share attempting to bring Pokémon above level cap causing crashes.
- Fixed level 100 Pokémon having experience go over the cap total amount they should have.
- Fixed `/pokemonspawnat` having the argument positions reverted making it impossible for Brigadier to understand when to suggest coordinates. It is now the intended `/spawnpokemonat <pos> <properties>`.
- Fixed performance issues with shouldered Pokémon in certain systems.
- Fixed learnset issues for Pokémon whose only modern debut was LGPE/BDSP/LA.
- Fixed shiny Zubat, Grimer, Omanyte, Elgyem, Delphox and Aegislash displaying their normal texture.
- Fixed sleeping in beds allowing fainted Pokémon to receive experience after a battle ends somehow.
- Fixed an issue where a Pokémon will claim to have learnt a new move they already have in their moveset when learnt at an earlier level in their previous evolution. I realize that's confusing.
- Fixed Dispensers not being able to shear Wooloo. This will also extend to other mods that check if an entity is valid to shear.
- Fixed the currently held item of your Pokémon not dropping to the ground when removing it if your inventory was full.
- Fixed creative mode allowing you to make your Pokémon hold more than 1 of the same item.
- Fixed a Pokémon duplication glitch when teleporting between worlds.
- Fixed dedicated servers being able to reload Cobblemon data with the vanilla `/reload` command causing unintended behavior for clients.
- Fixed underground Pokémon spawning above ground.
- Fixed Pokémon portrait not reverting back to the Pokémon after a failed capture during battle.
- Fixed edge texture artifacts on pane elements for Tentacool and Tentacruel models.
- Fixed crash caused by Pokémon pathing
- Fixed Pokémon not returning to their balls when being healed in a healing machine
- Fixed all Gen IX Pokémon as well as forms added in PLA and Wyrdeer, Kleavor, Ursaluna, Basculegion, Sneasler, Overqwil, and Enamorus having 0 exp yields.
- Fixed Irons Leaves having bluetooth back legs. If you saw it, you know what I mean.
- Fixed Golurk not having shoulder plates on its shoulders.
- Fixed some water Pokémon walking onto land from the water even though they are fish.
- Fixed Porygon2 and PorygonZ being too small.
- Fixed Snivy line head look animation.
- Fixed Staryu line not being able to swim.
- Fixed an incompatibility with [Thorium](https://modrinth.com/mod/thorium) patch for [MC-84873](https://bugs.mojang.com/browse/MC-84873).
- Fixed Pidgeotto wings when walking.
- Fixed Delphox walk animation.
- Fixed Froakie line sleep animations in battle.
- Fixed Pokémon missing the non-level up moves they could relearn when rejoining a world until a new move was added to their relearn list.
- Fixed instantly fleeing from Pokémon set to be unfleeable.
- Fixed Pumpkaboo line forms not working. (Currently sizes aren't visual but check base stats to see which size you have.)
- Fixed a bug that caused already interpreted messages for moves to be mistaken as uninterpreted.
### Developer
- Add events that are fired just before and after a Pokémon is released (ReleasePokemonEvent.Pre and .Post)
### Localization
- Added complete translations for Japanese, Thai, and Canadian French.
- Added partial translations for Russian, Ukrainian, Mexican Spanish, and Korean.
- Updated every existing language's translation.
- All the translators that contributed are amazing.
## [1.3.0 - The Foundation Update (March 17th, 2023)](#1-3-0)
@@ -135,6 +228,7 @@
- Fixed ambient sound file path for Porygon2.
- Fixed forms not being able to unset the secondary type of a Pokémon in the stat JSON.
- Fixed moves that haven't carried over from generation 8 onwards having the description they did in the generation 8 games instead of their last valid one.
- Fixed shoulder mounted pokemon not returning to party on healer use and on evolution
### Developer
- Reworked CatchRateModifier, as such, existing implementations need to be updated.
@@ -106,6 +106,7 @@ import com.cobblemon.mod.common.world.placement.CobblemonPlacementTypes
import dev.architectury.event.EventResult
import dev.architectury.event.events.common.CommandRegistrationEvent
import dev.architectury.event.events.common.InteractionEvent
import dev.architectury.event.events.common.PlayerEvent
import dev.architectury.hooks.item.tool.AxeItemHooks
import java.io.File
import java.io.FileReader
@@ -127,7 +128,7 @@ import org.apache.logging.log4j.LogManager
object Cobblemon {
const val MODID = "cobblemon"
const val VERSION = "1.3.0"
const val VERSION = "1.3.1"
const val CONFIG_PATH = "config/$MODID/main.json"
val LOGGER = LogManager.getLogger()
@@ -212,6 +213,11 @@ object Cobblemon {
}
return@RightClickBlock EventResult.pass()
})
PlayerEvent.CHANGE_DIMENSION.register(PlayerEvent.ChangeDimension{ player, _, _ ->
player.party().forEach { pokemon -> pokemon.entity?.recallWithAnimation() }
})
TrackedDataHandlerRegistry.register(Vec3DataSerializer)
TrackedDataHandlerRegistry.register(StringSetDataSerializer)
TrackedDataHandlerRegistry.register(PoseTypeDataSerializer)
@@ -312,8 +318,6 @@ object Cobblemon {
}
PokemonSpecies.observable.subscribe {
ShowdownService.get().closeConnection()
ShowdownService.get().openConnection()
LOGGER.info("Starting dummy Showdown battle to force it to pre-load data.")
battleRegistry.startBattle(
BattleFormat.GEN_9_SINGLES,
@@ -35,5 +35,8 @@ object CobblemonCommands {
FriendshipCommand.register(dispatcher)
GiveAllPokemon.register(dispatcher)
HeldItemCommand.register(dispatcher, registry)
PcCommand.register(dispatcher)
SpawnPokemonFromPool.register(dispatcher)
PokeboxCommand.register(dispatcher)
}
}
@@ -20,14 +20,22 @@ import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.registry.CompletableRegistry
import dev.architectury.registry.registries.RegistrySupplier
import net.minecraft.block.Block
import net.minecraft.item.AliasedBlockItem
import net.minecraft.item.BlockItem
import net.minecraft.item.Item
import net.minecraft.item.ItemGroup
import net.minecraft.block.ComposterBlock
import net.minecraft.item.*
import net.minecraft.util.registry.Registry
object CobblemonItems : CompletableRegistry<Item>(Registry.ITEM_KEY) {
init {
this.completed.thenRun {
val compostChance = ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE.getFloat(Items.OAK_LEAVES)
// Should always pass unless Mojang reworks leaves to no longer work in the Composter, in that case we already updated w.o doing anything
if (compostChance != ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE.defaultReturnValue()) {
ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE[APRICORN_LEAVES.get()] = compostChance
}
}
}
private val pokeballs = mutableListOf<RegistrySupplier<PokeBallItem>>()
@JvmField
@@ -28,10 +28,8 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleSetTeamPokemonP
import com.cobblemon.mod.common.net.messages.client.battle.BattleSwitchPokemonPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleUpdateTeamPokemonPacket
import com.cobblemon.mod.common.net.messages.client.battle.ChallengeNotificationPacket
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.*
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.effect.SpawnSnowstormParticlePacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.*
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.AddEvolutionPacket
@@ -189,6 +187,7 @@ object CobblemonNetwork {
buildClientMessage<ServerSettingsPacket>()
// Data registries
buildClientMessage<UnlockReloadPacket>()
buildClientMessage<AbilityRegistrySyncPacket>()
buildClientMessage<MovesRegistrySyncPacket>()
buildClientMessage<SpeciesRegistrySyncPacket>()
@@ -124,7 +124,7 @@ class BattleMessage(rawMessage: String) {
* @return A pair of [BattleActor] and [ActiveBattlePokemon] if the argument exists and successfully parses them otherwise null.
*/
fun actorAndActivePokemon(index: Int, battle: PokemonBattle): Pair<BattleActor, ActiveBattlePokemon>? {
val pnx = this.argumentAt(index)?.substring(0, 3) ?: return null
val pnx = this.argumentAt(index)?.takeIf { it.length >= 3 }?.substring(0, 3) ?: return null
return this.actorAndActivePokemon(pnx, battle)
}
@@ -25,6 +25,7 @@ import com.cobblemon.mod.common.battles.BattleCaptureAction
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
import com.cobblemon.mod.common.battles.dispatch.BattleDispatch
import com.cobblemon.mod.common.battles.dispatch.DispatchResult
import com.cobblemon.mod.common.battles.dispatch.GO
@@ -34,6 +35,8 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleEndPacket
import com.cobblemon.mod.common.pokemon.evolution.progress.DefeatEvolutionProgress
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.party
import net.minecraft.server.network.ServerPlayerEntity
import java.util.UUID
import java.util.concurrent.ConcurrentLinkedQueue
import net.minecraft.text.Text
@@ -191,7 +194,7 @@ open class PokemonBattle(
}
val experience = Cobblemon.experienceCalculator.calculate(opponentPokemon, faintedPokemon, multiplier)
if (experience > 0) {
opponent.awardExperience(opponentPokemon, (experience * Cobblemon.config.experienceMultiplier).toInt())
opponent.awardExperience(opponentPokemon, experience)
}
Cobblemon.evYieldCalculator.calculate(opponentPokemon).forEach { (stat, amount) ->
pokemon.evs.add(stat, amount)
@@ -258,7 +261,7 @@ open class PokemonBattle(
}
}
fun dispatchWaiting(dispatcher: () -> Unit, delaySeconds: Float = 1F) {
fun dispatchWaiting(delaySeconds: Float = 1F, dispatcher: () -> Unit) {
dispatch {
dispatcher()
WaitDispatch(delaySeconds)
@@ -306,15 +309,18 @@ open class PokemonBattle(
.filterIsInstance<FleeableBattleActor>()
.filter { it.getWorldAndPosition() != null }
.none { pokemonActor ->
val (world, pos) = pokemonActor.getWorldAndPosition()!!
val nearestPlayerActorDistance = actors.asSequence()
.filter { it.type == ActorType.PLAYER }
.filterIsInstance<EntityBackedBattleActor<*>>()
.mapNotNull { it.entity }
.filter { it.world == world }
.minOfOrNull { pos.distanceTo(it.pos) }
if (pokemonActor.fleeDistance == -1F) true
else {
val (world, pos) = pokemonActor.getWorldAndPosition()!!
val nearestPlayerActorDistance = actors.asSequence()
.filter { it.type == ActorType.PLAYER }
.filterIsInstance<EntityBackedBattleActor<*>>()
.mapNotNull { it.entity }
.filter { it.world == world }
.minOfOrNull { pos.distanceTo(it.pos) }
nearestPlayerActorDistance != null && nearestPlayerActorDistance < pokemonActor.fleeDistance
nearestPlayerActorDistance != null && nearestPlayerActorDistance < pokemonActor.fleeDistance
}
}
if (wildPokemonOutOfRange) {
@@ -345,8 +351,8 @@ open class PokemonBattle(
* @return The generated [Text] meant to notify the client.
*/
internal fun createUnimplemented(message: BattleMessage): Text {
LOGGER.error("Failed to handle '{}' action {}", message.id, message.rawMessage)
return Text.literal("Failed to handle '${message.id}' action ${message.rawMessage}").red()
LOGGER.error("Missing interpretation on '{}' action {}", message.id, message.rawMessage)
return Text.literal("Missing interpretation on '${message.id}' action ${message.rawMessage}").red()
}
/**
@@ -363,8 +369,8 @@ open class PokemonBattle(
if (publicMessage.id != privateMessage.id) {
throw IllegalArgumentException("Messages do not match")
}
LOGGER.error("Failed to handle '{}' action: \nPublic » {}\nPrivate » {}", publicMessage.id, publicMessage.rawMessage, privateMessage.rawMessage)
return Text.literal("Failed to handle '${publicMessage.id}' action please report to the developers").red()
LOGGER.error("Missing interpretation on '{}' action: \nPublic » {}\nPrivate » {}", publicMessage.id, publicMessage.rawMessage, privateMessage.rawMessage)
return Text.literal("Missing interpretation on '${publicMessage.id}' action please report to the developers").red()
}
}
@@ -29,6 +29,7 @@ import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEv
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionDisplayEvent
import com.cobblemon.mod.common.api.events.pokemon.interaction.ExperienceCandyUseEvent
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
import com.cobblemon.mod.common.api.reactive.CancelableObservable
import com.cobblemon.mod.common.api.reactive.EventObservable
import com.cobblemon.mod.common.api.reactive.SimpleObservable
@@ -92,6 +93,11 @@ object CobblemonEvents {
@JvmField
val EXPERIENCE_CANDY_USE_POST = EventObservable<ExperienceCandyUseEvent.Post>()
@JvmField
val POKEMON_RELEASED_EVENT_PRE = CancelableObservable<ReleasePokemonEvent.Pre>()
@JvmField
val POKEMON_RELEASED_EVENT_POST = EventObservable<ReleasePokemonEvent.Post>()
@JvmField
val LOOT_DROPPED = CancelableObservable<LootDroppedEvent>()
@JvmField
@@ -0,0 +1,61 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.storage
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.storage.PokemonStore
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.network.ServerPlayerEntity
/**
* The base of the events around the release of Pokémon from pc or party.
* For the generic experience gain event see [CobblemonEvents.POKEMON_RELEASED_EVENT_PRE] and [CobblemonEvents.POKEMON_RELEASED_EVENT_POST]
*
* @author Polymeta
* @since March 22nd, 2023
*/
interface ReleasePokemonEvent {
/**
* The [ServerPlayerEntity] that is releasing the Pokémon.
*/
val player: ServerPlayerEntity
/**
* The [Pokemon] being released.
*/
val pokemon: Pokemon
/**
* The [PokemonStore] from which the Pokémon is being released from.
*/
val storage: PokemonStore<*>
/**
* Fired when a player attempts to release a Pokémon from their pc or party.
* Canceling this event will prevent the Pokémon from being released.
* Users of this event are advised to send the player a message to let them know that they can't release that Pokémon and why.
* For the event that is fired after all the calculations took place see [ReleasePokemonEvent.Post].
*/
class Pre(
override val player: ServerPlayerEntity,
override val pokemon: Pokemon,
override val storage: PokemonStore<*>
) : ReleasePokemonEvent, Cancelable()
/**
* Fired after a player released a Pokémon from their pc or party.
*/
class Post(
override val player: ServerPlayerEntity,
override val pokemon: Pokemon,
override val storage: PokemonStore<*>
) : ReleasePokemonEvent
}
@@ -22,6 +22,7 @@ import net.minecraft.text.MutableText
* To build a Move you need to use its template
*
* @param name The name in Showdown ID form.
* @param num The numerical ID of this move on Showdown.
* @param elementalType The [ElementalType] of this move.
* @param damageCategory The [DamageCategory] of this move.
* @param power The base power of this move.
@@ -34,6 +35,7 @@ import net.minecraft.text.MutableText
*/
open class MoveTemplate(
val name: String,
val num: Int,
@SerializedName("type")
val elementalType: ElementalType,
val damageCategory: DamageCategory,
@@ -45,8 +47,7 @@ open class MoveTemplate(
val critRatio: Double,
val effectChances: Array<Double>
) {
/** Don't change this from 0. It was -1, but then the data JSON reader was making this 0 instead of -1, ignoring our original video. */
var id = 0
val displayName: MutableText
get() = lang("move.$name")
val description: MutableText
@@ -55,6 +56,7 @@ open class MoveTemplate(
get() = 8 * pp / 5
class Dummy(name: String) : MoveTemplate(
name = name,
num = -1,
elementalType = ElementalTypes.NORMAL,
damageCategory = DamageCategories.STATUS,
power = 0.0,
@@ -43,6 +43,7 @@ object Moves : DataRegistry {
val jsMove = movesJson[i].asJsonObject
val id = jsMove.get("id").asString
try {
val num = jsMove.get("num").asInt
val elementalType = ElementalTypes.getOrException(jsMove.get("type").asString)
val damageCategory = DamageCategories.getOrException(jsMove.get("category").asString)
val power = jsMove.get("basePower").asDouble
@@ -71,13 +72,12 @@ object Moves : DataRegistry {
effectChances += secondaryMember.get("chance").asDouble
}
}
val move = MoveTemplate(id, elementalType, damageCategory, power, target, accuracy, pp, priority, critRatio, effectChances.toTypedArray())
val move = MoveTemplate(id, num, elementalType, damageCategory, power, target, accuracy, pp, priority, critRatio, effectChances.toTypedArray())
this.register(move)
} catch (e: Exception) {
Cobblemon.LOGGER.error("Caught exception trying to resolve the move '{}'", id, e)
}
}
this.applyIDs()
Cobblemon.LOGGER.info("Loaded {} moves", this.allMoves.size)
this.observable.emit(this)
}
@@ -87,7 +87,7 @@ object Moves : DataRegistry {
}
fun getByName(name: String) = allMoves[name.lowercase()]
fun getByNumericalId(id: Int) = idMapping[id]!!
fun getByNumericalId(id: Int) = idMapping[id]
fun getByNameOrDummy(name: String) = allMoves[name.lowercase()] ?: MoveTemplate.dummy(name.lowercase())
fun getExceptional() = getByName("tackle") ?: allMoves.values.random()
fun count() = allMoves.size
@@ -95,24 +95,12 @@ object Moves : DataRegistry {
fun all() = this.allMoves.values.toList()
internal fun receiveSyncPacket(moves: Collection<MoveTemplate>) {
moves.forEach { move ->
this.register(move)
this.idMapping[move.id] = move
}
moves.forEach(this::register)
}
private fun register(move: MoveTemplate) {
this.allMoves[move.name] = move
}
private fun applyIDs() {
var id = 1
this.allMoves.values
.sortedBy { it.name }
.forEach {
it.id = id++
idMapping[it.id] = it
}
this.idMapping[move.num] = move
}
}
@@ -46,12 +46,17 @@ object CobblemonPermissions {
val TAKE_POKEMON = this.create("${COMMAND_PREFIX}takepokemon", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val TEACH = this.create("${COMMAND_PREFIX}teach", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val TEACH = this.create("${COMMAND_PREFIX}teach.base", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val TEACH_BYPASS_LEARNSET = this.create("${COMMAND_PREFIX}teach.bypass", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val FRIENDSHIP = this.create("${COMMAND_PREFIX}friendship", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val HELD_ITEM = this.create("${COMMAND_PREFIX}helditem", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val PC = this.create("${COMMAND_PREFIX}pc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val POKEBOX = this.create("${COMMAND_PREFIX}pokebox", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
fun all(): Iterable<Permission> = this.permissions
private fun create(node: String, level: PermissionLevel): Permission {
@@ -21,11 +21,17 @@ import com.cobblemon.mod.common.pokemon.Pokemon
fun interface PokemonPropertyExtractor {
companion object {
val SPECIES = PokemonPropertyExtractor { pokemon, properties -> properties.species = pokemon.species.resourceIdentifier.toString() }
val FORM = PokemonPropertyExtractor { pokemon, properties -> properties.form = pokemon.form.formOnlyShowdownId() }
val SHINY = PokemonPropertyExtractor { pokemon, properties -> properties.shiny = pokemon.shiny }
val ASPECTS = PokemonPropertyExtractor { pokemon, properties -> properties.aspects = pokemon.aspects }
val LEVEL = PokemonPropertyExtractor { pokemon, properties -> properties.level = pokemon.level }
val GENDER = PokemonPropertyExtractor { pokemon, properties -> properties.gender = pokemon.gender }
val FRIENDSHIP = PokemonPropertyExtractor { pokemon, properties -> properties.friendship = pokemon.friendship }
val POKEBALL = PokemonPropertyExtractor { pokemon, properties -> properties.pokeball = pokemon.caughtBall.name.toString() }
val NATURE = PokemonPropertyExtractor { pokemon, properties -> properties.nature = pokemon.nature.name.toString() }
val ABILITY = PokemonPropertyExtractor { pokemon, properties -> properties.ability = pokemon.ability.name }
val ALL = arrayOf(SPECIES, FORM, SHINY, ASPECTS, LEVEL, GENDER, FRIENDSHIP, POKEBALL, NATURE, ABILITY)
}
operator fun invoke(pokemon: Pokemon, properties: PokemonProperties)
@@ -112,6 +112,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
init {
SpeciesAdditions.observable.subscribe {
this.species.forEach(Species::initialize)
this.species.forEach(Species::resolveEvolutionMoves)
Cobblemon.showdownThread.queue { ShowdownService.get().registerSpecies() }
// Reload this with the mod
CobblemonEmptyHeldItemManager.load()
@@ -204,7 +205,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
"H" to "No Ability",
"S" to "No Ability"
)
val types = (form?.types ?: species.types).map { it.name }
val types = (form?.types ?: species.types).map { it.name.replaceFirstChar(Char::uppercase) }
val preevo: String? = (form?.preEvolution ?: species.preEvolution)?.let { if (it.form == it.species.standardForm) createShowdownName(it.species) else "${createShowdownName(it.species)}-${it.form.name}" }
// For the context of battles the content here doesn't matter whatsoever and due to how PokemonProperties work we can't guarantee an actual specific species is defined.
val evos = if ((form?.evolutions ?: species.evolutions).isEmpty()) emptyList() else arrayListOf("")
@@ -12,13 +12,16 @@ import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.abilities.AbilityPool
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEvent
import com.cobblemon.mod.common.api.moves.BenchedMove
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
import com.cobblemon.mod.common.util.lang
import net.minecraft.item.ItemStack
import net.minecraft.sound.SoundCategory
@@ -91,8 +94,20 @@ interface Evolution : EvolutionLike {
* @param pokemon The [Pokemon] being evolved.
*/
fun forceEvolve(pokemon: Pokemon) {
if (pokemon.state is ShoulderedState) {
pokemon.tryRecallWithAnimation()
}
// ToDo Once implemented queue evolution for a pokemon state that is not in battle, start animation instead of instantly doing all of this
this.result.apply(pokemon)
this.learnableMoves.forEach { move ->
if (pokemon.moveSet.hasSpace()) {
pokemon.moveSet.add(move.create())
}
else {
pokemon.benchedMoves.add(BenchedMove(move, 0))
}
pokemon.getOwnerPlayer()?.sendMessage(lang("experience.learned_move", pokemon.species.translatedName, move.displayName))
}
// we want to instantly tick for example you might only evolve your Bulbasaur at level 34 so Venusaur should be immediately available
pokemon.evolutions.filterIsInstance<PassiveEvolution>()
.forEach { evolution ->
@@ -9,10 +9,11 @@
package com.cobblemon.mod.common.api.pokemon.experience
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.pokemon.evolution.requirements.LevelRequirement
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import kotlin.math.pow
import kotlin.math.roundToInt
fun interface ExperienceCalculator {
fun calculate(battlePokemon: BattlePokemon, opponentPokemon: BattlePokemon, participationMultiplier: Double): Int
@@ -20,22 +21,25 @@ fun interface ExperienceCalculator {
object StandardExperienceCalculator : ExperienceCalculator {
override fun calculate(battlePokemon: BattlePokemon, opponentPokemon: BattlePokemon, participationMultiplier: Double): Int {
val trainerMultiplier = if (opponentPokemon.effectedPokemon.isWild()) 1.0 else 1.5 /* Not used in modern formulas */
val baseExp = opponentPokemon.originalPokemon.form.baseExperienceYield
val opponentLevel = opponentPokemon.effectedPokemon.level
val term1 = (baseExp * opponentLevel) / 5.0
// This is meant to be a division but this is due to the intended behavior of handling the 2.0 sent over from Exp. All in modern Pokémon
val term2 = 1 * participationMultiplier
val victorPokemon = battlePokemon.effectedPokemon
val victorLevel = victorPokemon.level
val term3 = (((2.0 * opponentLevel) + 10) / (opponentLevel + victorLevel + 10)).pow(2.5)
// ToDo when OT is implemented 1.5 if traded with someone from the same locale, 1.7 otherwise, 1.0 if OT
val nonOtBonus = 1.0
val luckyEggMultiplier = if (battlePokemon.effectedPokemon.heldItemNoCopy().isIn(CobblemonItemTags.LUCKY_EGG)) Cobblemon.config.luckyEggMultiplier else 1.0
val evolutionMultiplier = if (battlePokemon.effectedPokemon.evolutionProxy.server().any { evolution ->
val requirements = evolution.requirements.asSequence()
requirements.any { it is LevelRequirement } && requirements.all { it.check(battlePokemon.effectedPokemon) }
}) 1.2 else 1.0
val affectionMultiplier = if (battlePokemon.effectedPokemon.friendship >= 220) 1.2 else 1.0
val level = opponentPokemon.originalPokemon.level
val levelVictor = battlePokemon.originalPokemon.level
val pointPowerMultiplier = 1.0
val originalTrainerMultiplier = 1.0
val evolutionMultiplier =
if (battlePokemon.effectedPokemon.evolutionProxy.server().any { evolution ->
val requirements = evolution.requirements.asSequence()
requirements.any { it is LevelRequirement } && requirements.all { it.check(battlePokemon.effectedPokemon) }
}) 1.2 else 1.0
val term1 = (baseExp * level * affectionMultiplier * evolutionMultiplier) / (5 * participationMultiplier)
val term2 = ((2.0 * level + 10) / (level + levelVictor + 10)).pow(2.5)
val term3 = originalTrainerMultiplier * luckyEggMultiplier * pointPowerMultiplier
return (term1 * term2 * term3).toInt()
// that's us!
val gimmickBoost = Cobblemon.config.experienceMultiplier
val term4 = term1 * term2 * term3 + 1
return (term4 * nonOtBonus * luckyEggMultiplier * evolutionMultiplier * affectionMultiplier * gimmickBoost).roundToInt()
}
}
@@ -79,6 +79,11 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
val eggMoves = mutableListOf<MoveTemplate>()
val tutorMoves = mutableListOf<MoveTemplate>()
val tmMoves = mutableListOf<MoveTemplate>()
/**
* Moves the species/form will have learnt when evolving into itself.
* These are dynamically resolved each boot.
*/
val evolutionMoves = mutableSetOf<MoveTemplate>()
val formChangeMoves = mutableListOf<MoveTemplate>()
fun getLevelUpMovesUpTo(level: Int) = levelUpMoves
@@ -97,8 +102,7 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
val level = buffer.readSizedInt(IntSize.U_SHORT)
val moves = mutableListOf<MoveTemplate>()
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
val move = Moves.getByNumericalId(buffer.readSizedInt(IntSize.U_SHORT))
moves.add(move)
Moves.getByNumericalId(buffer.readInt())?.let(moves::add)
}
levelUpMoves[level] = moves
}
@@ -110,7 +114,7 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
buffer.writeSizedInt(IntSize.U_SHORT, level)
buffer.writeSizedInt(IntSize.U_BYTE, moves.size)
for (move in moves) {
buffer.writeSizedInt(IntSize.U_SHORT, move.id)
buffer.writeInt(move.num)
}
}
}
@@ -46,8 +46,9 @@ open class SpawnerManager {
open fun onServerTick() {
// Disables spawning
if (!Cobblemon.config.enableSpawning) {
return;
return
}
influences.removeIf { it.isExpired() }
getSpawnersOfType<TickingSpawner>().forEach(TickingSpawner::tick)
}
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.spawning.influence
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
@@ -32,4 +33,6 @@ interface SpawningInfluence {
fun affectAction(action: SpawnAction<*>) {}
/** Applies some influence over the entity that's been spawned. */
fun affectSpawn(entity: Entity) {}
/** Applies some influence over the weight of spawn buckets. */
fun affectBucketWeight(bucket: SpawnBucket, weight: Float): Float = weight
}
@@ -37,10 +37,10 @@ class BestSpawnerConfig {
"surface" to 0.01F
)
val buckets = mutableListOf(
SpawnBucket("common", 94.4F),
SpawnBucket("common", 93.8F),
SpawnBucket("uncommon", 5F),
SpawnBucket("rare", 0.5F),
SpawnBucket("ultra-rare", 0.1F)
SpawnBucket("rare", 1.0F),
SpawnBucket("ultra-rare", 0.2F)
)
companion object {
@@ -51,7 +51,7 @@ class BestSpawnerConfig {
.disableHtmlEscaping()
.create()
const val CONFIG_NAME = "best-spawner-config.json"
const val CONFIG_NAME = "best-spawner-config"
fun load(): BestSpawnerConfig {
val internal = loadInternal()
@@ -74,7 +74,7 @@ class BestSpawnerConfig {
}
private fun loadInternal(): BestSpawnerConfig {
val reader = InputStreamReader(Cobblemon::class.java.getResourceAsStream("/assets/${Cobblemon.MODID}/spawning/$CONFIG_NAME")!!)
val reader = InputStreamReader(Cobblemon::class.java.getResourceAsStream("/assets/${Cobblemon.MODID}/spawning/$CONFIG_NAME.json")!!)
val config = GSON.fromJson(reader, BestSpawnerConfig::class.java)
reader.close()
return config
@@ -99,7 +99,7 @@ class BestSpawnerConfig {
}
fun saveExternal() {
val stream = Cobblemon::class.java.getResourceAsStream("/assets/${Cobblemon.MODID}/spawning/$CONFIG_NAME")!!
val stream = Cobblemon::class.java.getResourceAsStream("/assets/${Cobblemon.MODID}/spawning/$CONFIG_NAME.json")!!
val bytes = stream.readAllBytes()
stream.close()
val configFile = File("config/${Cobblemon.MODID}/spawning/$CONFIG_NAME.json")
@@ -110,4 +110,4 @@ class BestSpawnerConfig {
outputStream.close()
}
}
}
}
@@ -108,7 +108,7 @@ abstract class AreaSpawner(
return true
}
private fun constrainArea(area: SpawningArea): SpawningArea? {
fun constrainArea(area: SpawningArea): SpawningArea? {
val basePos = BlockPos.Mutable(area.baseX, area.baseY, area.baseZ)
val originalY = area.baseY
@@ -43,12 +43,24 @@ interface Spawner {
fun copyInfluences() = influences.filter { !it.isExpired() }.toMutableList()
fun chooseBucket(): SpawnBucket {
val buckets = Cobblemon.bestSpawner.config.buckets
val weightSum = buckets.sumOf { it.weight.toDouble() }.toFloat()
val influences = this.copyInfluences()
val weightMap = mutableMapOf<SpawnBucket, Float>()
for (bucket in buckets) {
var weight = bucket.weight
for (influence in influences) {
weight = influence.affectBucketWeight(bucket, weight)
}
weightMap[bucket] = weight
}
val weightSum = weightMap.values.sum()
// Make the 0 exclusive and the weightSum inclusive on the random
val chosenSum = weightSum - Random().nextFloat(weightSum)
var sum = 0F
for (bucket in buckets) {
sum += bucket.weight
sum += weightMap[bucket]!!
if (sum >= chosenSum) {
return bucket
}
@@ -72,6 +72,7 @@ abstract class TickingSpawner(
val scheduledSpawn = scheduledSpawn
if (scheduledSpawn != null) {
performSpawn(scheduledSpawn)
this.scheduledSpawn = null
}
ticksUntilNextSpawn -= tickTimerMultiplier
@@ -97,8 +98,11 @@ abstract class TickingSpawner(
fun performSpawn(spawnAction: SpawnAction<*>) {
spawnAction.entity.subscribe { afterSpawn(it) }
spawnAction.run()
this.scheduledSpawn = null
}
open fun getCauseEntity(): Entity? = null
fun getAllInfluences() = this.influences + manager.influences
override fun copyInfluences() = this.getAllInfluences().toMutableList()
}
@@ -87,8 +87,13 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
}
override fun getObservingPlayers() = getServer()?.playerManager?.playerList?.filter { it.uuid in observerUUIDs } ?: emptyList()
/** The total amount of slots in the party. */
fun size() = slots.size
/** The amount of party slots that are occupied by a [Pokemon]. */
fun occupied() = slots.filterNotNull().count()
override fun sendTo(player: ServerPlayerEntity) {
player.sendPacket(InitializePartyPacket(false, uuid, slots.size))
slots.forEachIndexed { index, pokemon ->
@@ -162,6 +167,9 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
slots[slot] = Pokemon().loadFromNBT(pokemonNBT)
}
}
removeDuplicates()
return this
}
@@ -186,9 +194,25 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
slots[slot] = Pokemon().loadFromJSON(json.get(key).asJsonObject)
}
}
removeDuplicates()
return this
}
fun removeDuplicates() {
val knownUUIDs = mutableListOf<UUID>()
for (slot in 0 until this.slots.size) {
val pokemon = get(slot) ?: continue
if (pokemon.uuid !in knownUUIDs) {
knownUUIDs.add(pokemon.uuid)
} else {
slots[slot] = null
anyChangeObservable.emit(Unit)
}
}
}
override fun loadPositionFromNBT(nbt: NbtCompound): StoreCoordinates<PartyPosition> {
val slot = nbt.getByte(DataKeys.STORE_SLOT).toInt()
return StoreCoordinates(this, PartyPosition(slot))
@@ -62,6 +62,9 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
val boxNumber: Int
get() = this.pc.boxes.indexOf(this)
val unoccupiedSlots: Int
get() = POKEMON_PER_BOX - this.pokemon.filterNotNull().count()
fun getFirstAvailablePosition(): PCPosition? {
for (index in 0 until POKEMON_PER_BOX) {
if (this.pokemon[index] == null) {
@@ -144,9 +144,27 @@ open class PCStore(
} else {
tryRestoreBackedUpPokemon()
}
removeDuplicates()
return this
}
fun removeDuplicates() {
val knownUUIDs = mutableListOf<UUID>()
for (box in boxes) {
for (i in 0 until POKEMON_PER_BOX) {
val pokemon = box[i] ?: continue
if (pokemon.uuid !in knownUUIDs) {
knownUUIDs.add(pokemon.uuid)
} else {
box[i] = null
pcChangeObservable.emit(Unit)
}
}
}
}
override fun saveToJSON(json: JsonObject): JsonObject {
json.addProperty(DataKeys.STORE_BOX_COUNT, boxes.size.toShort())
json.addProperty(DataKeys.STORE_BOX_COUNT_LOCKED, lockedSize)
@@ -195,6 +213,9 @@ open class PCStore(
} else {
tryRestoreBackedUpPokemon()
}
removeDuplicates()
return this
}
@@ -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.storage.pc.link
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.permission.Permission
import com.cobblemon.mod.common.api.storage.pc.PCStore
import net.minecraft.server.network.ServerPlayerEntity
/**
* A [PCLink] tied to a player that must have a permission.
*
* @param pc The [PCStore] being opened.
* @param player The [ServerPlayerEntity] opening the PC.
* @param permission The [Permission] required for this link to work.
*/
class PermissiblePcLink(pc: PCStore, player: ServerPlayerEntity, private val permission: Permission) : PCLink(pc, player.uuid) {
override fun isPermitted(player: ServerPlayerEntity): Boolean {
val result = Cobblemon.permissionValidator.hasPermission(player, this.permission)
if (!result) {
PCLinkManager.removeLink(player.uuid)
}
return result
}
}
@@ -68,7 +68,8 @@ data class BattleFormat(
"mod": "$mod",
"gameType": "${battleType.name}",
"gen": $gen,
"ruleset": [${ruleSet.joinToString { "\"$it\"" }}]
"ruleset": [${ruleSet.joinToString { "\"$it\"" }}],
"effectType": "Format"
}
""".trimIndent().replace("\n", "")
}
@@ -43,6 +43,10 @@ object BattleRegistry {
/**
* Packs a team into the showdown format
*
* Example from https://gitlab.com/cable-mc/cobblemon-showdown/-/blob/master/sim/TEAMS.md#packed-format
* NICKNAME|SPECIES|ITEM|ABILITY|MOVES|NATURE|EVS|GENDER|IVS|SHINY|LEVEL|HAPPINESS,POKEBALL,HIDDENPOWERTYPE,GIGANTAMAX,DYNAMAXLEVEL,TERATYPE
*
* @return a string of the packed team
*/
fun List<BattlePokemon>.packTeam() : String {
@@ -98,14 +102,22 @@ object BattleRegistry {
packedTeamBuilder.append("${if (pk.shiny) "S" else ""}|")
// Level
packedTeamBuilder.append("${pk.level}|")
// Misc
// Happiness
packedTeamBuilder.append("${pk.friendship}|")
packedTeamBuilder.append("${pk.friendship},")
// Caught Ball
// This is safe to do as all our pokeballs that have showdown item equivalents are the same IDs they use for the pokeball attribute
val pokeball = pokemon.effectedPokemon.caughtBall.name.path.replace("_", "")
packedTeamBuilder.append("$pokeball|")
packedTeamBuilder.append("$pokeball,")
// Hidden Power Type
packedTeamBuilder.append("|")
packedTeamBuilder.append(",")
// Gigantamax
packedTeamBuilder.append(",")
// DynamaxLevel
packedTeamBuilder.append(",")
// Teratype
packedTeamBuilder.append(",")
team.add(packedTeamBuilder.toString())
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.battles
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.interpreter.Effect
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
@@ -94,12 +95,18 @@ object ShowdownInterpreter {
updateInstructions["|-fieldstart|"] = this::handleFieldStartInstructions
updateInstructions["|-fieldend|"] = this::handleFieldEndInstructions
updateInstructions["|-ability|"] = this::handleAbilityInstructions
//To-Do updateInstructions["|-endability|"] = this::handleEndAbilityInstruction
updateInstructions["|-nothing"] = { battle, _, _ ->
battle.dispatchGo { battle.broadcastChatMessage(battleLang("nothing")) }
}
updateInstructions["|-clearallboost"] = { battle, _, _ ->
battle.dispatchGo { battle.broadcastChatMessage(battleLang("clearallboost")) }
}
updateInstructions["|-singleturn|"] = this::handleSingleTurnInstruction
updateInstructions["|-singlemove|"] = this::handleSingleMoveInstruction
updateInstructions["|-prepare|"] = this::handlePrepareInstruction
updateInstructions["|-swapboost"] = this::handleSwapBoostInstruction
updateInstructions["|-swapsideconditions|"] = this::handleSilently
updateInstructions["|-unboost|"] = { battle, line, remainingLines -> boostInstruction(battle, line, remainingLines, false) }
updateInstructions["|-boost|"] = { battle, line, remainingLines -> boostInstruction(battle, line, remainingLines, true) }
updateInstructions["|-setboost|"] = this::handleSetBoostInstruction
@@ -554,49 +561,39 @@ object ShowdownInterpreter {
}
// |move|p1a: Charizard|Tackle|p2a: Magikarp
private fun handleMoveInstruction(battle: PokemonBattle, message: String, remainingLines: MutableList<String>) {
battle.dispatch {
val editMessaged = message.replace("|move|", "")
this.lastMover[battle.battleId] = message
val userPNX = editMessaged.split("|")[0].split(":")[0].trim()
val (_, userPokemon) = battle.getActorAndActiveSlotFromPNX(userPNX)
val moveName = editMessaged.split("|")[1].split("|")[0]
.replace(" ", "")
.lowercase()
.replace("[^A-z0-9]".toRegex(), "")
private fun handleMoveInstruction(battle: PokemonBattle, rawMessage: String, remainingLines: MutableList<String>) {
battle.dispatchGo {
this.lastMover[battle.battleId] = rawMessage
val message = BattleMessage(rawMessage)
val userPokemon = message.actorAndActivePokemon(0, battle)?.second ?: return@dispatchGo
val targetPokemon = message.actorAndActivePokemon(2, battle)?.second
val effect = message.effectAt(1) ?: return@dispatchGo
val move = Moves.getByNameOrDummy(effect.id)
val moveTemplate = Moves.getByName(moveName) ?: return@dispatch GO.also { battle.broadcastChatMessage("UNRECOGNIZED MOVE: $moveName".text()) }
val hasTarget = editMessaged.split("|").size == 3 && editMessaged.split("|")[2].isNotEmpty()
val targetPokemon = if (hasTarget) {
val targetPNX = editMessaged.split("|")[2].split(":")[0]
battle.getActorAndActiveSlotFromPNX(targetPNX)
} else {
null
}
userPokemon.battlePokemon?.effectedPokemon?.let { pokemon ->
val progress = UseMoveEvolutionProgress()
if (progress.shouldKeep(pokemon)) {
val created = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is UseMoveEvolutionProgress && it.currentProgress().move == moveTemplate }) { progress }
val created = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is UseMoveEvolutionProgress && it.currentProgress().move == move }) { progress }
created.updateProgress(UseMoveEvolutionProgress.Progress(created.currentProgress().move, created.currentProgress().amount + 1))
}
}
if (targetPokemon != null && targetPokemon.second != userPokemon) {
val targetPNX = editMessaged.split("|")[2].split(":")[0]
val (_, targetPokemon) = battle.getActorAndActiveSlotFromPNX(targetPNX)
if (targetPokemon != null && targetPokemon != userPokemon) {
battle.broadcastChatMessage(battleLang(
key = "used_move_on",
userPokemon.battlePokemon?.getName() ?: "ERROR".red(),
moveTemplate.displayName,
move.displayName,
targetPokemon.battlePokemon?.getName() ?: "ERROR".red()
))
} else {
battle.broadcastChatMessage(battleLang(
key = "used_move",
userPokemon.battlePokemon?.getName() ?: "ERROR".red(),
moveTemplate.displayName
move.displayName
))
}
GO
}
}
@@ -730,22 +727,20 @@ object ShowdownInterpreter {
}
}
private fun handleCureStatusInstruction(battle: PokemonBattle, message: String, remainingLines: MutableList<String>) {
battle.dispatch {
val pnx = message.split("|-curestatus|")[1].substring(0, 3)
val remaining = message.substringAfter("|-curestatus|")
val (_, pokemon) = battle.getActorAndActiveSlotFromPNX(pnx)
val status = Statuses.getStatus(remaining.split("|")[1])
val battlePokemon = pokemon.battlePokemon ?: return@dispatch GO
if (status is PersistentStatus) {
battlePokemon.effectedPokemon.status = null
battle.sendUpdate(BattlePersistentStatusPacket(pnx, null))
private fun handleCureStatusInstruction(battle: PokemonBattle, rawMessage: String, remainingLines: MutableList<String>) {
battle.dispatchWaiting {
val message = BattleMessage(rawMessage)
val pokemon = message.actorAndActivePokemon(0, battle)?.second?.battlePokemon ?: return@dispatchWaiting
val status = message.argumentAt(1)?.let(Statuses::getStatus) ?: return@dispatchWaiting
val effect = message.effect()
val lang = when {
effect?.type == Effect.Type.ABILITY -> battleLang("cure_status.ability.${effect.id}", pokemon.getName())
// Lang related to move stuff is tied to the status as a generic message such as fire moves defrosting Pokémon
effect?.type == Effect.Type.MOVE -> battleLang("cure_status.move.${status.name}", pokemon.getName(), Moves.getByNameOrDummy(effect.id).displayName)
message.hasOptionalArgument("msg") -> status.removeMessage?.asTranslated(pokemon.getName()) ?: return@dispatchWaiting
else -> return@dispatchWaiting
}
val showMessage = "[msg]" in remaining
if (status != null && showMessage) {
status.removeMessage?.let { battle.broadcastChatMessage(it.asTranslated(battlePokemon.getName())) }
}
WaitDispatch(1F)
battle.broadcastChatMessage(lang)
}
}
@@ -762,10 +757,63 @@ object ShowdownInterpreter {
if (message.contains("move: Bide")){
battle.broadcastChatMessage(battleLang("bide_start",pokemon.battlePokemon?.getName() ?: ""))
}
if (message.contains("move: Yawn")){
battle.broadcastChatMessage(battleLang("yawn_start",pokemon.battlePokemon?.getName() ?: ""))
}
WaitDispatch(2F)
}
}
private fun handleSingleTurnInstruction(battle: PokemonBattle, rawMessage: String, remainingLines: MutableList<String>) {
battle.dispatchGo{
val message = BattleMessage(rawMessage)
val pokemon = message.actorAndActivePokemon(0, battle)?.second ?: return@dispatchGo
val pokemonName = pokemon.battlePokemon?.getName() ?: return@dispatchGo
val effect = message.effectAt(1) ?: return@dispatchGo
val lang = when(effect.id) {
"protect" -> battleLang("singleturn.protect", pokemonName)
"endure" -> battleLang("singleturn.endure", pokemonName)
"craftyshield" -> battleLang("singleturn.crafty_shield", pokemonName)
"powder" -> battleLang("singleturn.powder", pokemonName)
"followme" -> battleLang("singleturn.follow_me", pokemonName)
"snatch" -> battleLang("singleturn.snatch", pokemonName)
"quickguard" -> battleLang("singleturn.quick_guard", pokemonName)
"wideguard" -> battleLang("singleturn.wide_guard", pokemonName)
"roost" -> battleLang("singleturn.roost", pokemonName)
"matblock" -> battleLang("singleturn.mat_block", pokemonName)
"maxguard" -> battleLang("singleturn.max_guard", pokemonName)
"instruct" -> battleLang("singleturn.instruct", pokemonName, message.actorAndActivePokemonFromOptional(battle)?.second?.battlePokemon?.getName() ?: return@dispatchGo)
"focuspunch" -> battleLang("singleturn.focus_punch", pokemonName)
"electrify" -> battleLang("singleturn.electrify", pokemonName)
"beakblast" -> battleLang("singleturn.beak_blast", pokemonName)
"helpinghand" -> battleLang("singleturn.helping_hand", pokemonName, message.actorAndActivePokemonFromOptional(battle)?.second?.battlePokemon?.getName() ?: return@dispatchGo)
"magiccoat" -> battleLang("singleturn.magic_coat", pokemonName)
"ragepowder" -> battleLang("singleturn.rage_powder", pokemonName)
"shelltrap" -> battleLang("singleturn.shell_trap", pokemonName)
"spotlight" -> battleLang("singleturn.spotlight", pokemonName)
else -> battle.createUnimplemented(message)
}
battle.broadcastChatMessage(lang)
}
}
private fun handleSingleMoveInstruction(battle: PokemonBattle, rawMessage: String, remainingLines: MutableList<String>) {
battle.dispatchGo {
val message = BattleMessage(rawMessage)
val pokemon = message.actorAndActivePokemon(0, battle)?.second ?: return@dispatchGo
val pokemonName = pokemon.battlePokemon?.getName() ?: return@dispatchGo
val effect = message.effectAt(1) ?: return@dispatchGo
val lang = when (effect.id) {
"destinybond" -> battleLang("singlemove.destiny_bond", pokemonName)
"glaiverush" -> battleLang("singlemove.glaive_rush", pokemonName)
"grudge" -> battleLang("singlemove.grudge", pokemonName)
"rage" -> battleLang("singlemove.rage", pokemonName)
else -> battle.createUnimplemented(message)
}
battle.broadcastChatMessage(lang)
}
}
private fun handleActivateInstructions(battle: PokemonBattle, rawMessage: String, remainingLines: MutableList<String>){
battle.dispatchGo{
val message = BattleMessage(rawMessage)
@@ -790,6 +838,11 @@ object ShowdownInterpreter {
"aromatherapy" -> battleLang("activate.aromatherapy")
"trapped" -> battleLang("activate.trapped")
"quickclaw" -> battleLang("item.quick_claw.end", pokemonName)
"bind" -> battleLang("activate.bind", pokemonName, message.actorAndActivePokemonFromOptional(battle)?.second?.battlePokemon?.getName() ?: return@dispatchGo)
"courtchange" -> battleLang("activate.court_change", pokemonName)
"guardsplit" -> battleLang("activate.guard_split", pokemonName)
"spite" -> battleLang("activate.spite", pokemonName, message.argumentAt(2)!!, message.argumentAt(3)!!)
"wrap" -> battleLang("activate.wrap", pokemonName, message.actorAndActivePokemonFromOptional(battle)?.second?.battlePokemon?.getName() ?: return@dispatchGo)
else -> battle.createUnimplemented(message)
}
battle.broadcastChatMessage(lang)
@@ -797,7 +850,7 @@ object ShowdownInterpreter {
}
private fun handleFieldStartInstructions(battle: PokemonBattle, rawMessage: String, remainingLines: MutableList<String>){
battle.dispatchWaiting({
battle.dispatchWaiting {
val message = BattleMessage(rawMessage)
// Note persistent is a CAP ability only we can ignore the flag
val lang: Text = when (message.argumentAt(0)) {
@@ -818,11 +871,11 @@ object ShowdownInterpreter {
else -> battle.createUnimplemented(message)
}
battle.broadcastChatMessage(lang)
})
}
}
private fun handleFieldEndInstructions(battle: PokemonBattle, rawMessage: String, remainingLines: MutableList<String>){
battle.dispatchWaiting({
battle.dispatchWaiting {
val message = BattleMessage(rawMessage)
// Note persistent is a CAP ability only we can ignore the flag
val lang: Text = when (message.argumentAt(0)) {
@@ -843,37 +896,97 @@ object ShowdownInterpreter {
else -> battle.createUnimplemented(message)
}
battle.broadcastChatMessage(lang)
})
}
private fun handleAbilityInstructions(battle: PokemonBattle, message: String, remainingLines: MutableList<String>) {
battle.dispatch {
val editedMessage = message.split("|-ability|")[1]
val pnx = editedMessage.substring(0, 3)
val (_, pokemon) = battle.getActorAndActiveSlotFromPNX(pnx)
val fromWhat = editedMessage.split("|")[1]
when (fromWhat) {
"Speed Boost" -> battle.broadcastChatMessage(battleLang("speed_boost_ability", pokemon.battlePokemon!!.getName()))
else -> battle.broadcastChatMessage(editedMessage.text())
}
WaitDispatch(1F)
}
}
private fun handleEndInstruction(battle: PokemonBattle, message: String, remainingLines: MutableList<String>) {
battle.dispatch {
val editedMessage = message.split("|-end|")[1]
val pnx = editedMessage.substring(0, 3)
val (_, pokemon) = battle.getActorAndActiveSlotFromPNX(pnx)
val fromWhat = editedMessage.split("|")[1]
when (fromWhat) {
"confusion" -> battle.broadcastChatMessage(battleLang("confusion_snapped", pokemon.battlePokemon!!.getName()))
"move: Bide" -> battle.broadcastChatMessage(battleLang("bide_end", pokemon.battlePokemon!!.getName()))
else -> battle.broadcastChatMessage(editedMessage.text())
private fun handleAbilityInstructions(battle: PokemonBattle, rawMessage: String, remainingLines: MutableList<String>) {
battle.dispatchGo {
val message = BattleMessage(rawMessage)
val pokemon = message.actorAndActivePokemon(0, battle)?.second ?: return@dispatchGo
val pokemonName = pokemon.battlePokemon?.getName() ?: return@dispatchGo
val effect = message.effectAt(1) ?: return@dispatchGo
//val abilityId = message.argumentAt(1) ?: return@dispatchWaiting
//val effect = message.effect() ?: return@dispatchWaiting
val feedback = when (effect.id) {
"speedboost" -> battleLang("ability.speed_boost", pokemonName)
"sturdy" -> battleLang("ability.sturdy", pokemonName)
"intimidate" -> battleLang("ability.intimidate", pokemonName)
"unnerve" -> battleLang("ability.unnerve", pokemonName)
"anticipation" -> battleLang("ability.anticipation", pokemonName)
"airlock" -> battleLang("ability.air_lock")
"cloudnine" -> battleLang("ability.air_lock") //Cloud Nine shares the same text as Air Lock
else -> battleLang("ability.generic", pokemonName, message.argumentAt(1)!!)
}
battle.broadcastChatMessage(feedback)
}
}
WaitDispatch(1F)
private fun handlePrepareInstruction(battle: PokemonBattle, rawMessage: String, remainingLines: MutableList<String>) {
battle.dispatchGo {
val message = BattleMessage(rawMessage)
val pokemon = message.actorAndActivePokemon(0, battle)?.second ?: return@dispatchGo
val pokemonName = pokemon.battlePokemon?.getName() ?: return@dispatchGo
val effect = message.effectAt(1) ?: return@dispatchGo
//Prevents spam when the move Role Play is used
val lang = when(effect.id) {
"bounce" -> battleLang("prepare.bounce", pokemonName)
"dig" -> battleLang("prepare.dig", pokemonName)
"dive" -> battleLang("prepare.dive", pokemonName)
"fly" -> battleLang("prepare.fly", pokemonName)
"freezeshock" -> battleLang("prepare.freeze_shock", pokemonName)
"geomancy" -> battleLang("prepare.geomancy", pokemonName)
"iceburn" -> battleLang("prepare.ice_burn", pokemonName)
"meteorbeam" -> battleLang("prepare.meteor_beam", pokemonName)
"phantomforce" -> battleLang("prepare.phantom_force", pokemonName)
"razorwind" -> battleLang("prepare.razor_wind", pokemonName)
"shadowforce" -> battleLang("prepare.phantom_force", pokemonName) //Phantom Force and Shadow Force share the same text
"skullbash" -> battleLang("prepare.skull_bash", pokemonName)
"skyattack" -> battleLang("prepare.sky_attack", pokemonName)
"skydrop" -> battleLang("prepare.sky_drop", pokemonName)
"solarbeam" -> battleLang("prepare.solar_beam", pokemonName)
"solarblade" -> battleLang("prepare.solar_beam", pokemonName) //Solar Beam and Solar Blade share the same text
else -> battle.createUnimplemented(message)
}
battle.broadcastChatMessage(lang)
}
}
private fun handleSwapBoostInstruction(battle: PokemonBattle, rawMessage: String, remainingLines: MutableList<String>) {
battle.dispatchGo {
val message = BattleMessage(rawMessage)
val pokemon = message.actorAndActivePokemon(0, battle)?.second ?: return@dispatchGo
val pokemonName = pokemon.battlePokemon?.getName() ?: return@dispatchGo
val targetPokemonName = message.actorAndActivePokemonFromOptional(battle)?.second?.battlePokemon?.getName() ?: return@dispatchGo
val effect = message.effectAt(1) ?: return@dispatchGo
val lang = when(effect.id) {
"guardswap" -> battleLang("swapboost.generic", pokemonName, targetPokemonName)
"heartswap" -> battleLang("swapboost.generic", pokemonName, targetPokemonName)
"powerswap" -> battleLang("swapboost.generic", pokemonName, targetPokemonName)
else -> battle.createUnimplemented(message)
}
battle.broadcastChatMessage(lang)
}
}
private fun handleEndInstruction(battle: PokemonBattle, rawMessage: String, remainingLines: MutableList<String>) {
battle.dispatchWaiting {
val message = BattleMessage(rawMessage)
val battlePokemon = message.actorAndActivePokemon(0, battle)?.second?.battlePokemon ?: return@dispatchWaiting
val pokemonName = battlePokemon.getName()
val effect = message.effectAt(1) ?: return@dispatchWaiting
if (message.hasOptionalArgument("silent")) {
LOGGER.debug("Received silent: {}", message.rawMessage)
}
else {
val feedback = when (effect.id) {
"confusion" -> battleLang("confusion_snapped", pokemonName)
"bide" -> battleLang("bide_end", pokemonName)
"bind" -> battleLang("end.bide", pokemonName)
"wrap" -> battleLang("end.wrap", pokemonName)
else -> battle.createUnimplemented(message)
}
battle.broadcastChatMessage(feedback)
}
}
}
@@ -1018,6 +1131,11 @@ object ShowdownInterpreter {
battle.dispatch {
val newHealthRatio: Float
val remainingHealth = newHealth.split("/")[0].toInt()
if (battleMessage.optionalArgument("from")?.equals("move: Wrap") == true) {
battle.broadcastChatMessage(battleLang("hurt.wrap", activePokemon.battlePokemon?.getName()!!))
}
if (newHealth == "0") {
newHealthRatio = 0F
battle.dispatch {
@@ -1101,7 +1219,7 @@ object ShowdownInterpreter {
}
private fun handleHealInstruction(battle: PokemonBattle, actor: BattleActor, rawPublic: String, rawPrivate: String) {
battle.dispatchWaiting({
battle.dispatchWaiting {
val publicMessage = BattleMessage(rawPublic)
val privateMessage = BattleMessage(rawPrivate)
val pnx = privateMessage.argumentAt(0)?.substring(0, 3) ?: return@dispatchWaiting
@@ -1136,6 +1254,7 @@ object ShowdownInterpreter {
"ingrain" -> battleLang("heal.ingrain", battlePokemon.getName())
"grassyterrain" -> battleLang("heal.grassy_terrain", battlePokemon.getName())
"leftovers" -> battleLang("heal.leftovers", battlePokemon.getName())
"raindish" -> battleLang("heal.rain_dish", battlePokemon.getName())
else -> battle.createUnimplementedSplit(publicMessage, privateMessage)
}
}
@@ -1154,11 +1273,11 @@ object ShowdownInterpreter {
status.applyMessage.let { battle.broadcastChatMessage(it.asTranslated(battlePokemon.getName())) }
}
}
})
}
}
private fun handleSetHpInstructions(battle: PokemonBattle, actor: BattleActor, rawPublic: String, rawPrivate: String){
battle.dispatchWaiting({
battle.dispatchWaiting {
val publicMessage = BattleMessage(rawPublic)
val privateMessage = BattleMessage(rawPrivate)
val pnx = privateMessage.argumentAt(0)?.substring(0, 3) ?: return@dispatchWaiting
@@ -1177,7 +1296,13 @@ object ShowdownInterpreter {
else -> battle.createUnimplemented(publicMessage)
}
battle.broadcastChatMessage(lang)
})
}
}
// Used for things that are only meant for visual information we don't have
private fun handleSilently(battle: PokemonBattle, baseMessage: String, remainingLines: MutableList<String>) {
battle.dispatchGo { }
}
}
@@ -33,13 +33,14 @@ class GraalShowdownUnbundler {
.create()
fun attemptUnbundle() {
var showdownDir = File("showdown")
val showdownDir = File("showdown")
val metadata = loadShowdownMetadata()
// Check if showdown needs to be installed
if (!showdownDir.exists() || Cobblemon.config.autoUpdateShowdown) {
showdownDir.mkdirs()
val showdownZip = File(showdownDir, "showdown.zip")
showdownZip.mkdirs()
val showdownMetadataFile = File(showdownDir, "showdown.json")
var extract = true
@@ -56,12 +57,11 @@ class GraalShowdownUnbundler {
backupDir.deleteRecursively()
}
showdownDir.copyTo(File("showdown-backup"))
showdownDir.copyTo(backupDir)
}
}
if (extract) {
showdownDir = showdownZip.parentFile
Identifier(Cobblemon.MODID, "showdown.zip").extractTo(showdownZip)
Identifier(Cobblemon.MODID, "showdown.json").extractTo(showdownMetadataFile)
FileUtils.unzipFile(showdownZip.toPath(), showdownDir.toPath())
@@ -12,9 +12,11 @@ import com.cobblemon.mod.common.api.apricorn.Apricorn
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.farming.ApricornHarvestEvent
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.util.playSoundServer
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.block.*
import net.minecraft.entity.ItemEntity
import net.minecraft.entity.ai.pathing.NavigationType
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemPlacementContext
@@ -79,6 +81,14 @@ class ApricornBlock(settings: Settings, val apricorn: Apricorn) : HorizontalFaci
}
}
@Deprecated("Deprecated in Java")
override fun getCollisionShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape {
if (context is EntityShapeContext && (context.entity as? ItemEntity)?.stack?.isIn(CobblemonItemTags.APRICORNS) == true) {
return VoxelShapes.empty()
}
return super.getCollisionShape(state, world, pos, context)
}
override fun getPlacementState(ctx: ItemPlacementContext): BlockState? {
var blockState = defaultState
val worldView = ctx.world
@@ -149,7 +149,7 @@ class HealingMachineBlock(properties: Settings) : BlockWithEntity(properties) {
return ActionResult.SUCCESS
}
if (player.uuid in HealingMachineBlockEntity.alreadyHealing) {
if (HealingMachineBlockEntity.isUsingHealer(player)) {
player.sendMessage(lang("healingmachine.alreadyhealing").red())
return ActionResult.SUCCESS
}
@@ -161,6 +161,7 @@ class HealingMachineBlock(properties: Settings) : BlockWithEntity(properties) {
val neededCharge = player.party().getHealingRemainderPercent() - blockEntity.healingCharge
player.sendMessage(lang("healingmachine.notenoughcharge", "${((neededCharge/party.count())*100f).toInt()}%").red())
}
party.forEach { it.tryRecallWithAnimation() }
return ActionResult.CONSUME
}
@@ -176,6 +177,14 @@ class HealingMachineBlock(properties: Settings) : BlockWithEntity(properties) {
}
}
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) {
val blockEntity = world.getBlockEntity(pos)
if (blockEntity is HealingMachineBlockEntity) {
blockEntity.clearData()
}
super.onBreak(world, pos, state, player)
}
override fun randomDisplayTick(state: BlockState, world: World, pos: BlockPos, random: Random) {
val blockEntity = world.getBlockEntity(pos)
if (blockEntity !is HealingMachineBlockEntity) return
@@ -205,4 +214,4 @@ class HealingMachineBlock(properties: Settings) : BlockWithEntity(properties) {
tooltip.add("block.${Cobblemon.MODID}.healing_machine.tooltip1".asTranslated().gray())
tooltip.add("block.${Cobblemon.MODID}.healing_machine.tooltip2".asTranslated().gray())
}
}
}
@@ -20,6 +20,7 @@ import java.util.UUID
import net.minecraft.block.BlockState
import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityTicker
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket
import net.minecraft.server.network.ServerPlayerEntity
@@ -90,14 +91,14 @@ class HealingMachineBlockEntity(
val player = this.currentUser?.getPlayer() ?: return clearData()
val party = player.party()
alreadyHealing.remove(player.uuid)
party.heal()
player.sendMessage(lang("healingmachine.healed").green())
updateBlockChargeLevel()
clearData()
}
private fun clearData() {
internal fun clearData() {
this.currentUser?.let(alreadyHealing::remove)
this.currentUser = null
this.pokeBalls.clear()
this.healTimeLeft = 0
@@ -198,7 +199,7 @@ class HealingMachineBlockEntity(
}
companion object {
val alreadyHealing = mutableListOf<UUID>()
private val alreadyHealing = hashSetOf<UUID>()
const val MAX_REDSTONE_SIGNAL = 10
internal val TICKER = BlockEntityTicker<HealingMachineBlockEntity> { world, _, _, blockEntity ->
@@ -222,5 +223,8 @@ class HealingMachineBlockEntity(
}
}
}
fun isUsingHealer(player: PlayerEntity) = this.alreadyHealing.contains(player.uuid)
}
}
@@ -28,4 +28,9 @@ class EmptyPokeBallClientDelegate : PokeBallPoseableState(), EntitySideDelegate<
}
entity.shakeEmitter.subscribe { shakeEmitter.emit(Unit) }
}
override fun tick(entity: EmptyPokeBallEntity) {
super.tick(entity)
updateLocatorPosition(entity.pos)
}
}
@@ -113,13 +113,7 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
}
}
val model = currentModel
if (model != null) {
val curState = model.currentState
model.currentState = this
model.updateLocators()
model.currentState = curState
}
updateLocatorPosition(entity.pos)
previousVerticalVelocity = entity.velocity.y.toFloat()
}
@@ -135,4 +129,8 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
statefulAnimations.add(animation)
}
}
override fun updatePostDeath() {
++entity.deathTime
}
}
@@ -29,6 +29,7 @@ import com.cobblemon.mod.common.util.lang
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.util.InputUtil
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.sound.SoundEvent
import net.minecraft.text.Text
@@ -408,9 +409,19 @@ class PCGUI(
}
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
if (keyCode == 256) {
playSound(CobblemonSounds.PC_OFF.get())
UnlinkPlayerFromPCPacket().sendToServer()
when (keyCode) {
InputUtil.GLFW_KEY_ESCAPE -> {
playSound(CobblemonSounds.PC_OFF.get())
UnlinkPlayerFromPCPacket().sendToServer()
}
InputUtil.GLFW_KEY_RIGHT -> {
playSound(CobblemonSounds.GUI_CLICK.get())
this.storageWidget.box += 1
}
InputUtil.GLFW_KEY_LEFT -> {
playSound(CobblemonSounds.GUI_CLICK.get())
this.storageWidget.box -= 1
}
}
return super.keyPressed(keyCode, scanCode, modifiers)
}
@@ -157,13 +157,15 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
starterRoundaboutLeft = StarterRoundabout(
pX = x + 89, pY = height / 2 + 84,
pWidth = StarterRoundabout.MODEL_WIDTH, pHeight = StarterRoundabout.MODEL_HEIGHT,
pokemon = currentCategory.pokemon[leftOfCurrentSelection()]
pokemon = currentCategory.pokemon[leftOfCurrentSelection()],
clickAction = { _, _ -> this.left() }
)
starterRoundaboutRight = StarterRoundabout(
pX = x + 149, pY = height / 2 + 84,
pWidth = StarterRoundabout.MODEL_WIDTH, pHeight = StarterRoundabout.MODEL_HEIGHT,
pokemon = currentCategory.pokemon[rightOfCurrentSelection()]
pokemon = currentCategory.pokemon[rightOfCurrentSelection()],
clickAction = { _, _ -> this.right() }
)
addDrawableChild(starterRoundaboutLeft)
@@ -29,7 +29,8 @@ import net.minecraft.util.math.Vec3f
class StarterRoundabout(
pX: Int, pY: Int,
pWidth: Int, pHeight: Int,
var pokemon: RenderablePokemon
var pokemon: RenderablePokemon,
private val clickAction: (mouseX: Double, mouseY: Double) -> Unit = { _, _ -> }
): SoundlessWidget(pX, pY, pWidth, pHeight, Text.literal("StarterRoundabout")) {
companion object {
@@ -44,6 +45,10 @@ class StarterRoundabout(
}
override fun render(matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
if (!this.visible) {
return
}
this.hovered = mouseX >= this.x && mouseX < this.x + this.width && mouseY >= (this.y - MODEL_HEIGHT) && mouseY < this.y
matrices.push()
/*
* This correction term is due to where scaling comes from in a render. We are giving the drawProfilePokemon
@@ -76,4 +81,19 @@ class StarterRoundabout(
matrices.pop()
}
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
if (this.clicked(pMouseX, pMouseY) && this.isValidClickButton(pButton)) {
this.onClick(pMouseX, pMouseY)
}
return super.mouseClicked(pMouseX, pMouseY, pButton)
}
override fun clicked(mouseX: Double, mouseY: Double): Boolean {
return this.active && this.visible && this.hovered
}
override fun onClick(mouseX: Double, mouseY: Double) {
this.clickAction(mouseX, mouseY)
}
}
@@ -28,6 +28,7 @@ import com.cobblemon.mod.common.client.net.battle.BattleSwitchPokemonHandler
import com.cobblemon.mod.common.client.net.battle.BattleUpdateTeamPokemonHandler
import com.cobblemon.mod.common.client.net.battle.ChallengeNotificationHandler
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.effect.SpawnSnowstormParticleHandler
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
@@ -141,6 +142,7 @@ object ClientPacketRegistrar : SidedPacketRegistrar() {
// Data registries
registerHandler(UnlockReloadPacketHandler)
registerHandler(DataRegistrySyncPacketHandler<AbilityTemplate, AbilityRegistrySyncPacket>())
registerHandler(DataRegistrySyncPacketHandler<MoveTemplate, MovesRegistrySyncPacket>())
registerHandler(DataRegistrySyncPacketHandler<Species, SpeciesRegistrySyncPacket>())
@@ -24,5 +24,6 @@ object BattleCaptureEndHandler : ClientPacketHandler<BattleCaptureEndPacket> {
} else {
activeBattlePokemon.ballCapturing?.finish()
}
activeBattlePokemon.ballCapturing = null
}
}
@@ -46,5 +46,7 @@ object BattleSwitchPokemonHandler : ClientPacketHandler<BattleSwitchPokemonPacke
}
)
)
CobblemonClient.storage.switchToPokemon(packet.newPokemon.uuid)
}
}
@@ -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.client.net.data
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.net.PacketHandler
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
internal object UnlockReloadPacketHandler : PacketHandler<UnlockReloadPacket> {
override fun invoke(packet: UnlockReloadPacket, ctx: CobblemonNetwork.NetworkContext) {
CobblemonDataProvider.canReload = true
}
}
@@ -25,7 +25,7 @@ object SpawnSnowstormParticleHandler : ClientPacketHandler<SpawnSnowstormParticl
matrix.translate(packet.position.x, packet.position.y, packet.position.z)
matrix.multiply(POSITIVE_Y.getDegreesQuaternion(packet.yawDegrees))
matrix.multiply(POSITIVE_X.getDegreesQuaternion(packet.pitchDegrees))
wrapper.update(matrix.peek().positionMatrix)
wrapper.updateMatrix(matrix.peek().positionMatrix)
val world = MinecraftClient.getInstance().world ?: return
ParticleStorm(packet.effect, wrapper, world).spawn()
}
@@ -39,7 +39,7 @@ class ParticleStorm(
val sourceAlive: () -> Boolean = { true },
val sourceVisible: () -> Boolean = { true },
val runtime: MoLangRuntime = MoLangRuntime()
): NoRenderParticle(world, matrixWrapper.matrix.getOrigin().x, matrixWrapper.matrix.getOrigin().y, matrixWrapper.matrix.getOrigin().z) {
): NoRenderParticle(world, matrixWrapper.getOrigin().x, matrixWrapper.getOrigin().y, matrixWrapper.getOrigin().z) {
fun spawn() {
MinecraftClient.getInstance().particleManager.addParticle(this)
}
@@ -85,7 +85,7 @@ class ParticleStorm(
return
}
val pos = matrixWrapper.matrix.getOrigin()
val pos = matrixWrapper.getOrigin()
prevPosX = x
prevPosY = y
prevPosZ = z
@@ -134,6 +134,6 @@ class ParticleStorm(
contextStorm = null
}
fun transformPosition(position: Vec3d): Vec3d = matrixWrapper.matrix.transformPosition(position)
fun transformPosition(position: Vec3d): Vec3d = matrixWrapper.transformPosition(position)
fun transformDirection(direction: Vec3d): Vec3d = matrixWrapper.matrix.transformDirection(direction)
}
@@ -8,7 +8,10 @@
package com.cobblemon.mod.common.client.render
import com.cobblemon.mod.common.util.math.geometry.getOrigin
import com.cobblemon.mod.common.util.math.geometry.transformPosition
import net.minecraft.util.math.Matrix4f
import net.minecraft.util.math.Vec3d
/**
* Holds onto a space matrix for quick access, exposes the matrix to mutation.
@@ -17,10 +20,17 @@ import net.minecraft.util.math.Matrix4f
* @since February 10th, 2023
*/
class MatrixWrapper {
var position: Vec3d = Vec3d.ZERO
var matrix: Matrix4f = Matrix4f()
fun update(rotationMatrix: Matrix4f): MatrixWrapper {
fun updateMatrix(rotationMatrix: Matrix4f) = apply {
this.matrix = rotationMatrix.copy()
return this
}
fun updatePosition(position: Vec3d) = apply {
this.position = position
}
fun getOrigin() = position.add(matrix.getOrigin())
fun transformPosition(position: Vec3d) = this.position.add(matrix.transformPosition(position))
}
@@ -26,11 +26,11 @@ import net.minecraft.util.math.Vec3f
class PokemonItemRenderer : CobblemonBuiltinItemRenderer {
override fun render(stack: ItemStack, mode: ModelTransformation.Mode, matrices: MatrixStack, vertexConsumers: VertexConsumerProvider, light: Int, overlay: Int) {
val pokemonItem = stack.item as? PokemonItem ?: return
val pokemon = pokemonItem.asPokemon(stack) ?: return
val (species, aspects) = pokemonItem.getSpeciesAndAspects(stack) ?: return
matrices.push()
val model = PokemonModelRepository.getPoser(pokemon.species.resourceIdentifier, pokemon.aspects)
val renderLayer = model.getLayer(PokemonModelRepository.getTexture(pokemon.species.resourceIdentifier, pokemon.aspects, null))
val model = PokemonModelRepository.getPoser(species.resourceIdentifier, aspects)
val renderLayer = model.getLayer(PokemonModelRepository.getTexture(species.resourceIdentifier, aspects, null))
val transformations = positions[mode]!!
@@ -54,7 +54,7 @@ class PokemonItemRenderer : CobblemonBuiltinItemRenderer {
matrices.push()
val packedLight = LightmapTextureManager.pack(11, 7)
model.withLayerContext(vertexConsumers, null, PokemonModelRepository.getLayers(pokemon.species.resourceIdentifier, pokemon.aspects)) {
model.withLayerContext(vertexConsumers, null, PokemonModelRepository.getLayers(species.resourceIdentifier, aspects)) {
model.render(matrices, vertexConsumer, packedLight, OverlayTexture.DEFAULT_UV, 1F, 1F, 1F, 1F)
}
@@ -22,7 +22,12 @@ import net.minecraft.client.render.entity.feature.FeatureRendererContext
import net.minecraft.client.render.entity.model.PlayerEntityModel
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.entity.player.PlayerEntity
import java.util.*
class PokemonOnShoulderRenderer<T : PlayerEntity>(renderLayerParent: FeatureRendererContext<T, PlayerEntityModel<T>>) : FeatureRenderer<T, PlayerEntityModel<T>>(renderLayerParent) {
private val playerCache = hashMapOf<UUID, ShoulderCache>()
override fun render(
pMatrixStack: MatrixStack,
pBuffer: VertexConsumerProvider,
@@ -53,7 +58,21 @@ class PokemonOnShoulderRenderer<T : PlayerEntity>(renderLayerParent: FeatureRend
val compoundTag = if (pLeftShoulder) pLivingEntity.shoulderEntityLeft else pLivingEntity.shoulderEntityRight
if (compoundTag.isPokemonEntity()) {
pMatrixStack.push()
val pokemon = Pokemon().loadFromNBT(compoundTag.getCompound(DataKeys.POKEMON))
val uuid = compoundTag.getCompound(DataKeys.POKEMON).getUuid(DataKeys.POKEMON_UUID)
val cache = this.playerCache.getOrPut(pLivingEntity.uuid) { ShoulderCache() }
val pokemon: Pokemon
if (pLeftShoulder && cache.lastKnownLeft?.uuid != uuid) {
pokemon = Pokemon().loadFromNBT(compoundTag.getCompound(DataKeys.POKEMON))
cache.lastKnownLeft = pokemon
}
else if (!pLeftShoulder && cache.lastKnownRight?.uuid != uuid) {
pokemon = Pokemon().loadFromNBT(compoundTag.getCompound(DataKeys.POKEMON))
cache.lastKnownRight = pokemon
}
else {
// should never be null but might as well be safe
pokemon = (if (pLeftShoulder) cache.lastKnownLeft else cache.lastKnownRight) ?: Pokemon()
}
val scale = pokemon.form.baseScale * pokemon.scaleModifier
val width = pokemon.form.hitbox.width
val offset = width / 2 - 0.7
@@ -90,4 +109,7 @@ class PokemonOnShoulderRenderer<T : PlayerEntity>(renderLayerParent: FeatureRend
pMatrixStack.pop();
}
}
private data class ShoulderCache(var lastKnownLeft: Pokemon? = null, var lastKnownRight: Pokemon? = null)
}
@@ -75,7 +75,7 @@ class LocatorAccess(
for ((name, locator) in locators) {
matrixStack.push()
locator.rotate(matrixStack)
state.getOrPut(name) { MatrixWrapper() }.update(matrixStack.peek().positionMatrix)
state.getOrPut(name) { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
matrixStack.pop()
}
@@ -293,7 +293,7 @@ abstract class PoseableEntityModel<T : Entity>(
state.currentModel = this
setDefault()
state.preRender()
updateLocators()
updateLocators(state)
var poseName = state.getPose()
var pose = poseName?.let { getPose(it) }
val entityPoseType = if (entity is Poseable) entity.getPoseType() else null
@@ -328,7 +328,7 @@ abstract class PoseableEntityModel<T : Entity>(
state.currentPose?.let { getPose(it) }?.idleStateful(entity, this, state, limbSwing, limbSwingAmount, ageInTicks, headYaw, headPitch)
state.applyAdditives(entity, this, state)
relevantParts.forEach { it.changeFactor = 1F }
updateLocators()
updateLocators(state)
}
override fun setAngles(entity: T, limbSwing: Float, limbSwingAmount: Float, ageInTicks: Float, headYaw: Float, headPitch: Float) {
@@ -364,30 +364,26 @@ abstract class PoseableEntityModel<T : Entity>(
* Figures out where all of this model's locators are in real space, so that they can be
* found and used from other client-side systems.
*/
fun updateLocators() {
currentState?.let {
val entity = currentEntity ?: return
val matrixStack = MatrixStack()
matrixStack.translate(entity.x, entity.y, entity.z)
fun updateLocators(state: PoseableEntityState<T>) {
val entity = currentEntity ?: return
val matrixStack = MatrixStack()
// We could improve this to be generalized for other entities
if (entity is PokemonEntity) {
matrixStack.multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion(180 - entity.bodyYaw))
matrixStack.push()
// We could improve this to be generalized for other entities
if (entity is PokemonEntity) {
matrixStack.multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion(180 - entity.bodyYaw))
matrixStack.push()
matrixStack.scale(-1F, -1F, 1F)
val scale = entity.pokemon.form.baseScale * entity.pokemon.scaleModifier * (entity.delegate as PokemonClientDelegate).entityScaleModifier
matrixStack.scale(scale, scale, scale)
} else if (entity is EmptyPokeBallEntity) {
matrixStack.multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion(entity.yaw))
matrixStack.push()
matrixStack.scale(1F, -1F, -1F)
matrixStack.scale(0.7F, 0.7F, 0.7F)
}
// Standard living entity offset, only God knows why Mojang did this.
matrixStack.translate(0.0, -1.5, 0.0)
locatorAccess.update(matrixStack, it.locatorStates)
matrixStack.scale(-1F, -1F, 1F)
val scale = entity.pokemon.form.baseScale * entity.pokemon.scaleModifier * (entity.delegate as PokemonClientDelegate).entityScaleModifier
matrixStack.scale(scale, scale, scale)
} else if (entity is EmptyPokeBallEntity) {
matrixStack.multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion(entity.yaw))
matrixStack.push()
matrixStack.scale(1F, -1F, -1F)
matrixStack.scale(0.7F, 0.7F, 0.7F)
}
// Standard living entity offset, only God knows why Mojang did this.
matrixStack.translate(0.0, -1.5, 0.0)
locatorAccess.update(matrixStack, state.locatorStates)
}
fun ModelPart.translation(
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.quirk.QuirkData
import java.util.concurrent.ConcurrentLinkedQueue
import net.minecraft.client.MinecraftClient
import net.minecraft.entity.Entity
import net.minecraft.util.math.Vec3d
/**
* Represents the entity-specific state for a poseable model. The implementation is responsible for
@@ -108,4 +109,8 @@ abstract class PoseableEntityState<T : Entity> {
statefulAnimations.clear()
statefulAnimations.addAll(animations)
}
fun updateLocatorPosition(position: Vec3d) {
locatorStates.values.toList().forEach { it.updatePosition(position) }
}
}
@@ -32,8 +32,8 @@ class PidgeotModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Biped
override val head = getPart("head")
private val tail = getPart("tail")
override val portraitScale = 2.3F
override val portraitTranslation = Vec3d(-0.15, -0.4, 0.0)
override val portraitScale = 2.8F
override val portraitTranslation = Vec3d(-0.15, -0.8, 0.0)
override val profileScale = 1.2F
override val profileTranslation = Vec3d(0.0, -0.05, 0.0)
@@ -34,8 +34,8 @@ class PidgeottoModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bip
override val head = getPart("head")
private val tail = getPart("tail")
override val portraitScale = 2.3F
override val portraitTranslation = Vec3d(-0.1, -0.55, 0.0)
override val portraitScale = 2.8F
override val portraitTranslation = Vec3d(-0.1, -0.8, 0.0)
override val profileScale = 1.1F
override val profileTranslation = Vec3d(0.0, 0.1, 0.0)
@@ -82,6 +82,7 @@ class PidgeottoModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bip
quirks = arrayOf(blink),
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("pidgeotto", "ground_idle"),
rootPart.translation(
function = parabolaFunction(
peak = -4F,
@@ -32,8 +32,8 @@ class PidgeyModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BipedF
override val head = getPart("head")
private val tail = getPart("tail")
override val portraitScale = 2.5F
override val portraitTranslation = Vec3d(0.0, -1.3, 0.0)
override val portraitScale = 3.5F
override val portraitTranslation = Vec3d(-0.1, -2.1, 0.0)
override val profileScale = 1.2F
override val profileTranslation = Vec3d(0.0, -0.01, 0.0)
@@ -8,14 +8,17 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
class PorygonModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BipedFrame {
@@ -30,13 +33,21 @@ class PorygonModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Biped
override val profileScale = 1.0F
override val profileTranslation = Vec3d(0.0, 0.8, 0.0)
lateinit var sleep: PokemonPose
lateinit var standing: PokemonPose
lateinit var walk: PokemonPose
override fun registerPoses() {
val blink = quirk("blink") { bedrockStateful("porygonz", "blink").setPreventsIdle(false) }
sleep = registerPose(
poseType = PoseType.SLEEP,
idleAnimations = arrayOf(bedrock("porygon", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink),
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
@@ -47,16 +58,17 @@ class PorygonModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Biped
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
quirks = arrayOf(blink),
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("porygon", "ground_idle")
bedrock("porygon", "ground_walk")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PoseableEntityState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("porygon", "faint") else null
override fun getFaintAnimation(
pokemonEntity: PokemonEntity,
state: PoseableEntityState<PokemonEntity>
) = if (state.isPosedIn(standing, walk)) bedrockStateful("porygon", "faint2") else null
}
@@ -8,8 +8,10 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.asTransformed
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
@@ -24,27 +26,67 @@ class StarmieModel(root: ModelPart) : PokemonPoseableModel() {
override val profileScale = 1.4F
override val profileTranslation = Vec3d(0.0, -0.24, 0.0)
lateinit var battleidle: PokemonPose
lateinit var standing: PokemonPose
lateinit var walk: PokemonPose
lateinit var swim: PokemonPose
lateinit var float: PokemonPose
lateinit var sleep: PokemonPose
override fun registerPoses() {
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
transformTicks = 10,
poseType = PoseType.STAND,
condition = { !it.isBattling },
idleAnimations = arrayOf(
//bedrock("starmie", "ground_idle")
bedrock("starmie", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
transformTicks = 10,
poseType = PoseType.WALK,
condition = { !it.isBattling },
idleAnimations = arrayOf(
//bedrock("starmie", "ground_walk")
bedrock("starmie", "ground_walk")
)
)
float = registerPose(
poseName = "float",
poseTypes = UI_POSES + PoseType.FLOAT,
condition = { !it.isBattling },
idleAnimations = arrayOf(
bedrock("starmie", "water_idle")
)
)
swim = registerPose(
poseName = "swim",
poseType = PoseType.SWIM,
condition = { !it.isBattling },
idleAnimations = arrayOf(
bedrock("starmie", "water_swim")
)
)
sleep = registerPose(
poseName = "sleep",
poseType = PoseType.SLEEP,
idleAnimations = arrayOf(bedrock("starmie", "sleep"))
)
battleidle = registerPose(
poseName = "battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
transformTicks = 10,
condition = { it.isBattling },
idleAnimations = arrayOf(
bedrock("starmie", "battle_idle")
)
)
}
// override fun getFaintAnimation(
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
@@ -26,25 +27,49 @@ class StaryuModel(root: ModelPart) : PokemonPoseableModel() {
lateinit var standing: PokemonPose
lateinit var walk: PokemonPose
lateinit var swim: PokemonPose
lateinit var float: PokemonPose
lateinit var sleep: PokemonPose
override fun registerPoses() {
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
transformTicks = 10,
poseType = PoseType.STAND,
idleAnimations = arrayOf(
bedrock("staryu", "idle")
bedrock("staryu", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
transformTicks = 10,
poseType = PoseType.WALK,
idleAnimations = arrayOf(
//bedrock("staryu", "ground_walk")
bedrock("staryu", "ground_walk")
)
)
float = registerPose(
poseName = "float",
poseTypes = UI_POSES + PoseType.FLOAT,
idleAnimations = arrayOf(
bedrock("staryu", "water_idle")
)
)
swim = registerPose(
poseName = "swim",
poseType = PoseType.SWIM,
idleAnimations = arrayOf(
bedrock("staryu", "water_swim")
)
)
sleep = registerPose(
poseName = "sleep",
poseType = PoseType.SLEEP,
idleAnimations = arrayOf(bedrock("staryu", "sleep"))
)
}
// override fun getFaintAnimation(
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen2
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
class MagcargoModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("magcargo")
override val head = getPart("head")
override val portraitScale = 1.8F
override val portraitTranslation = Vec3d(-0.55, 0.1, 0.0)
override val profileScale = 0.8F
override val profileTranslation = Vec3d(0.0, 0.5, 0.0)
lateinit var sleep: PokemonPose
lateinit var standing: PokemonPose
lateinit var walk: PokemonPose
override fun registerPoses() {
val blink = quirk("blink") { bedrockStateful("magcargo", "blink").setPreventsIdle(false) }
sleep = registerPose(
poseType = PoseType.SLEEP,
idleAnimations = arrayOf(bedrock("magcargo", "ground_sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("magcargo", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
bedrock("magcargo", "ground_walk")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PoseableEntityState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("magcargo", "faint") else null
}
@@ -18,7 +18,7 @@ import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
class PiloswineModel(root: ModelPart) : PokemonPoseableModel() {
override val rootPart = root.registerChildWithAllChildren("piloswine_male")
override val rootPart = root.registerChildWithAllChildren("piloswine")
override val portraitScale = 3.0F
override val portraitTranslation = Vec3d(-0.6, -3.0, 0.0)
@@ -8,14 +8,17 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen2
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
class Porygon2Model(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BipedFrame {
@@ -25,18 +28,27 @@ class Porygon2Model(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bipe
override val rightLeg = getPart("leg_right")
override val portraitScale = 2.5F
override val portraitTranslation = Vec3d(-0.45, -0.8, 0.0)
override val portraitTranslation = Vec3d(-0.45, 0.5, 0.0)
override val profileScale = 1.1F
override val profileTranslation = Vec3d(0.0, 0.0, 0.0)
override val profileTranslation = Vec3d(0.0, 0.55, 0.0)
lateinit var sleep: PokemonPose
lateinit var standing: PokemonPose
lateinit var walk: PokemonPose
override fun registerPoses() {
val blink = quirk("blink") { bedrockStateful("porygon2", "blink").setPreventsIdle(false) }
sleep = registerPose(
poseType = PoseType.SLEEP,
idleAnimations = arrayOf(bedrock("porygon2", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink),
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("porygon2", "ground_idle")
@@ -46,16 +58,17 @@ class Porygon2Model(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bipe
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
quirks = arrayOf(blink),
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("porygon2", "ground_idle"),
BipedWalkAnimation(this, periodMultiplier = 0.6F, amplitudeMultiplier = 0.9F),
bedrock("porygon2", "ground_walk")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PoseableEntityState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("porygon2", "faint") else null
override fun getFaintAnimation(
pokemonEntity: PokemonEntity,
state: PoseableEntityState<PokemonEntity>
) = if (state.isPosedIn(standing, walk)) bedrockStateful("porygon2", "faint") else null
}
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen2
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
class SlugmaModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("slugma")
override val head = getPart("head")
override val portraitScale = 1.7F
override val portraitTranslation = Vec3d(-0.35, -0.3, 0.0)
override val profileScale = 0.75F
override val profileTranslation = Vec3d(0.0, 0.575, 0.0)
lateinit var sleep: PokemonPose
lateinit var standing: PokemonPose
lateinit var walk: PokemonPose
override fun registerPoses() {
val blink = quirk("blink") { bedrockStateful("slugma", "blink").setPreventsIdle(false) }
sleep = registerPose(
poseType = PoseType.SLEEP,
idleAnimations = arrayOf(bedrock("slugma", "ground_sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("slugma", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
bedrock("slugma", "ground_walk")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PoseableEntityState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("slugma", "faint") else null
}
@@ -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.client.render.models.blockbench.pokemon.gen3
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
class NosepassModel(root: ModelPart) : PokemonPoseableModel(), BipedFrame {
override val rootPart = root.registerChildWithAllChildren("nosepass")
override val leftLeg = getPart("leg_left")
override val rightLeg = getPart("leg_right")
override val portraitScale = 1.5F
override val portraitTranslation = Vec3d(-0.1, -0.1, 0.0)
override val profileScale = 0.75F
override val profileTranslation = Vec3d(0.0, 0.65, 0.0)
lateinit var sleep: PokemonPose
lateinit var standing: PokemonPose
lateinit var walk: PokemonPose
override fun registerPoses() {
sleep = registerPose(
poseType = PoseType.SLEEP,
idleAnimations = arrayOf(bedrock("nosepass", "ground_sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
idleAnimations = arrayOf(
bedrock("nosepass", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
idleAnimations = arrayOf(
bedrock("nosepass", "ground_walk"),
)
)
}
override fun getFaintAnimation(
pokemonEntity: PokemonEntity,
state: PoseableEntityState<PokemonEntity>
) = if (state.isNotPosedIn(sleep)) bedrockStateful("nosepass", "faint") else null
}
@@ -8,7 +8,9 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen4
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BimanualSwingAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
@@ -19,12 +21,14 @@ import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
class InfernapeModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BipedFrame {
class InfernapeModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BipedFrame, BimanualFrame {
override val rootPart = root.registerChildWithAllChildren("infernape")
override val head = getPart("head")
override val head = getPart("head_ai")
override val leftLeg = getPart("leg_left")
override val rightLeg = getPart("leg_right")
override val leftArm = getPart("arm_left")
override val rightArm = getPart("arm_right")
override val portraitScale = 1.8F
override val portraitTranslation = Vec3d(-0.65, 1.55, 0.0)
@@ -42,7 +46,7 @@ class InfernapeModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bip
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
singleBoneLook(),
//singleBoneLook(),
bedrock("infernape", "idle")
)
)
@@ -52,9 +56,10 @@ class InfernapeModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bip
poseTypes = MOVING_POSES,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
singleBoneLook(),
//singleBoneLook(),
bedrock("infernape", "idle"),
BipedWalkAnimation(this, periodMultiplier = 0.75F, amplitudeMultiplier = 0.8F)
BipedWalkAnimation(this, periodMultiplier = 0.75F, amplitudeMultiplier = 0.8F),
BimanualSwingAnimation(this, swingPeriodMultiplier = 0.6F, amplitudeMultiplier = 0.9F)
//bedrock("infernape", "ground_walk")
)
)
@@ -8,12 +8,15 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen4
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
class PorygonzModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
@@ -21,20 +24,34 @@ class PorygonzModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
override val head = getPart("head")
override val portraitScale = 2.4F
override val portraitTranslation = Vec3d(-0.1, 0.75, 0.0)
override val portraitTranslation = Vec3d(-0.1, 1.1, 0.0)
override val profileScale = 0.85F
override val profileTranslation = Vec3d(0.0, 0.6, 0.0)
lateinit var sleep: PokemonPose
lateinit var standing: PokemonPose
lateinit var walk: PokemonPose
override fun registerPoses() {
val blink = quirk("blink") { bedrockStateful("porygonz", "blink").setPreventsIdle(false) }
val twitch1 = quirk("twitch1") { bedrockStateful("porygonz", "twitch1").setPreventsIdle(false) }
val twitch2 = quirk("twitch2") { bedrockStateful("porygonz", "twitch2").setPreventsIdle(false) }
val twitch3 = quirk("twitch3") { bedrockStateful("porygonz", "twitch3").setPreventsIdle(false) }
val twitch4 = quirk("twitch4") { bedrockStateful("porygonz", "twitch4").setPreventsIdle(false) }
val twitch5 = quirk("twitch5") { bedrockStateful("porygonz", "twitch5").setPreventsIdle(false) }
val twitch6 = quirk("twitch6") { bedrockStateful("porygonz", "twitch6").setPreventsIdle(false) }
sleep = registerPose(
poseType = PoseType.SLEEP,
quirks = arrayOf(twitch1, twitch2, twitch3, twitch4, twitch5, twitch6),
idleAnimations = arrayOf(bedrock("porygonz", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = UI_POSES + STATIONARY_POSES,
quirks = arrayOf(blink),
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink, twitch1, twitch2, twitch3, twitch4, twitch5, twitch6),
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("porygonz", "ground_idle")
@@ -44,11 +61,17 @@ class PorygonzModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
quirks = arrayOf(blink),
quirks = arrayOf(blink, twitch1, twitch2, twitch3, twitch4, twitch5, twitch6),
transformTicks = 10,
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("porygonz", "ground_idle")
bedrock("porygonz", "ground_walk")
)
)
}
override fun getFaintAnimation(
pokemonEntity: PokemonEntity,
state: PoseableEntityState<PokemonEntity>
) = if (state.isPosedIn(standing, walk)) bedrockStateful("porygonz", "faint") else null
}
@@ -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.client.render.models.blockbench.pokemon.gen4
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
class ProbopassModel(root: ModelPart) : PokemonPoseableModel() {
override val rootPart = root.registerChildWithAllChildren("probopass")
override val portraitScale = 1F
override val portraitTranslation = Vec3d(0.0, 0.425, 0.0)
override val profileScale = 0.75F
override val profileTranslation = Vec3d(0.0, 0.7, 0.0)
lateinit var sleep: PokemonPose
lateinit var standing: PokemonPose
lateinit var walk: PokemonPose
override fun registerPoses() {
val blink = quirk("blink") { bedrockStateful("probopass", "blink").setPreventsIdle(false) }
sleep = registerPose(
poseType = PoseType.SLEEP,
idleAnimations = arrayOf(bedrock("probopass", "ground_sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
bedrock("probopass", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
bedrock("probopass", "ground_walk"),
)
)
}
override fun getFaintAnimation(
pokemonEntity: PokemonEntity,
state: PoseableEntityState<PokemonEntity>
) = if (state.isNotPosedIn(sleep)) bedrockStateful("probopass", "faint") else null
}
@@ -23,15 +23,10 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
class GolurkModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BipedFrame, BimanualFrame {
class GolurkModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("golurk")
override val head = getPart("head")
override val leftArm = getPart("arm_left")
override val rightArm = getPart("arm_right")
override val leftLeg = getPart("leg_left")
override val rightLeg = getPart("leg_right")
override val portraitScale = 3.0F
override val portraitTranslation = Vec3d(-0.3, 5.4, 0.0)
@@ -8,9 +8,11 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen5
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
@@ -45,4 +47,8 @@ class KlangModel(root: ModelPart) : PokemonPoseableModel() {
)
)
}
override fun getFaintAnimation(
pokemonEntity: PokemonEntity,
state: PoseableEntityState<PokemonEntity>
) = if (state.isPosedIn(standing, walk)) bedrockStateful("klang", "faint") else null
}
@@ -8,9 +8,11 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen5
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
@@ -45,4 +47,8 @@ class KlinkModel(root: ModelPart) : PokemonPoseableModel() {
)
)
}
override fun getFaintAnimation(
pokemonEntity: PokemonEntity,
state: PoseableEntityState<PokemonEntity>
) = if (state.isPosedIn(standing, walk)) bedrockStateful("klink", "faint") else null
}
@@ -8,9 +8,11 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen5
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
@@ -45,4 +47,8 @@ class KlinklangModel(root: ModelPart) : PokemonPoseableModel() {
)
)
}
override fun getFaintAnimation(
pokemonEntity: PokemonEntity,
state: PoseableEntityState<PokemonEntity>
) = if (state.isPosedIn(standing, walk)) bedrockStateful("klinklang", "faint") else null
}
@@ -17,7 +17,7 @@ import net.minecraft.util.math.Vec3d
class SerperiorModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("serperior")
override val head = getPart("head")
override val head = getPart("head_ai")
override val portraitScale = 2.5F
override val portraitTranslation = Vec3d(-0.3, 1.25, 0.0)
@@ -33,7 +33,7 @@ class SerperiorModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
idleAnimations = arrayOf(
// singleBoneLook(),
singleBoneLook(),
bedrock("serperior", "ground_idle")
)
)
@@ -42,7 +42,7 @@ class SerperiorModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
idleAnimations = arrayOf(
// singleBoneLook(),
singleBoneLook(),
bedrock("serperior", "ground_walk")
)
)
@@ -19,7 +19,7 @@ import net.minecraft.util.math.Vec3d
class ServineModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BipedFrame, BimanualFrame {
override val rootPart = root.registerChildWithAllChildren("servine")
override val head = getPart("head")
override val head = getPart("head_ai")
override val rightArm = getPart("arm_right")
override val leftArm = getPart("arm_left")
override val rightLeg = getPart("leg_right")
@@ -48,7 +48,7 @@ class ServineModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bipe
transformTicks = 10,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
// singleBoneLook(),
singleBoneLook(),
bedrock("servine", "ground_idle")
)
)
@@ -59,7 +59,7 @@ class ServineModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bipe
transformTicks = 10,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
// singleBoneLook(),
singleBoneLook(),
bedrock("servine", "ground_walk")
)
)
@@ -19,7 +19,7 @@ import net.minecraft.util.math.Vec3d
class SnivyModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BipedFrame, BimanualFrame {
override val rootPart = root.registerChildWithAllChildren("snivy")
override val head = getPart("head")
override val head = getPart("head_ai")
override val rightArm = getPart("arm_right")
override val leftArm = getPart("arm_left")
override val rightLeg = getPart("leg_right")
@@ -48,7 +48,7 @@ class SnivyModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BipedF
transformTicks = 10,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
// singleBoneLook(),
singleBoneLook(),
bedrock("snivy", "ground_idle")
)
)
@@ -59,7 +59,7 @@ class SnivyModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BipedF
transformTicks = 10,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
// singleBoneLook(),
singleBoneLook(),
bedrock("snivy", "ground_walk")
)
)
@@ -46,7 +46,6 @@ class FroakieModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Biped
poseType = PoseType.SLEEP,
transformTicks = 10,
quirks = arrayOf(blink),
condition = { !it.isBattling },
idleAnimations = arrayOf(bedrock("froakie", "sleep"))
)
@@ -46,7 +46,6 @@ class FrogadierModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bip
poseType = PoseType.SLEEP,
transformTicks = 10,
quirks = arrayOf(blink),
condition = { !it.isBattling },
idleAnimations = arrayOf(bedrock("frogadier", "sleep"))
)
@@ -49,7 +49,6 @@ class GreninjaModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bipe
poseType = PoseType.SLEEP,
transformTicks = 10,
quirks = arrayOf(blink),
condition = { !it.isBattling },
transformedParts = arrayOf(
shurikenleft.asTransformed().withVisibility(visibility = false),
shurikenright.asTransformed().withVisibility(visibility = false)
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen9
import com.cobblemon.mod.common.client.render.models.blockbench.asTransformed
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.QuadrupedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
@@ -16,14 +17,12 @@ import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
class IronleavesModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame, QuadrupedFrame {
class IronleavesModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("ironleaves")
override val head = getPart("head")
override val foreLeftLeg = getPart("leg_front_left")
override val foreRightLeg = getPart("leg_front_right")
override val hindLeftLeg = getPart("leg_back_left")
override val hindRightLeg = getPart("leg_back_right")
val neck_sword_left = getPart("neck_sword_left")
val neck_sword_right = getPart("neck_sword_right")
val sword_head = getPart("sword_head")
override val portraitScale = 3.0F
override val portraitTranslation = Vec3d(-0.9, 2.2, 0.0)
@@ -43,6 +42,11 @@ class IronleavesModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Q
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
transformedParts = arrayOf(
neck_sword_left.asTransformed().withVisibility(visibility = false),
neck_sword_right.asTransformed().withVisibility(visibility = false),
sword_head.asTransformed().withVisibility(visibility = false)
),
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("ironleaves", "ground_idle")
@@ -51,6 +55,11 @@ class IronleavesModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Q
sleep = registerPose(
poseType = PoseType.SLEEP,
transformedParts = arrayOf(
neck_sword_left.asTransformed().withVisibility(visibility = false),
neck_sword_right.asTransformed().withVisibility(visibility = false),
sword_head.asTransformed().withVisibility(visibility = false)
),
idleAnimations = arrayOf(bedrock("ironleaves", "sleep"))
)
@@ -59,6 +68,11 @@ class IronleavesModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Q
poseTypes = PoseType.MOVING_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
transformedParts = arrayOf(
neck_sword_left.asTransformed().withVisibility(visibility = false),
neck_sword_right.asTransformed().withVisibility(visibility = false),
sword_head.asTransformed().withVisibility(visibility = false)
),
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("ironleaves", "ground_walk")
@@ -28,7 +28,7 @@ class WalkingwakeModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame,
override val rightLeg = getPart("leg_right")
override val portraitScale = 2.2F
override val portraitTranslation = Vec3d(-2.35, 2.4, 0.0)
override val portraitTranslation = Vec3d(-2.5, 2.4, 0.0)
override val profileScale = 0.35F
override val profileTranslation = Vec3d(0.0, 1.2, 0.0)
@@ -379,8 +379,10 @@ object PokemonModelRepository : VaryingModelRepository<PokemonEntity, PokemonPos
inbuilt("snivy", ::SnivyModel)
inbuilt("servine", ::ServineModel)
inbuilt("serperior", ::SerperiorModel)
inbuilt("slugma", ::SlugmaModel)
inbuilt("magcargo", ::MagcargoModel)
inbuilt("nosepass", ::NosepassModel)
inbuilt("probopass", ::ProbopassModel)
// Unhooked because the families are not completed
// inbuilt("sprigatito", ::SprigatitoModel)
@@ -27,10 +27,8 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.isLookingAt
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.DoubleRange
import com.cobblemon.mod.common.util.math.geometry.getOrigin
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.math.remap
import java.util.Vector
import kotlin.math.max
import kotlin.math.min
import kotlin.math.tan
@@ -151,7 +149,7 @@ class PokemonRenderer(
val clientDelegate = entity.delegate as PokemonClientDelegate
val pokemonPosition = entity.pos.add(0.0, entity.height / 2.0 * clientDelegate.entityScaleModifier.toDouble(), 0.0)
val beamSourcePosition = if (beamTarget is EmptyPokeBallEntity) {
(beamTarget.delegate as PokeBallPoseableState).locatorStates["beam"]?.matrix?.getOrigin() ?: beamTarget.pos
(beamTarget.delegate as PokeBallPoseableState).locatorStates["beam"]?.getOrigin() ?: beamTarget.pos
// beamTarget.pos.let { it.add(pokemonPosition.subtract(it).normalize().multiply(0.4, 0.0, 0.4)) }
} else {
beamTarget as PlayerEntity
@@ -51,6 +51,11 @@ class ClientStorageManager {
}
}
fun switchToPokemon(pokemon: UUID) {
selectedPokemon = pokemon
selectedSlot = myParty.indexOf(myParty.findByUUID(pokemon))
}
fun checkSelectedPokemon() {
if (selectedSlot == -1) {
val pokemon = myParty.firstOrNull { it != null } ?: return
@@ -77,7 +82,7 @@ class ClientStorageManager {
}
fun locatePokemon(storeID: UUID, pokemonID: UUID): Pokemon? {
partyStores[storeID]?.let { return it.findByUUID(pokemonID) } ?: return pcStores[storeID]?.findByUUID(pokemonID)
return partyStores[storeID]?.findByUUID(pokemonID) ?: pcStores[storeID]?.findByUUID(pokemonID)
}
fun createParty(mine: Boolean, uuid: UUID, slots: Int) {
@@ -144,4 +149,4 @@ class ClientStorageManager {
partyStores.clear()
pcStores.clear()
}
}
}
@@ -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.command
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
import com.cobblemon.mod.common.api.storage.pc.link.PermissiblePcLink
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.net.messages.client.storage.pc.OpenPCPacket
import com.cobblemon.mod.common.util.isInBattle
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.pc
import com.cobblemon.mod.common.util.permission
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.context.CommandContext
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType
import net.minecraft.server.command.CommandManager.*
import net.minecraft.server.command.ServerCommandSource
object PcCommand {
private const val NAME = "pc"
private val IN_BATTLE_EXCEPTION = SimpleCommandExceptionType(lang("pc.inbattle").red())
fun register(dispatcher : CommandDispatcher<ServerCommandSource>) {
dispatcher.register(literal(NAME)
.permission(CobblemonPermissions.PC)
.executes(this::execute)
)
}
private fun execute(context: CommandContext<ServerCommandSource>): Int {
val player = context.source.playerOrThrow
val pc = player.pc()
if (player.isInBattle()) {
throw IN_BATTLE_EXCEPTION.create()
}
PCLinkManager.addLink(PermissiblePcLink(pc, player, CobblemonPermissions.PC))
OpenPCPacket(pc.uuid).sendToPlayer(player)
return Command.SINGLE_SUCCESS
}
}
@@ -0,0 +1,133 @@
/*
* 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.command
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.client.settings.ServerSettings
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
import com.cobblemon.mod.common.net.messages.client.storage.RemoveClientPokemonPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.*
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.IntegerArgumentType
import com.mojang.brigadier.context.CommandContext
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType
import net.minecraft.command.argument.EntityArgumentType
import net.minecraft.server.command.CommandManager
import net.minecraft.server.command.CommandManager.literal
import net.minecraft.server.command.ServerCommandSource
import net.minecraft.server.network.ServerPlayerEntity
/**
* Send Pokemon to the PC.
*
* Two similar commands with the syntax:
*
* `/pokebox <player> <slot> [box]` Sends a single Pokemon to the PC, allowing for a specific box to be selected.
* If a box is selected and is full, no action will take place.
*
* `/pokeboxall <player> [box]` Sends all party pokemon to the PC, allowing for a specific box to be selected.
* If a box is selected and would not beable to house all of the party Pokemon, no action will take place.
*/
object PokeboxCommand {
private val BOX_DOES_NOT_EXIST = { boxNo: Int -> commandLang("pokebox.box_does_not_exist", boxNo) }
private val BOX_IS_FULL_EXCEPTION = { boxNo: Int -> commandLang("pokebox.box_is_full", boxNo) }
private val STORAGE_IS_FULL_EXCEPTION = commandLang("pokebox.storage_is_full")
private val LAST_POKE_MESSAGE = commandLang("pokebox.last_pokemon")
fun register(dispatcher: CommandDispatcher<ServerCommandSource>) {
dispatcher.register(literal("pokebox")
.permission(CobblemonPermissions.POKEBOX)
.then(CommandManager.argument("player", EntityArgumentType.player())
.then(CommandManager.argument("slot", PartySlotArgumentType.partySlot())
.then(CommandManager.argument("box", IntegerArgumentType.integer(1))
.executes { context ->
val player = context.player()
val pokemon = PartySlotArgumentType.getPokemonOf(context, "slot", player)
val box = IntegerArgumentType.getInteger(context, "box")
execute(context, player, listOf(pokemon), box)
})
.executes { context ->
val player = context.player()
val pokemon = PartySlotArgumentType.getPokemonOf(context, "slot", player)
execute(context, player, listOf(pokemon))
})
)
)
dispatcher.register(literal("pokeboxall")
.permission(CobblemonPermissions.POKEBOX)
.then(CommandManager.argument("player", EntityArgumentType.player())
.then(CommandManager.argument("box", IntegerArgumentType.integer(1))
.executes { context ->
val player = context.player()
val box = IntegerArgumentType.getInteger(context, "box")
execute(context, player, player.party().toList(), box)
})
.executes { context ->
val player = context.player()
execute(context, player, player.party().toList())
}))
}
private fun execute(
context: CommandContext<ServerCommandSource>,
player: ServerPlayerEntity,
pokemons: Collection<Pokemon>,
box: Int? = null,
): Int {
val playerPc = player.pc()
val playerParty = player.party()
// If specifying a box, first check that the box exists and can sufficiently hold all the pokemon to be moved.
if (box != null) {
if (playerPc.boxes.size < box) {
throw SimpleCommandExceptionType(BOX_DOES_NOT_EXIST(box).red()).create()
}
val pcBox = playerPc.boxes.get(box - 1)
if (pcBox.unoccupiedSlots < pokemons.size) {
throw SimpleCommandExceptionType(BOX_IS_FULL_EXCEPTION(box).red()).create()
}
}
// Operate in reverse so that the party "lead" pokemon would be kept
pokemons.reversed().forEach { pokemon ->
if (ServerSettings.preventCompletePartyDeposit && playerParty.occupied() == 1) {
context.source.sendFeedback(LAST_POKE_MESSAGE.red(), false)
return pokemons.size - 1
}
// If PCStore and PCBox both implemented PokemonStore we could make this code a lot cleaner via the same interface
val pcPosition = if (box == null) {
playerPc.getFirstAvailablePosition()
?: throw SimpleCommandExceptionType(STORAGE_IS_FULL_EXCEPTION.red()).create()
} else {
val pcBox = playerPc.boxes.get(box - 1)
pcBox.getFirstAvailablePosition()
?: throw SimpleCommandExceptionType(BOX_IS_FULL_EXCEPTION(box).red()).create()
}
playerParty.remove(pokemon)
playerPc[pcPosition] = pokemon
// Let the client(s) know about the change to party
playerParty.sendPacketToObservers(
RemoveClientPokemonPacket(player.party(), pokemon.uuid)
)
}
// Let the call know how many Pokemon were moved to the PC
return pokemons.size
}
}
@@ -50,8 +50,8 @@ object SpawnPokemon {
dispatcher.register(contextPositionCommand.alias(ALIAS))
val argumentPositionCommand = dispatcher.register(literal(AT_NAME)
.permission(CobblemonPermissions.SPAWN_POKEMON)
.then(argument(PROPERTIES, PokemonPropertiesArgumentType.properties())
.then(argument(POSITION, Vec3ArgumentType.vec3())
.then(argument(POSITION, Vec3ArgumentType.vec3())
.then(argument(PROPERTIES, PokemonPropertiesArgumentType.properties())
.executes { context -> execute(context, Vec3ArgumentType.getVec3(context, POSITION)) }
)
)
@@ -0,0 +1,96 @@
/*
* 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.command
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.spawning.CobblemonWorldSpawnerManager
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.context.AreaSpawningContext
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.util.alias
import com.cobblemon.mod.common.util.commandLang
import com.cobblemon.mod.common.util.permission
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.IntegerArgumentType
import com.mojang.brigadier.context.CommandContext
import net.minecraft.server.command.CommandManager
import net.minecraft.server.command.CommandManager.literal
import net.minecraft.server.command.ServerCommandSource
import net.minecraft.text.Text
/**
* Spawn Pokemon From Surrounding Pool
*
* `/spawnpokemonfrompool [amount]` or the alias `/forcespawn [amount]`
*
* This command can fail if the randomly selection spawn region has no possible [AreaSpawningContext]. For example if
* you are flying in the air
*/
object SpawnPokemonFromPool {
const val NAME = "spawnpokemonfrompool"
const val ALIAS = "forcespawn"
private val UNABLE_TO_SPAWN = commandLang("spawnpokemonfrompool.unable_to_spawn")
fun register(dispatcher: CommandDispatcher<ServerCommandSource>) {
val spawnPokemonFromPoolCommand = dispatcher.register(literal(NAME)
.permission(CobblemonPermissions.SPAWN_POKEMON)
.then(CommandManager.argument("amount", IntegerArgumentType.integer(1))
.executes { context -> execute(context, IntegerArgumentType.getInteger(context, "amount")) }
)
.executes { context -> execute(context, 1) }
)
dispatcher.register(spawnPokemonFromPoolCommand.alias(ALIAS))
}
private fun execute(context: CommandContext<ServerCommandSource>, amount: Int): Int {
val player = context.source.playerOrThrow
val spawner = CobblemonWorldSpawnerManager.spawnersForPlayers.getValue(player.uuid)
var spawnsTriggered = 0
// This could instead directly use a [Spawner] method if refactored, as it is currently it has
// entity counting coupled to the generation of a entity to spawn. Might be a good future change?
for (i in 1..amount) {
val spawnCause = SpawnCause(spawner = spawner, bucket = spawner.chooseBucket(), entity = spawner.getCauseEntity())
val area = spawner.getArea(spawnCause) ?: continue
val slice = spawner.prospector.prospect(spawner, area)
val contexts = spawner.resolver.resolve(spawner, spawner.contextCalculators, slice)
// This has a chance to fail, if you get a "slice" that has no associated contexts.
// but as it was selected at random by the Prospector, it could just be a miss which
// means two attempts to spawn in the same location can have differing results (which is expected for
// randomness).
if (contexts.isEmpty()) {
player.sendMessage(UNABLE_TO_SPAWN.red())
continue
}
val result = spawner.getSpawningSelector().select(spawner, contexts)
if (result == null) {
player.sendMessage(UNABLE_TO_SPAWN.red())
continue
}
val spawnAction = result.second.doSpawn(ctx = result.first)
spawnAction.entity.subscribe {
player.sendMessage(commandLang("spawnpokemonfrompool", it.displayName).green())
}
spawner.performSpawn(spawnAction)
spawnsTriggered++
}
return spawnsTriggered
}
}
@@ -8,8 +8,10 @@
package com.cobblemon.mod.common.command
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.moves.BenchedMove
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.pokemon.moves.LearnsetQuery
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.command.argument.MoveArgumentType
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
@@ -31,7 +33,8 @@ object TeachCommand {
private const val PLAYER = "player"
private const val SLOT = "slot"
private const val MOVE = "move"
private val ALREAD_KNOWS_EXCEPTION = Dynamic2CommandExceptionType { a, b -> commandLang("$NAME.already_knows", a, b).red() }
private val ALREADY_KNOWS_EXCEPTION = Dynamic2CommandExceptionType { a, b -> commandLang("$NAME.already_knows", a, b).red() }
private val CANT_LEARN_EXCEPTION = Dynamic2CommandExceptionType { a, b -> commandLang("$NAME.cant_learn", a, b).red() }
fun register(dispatcher : CommandDispatcher<ServerCommandSource>) {
val command = CommandManager.literal(NAME)
@@ -46,18 +49,31 @@ object TeachCommand {
}
private fun execute(context: CommandContext<ServerCommandSource>, player: ServerPlayerEntity) : Int {
val pokemon = PartySlotArgumentType.getPokemon(context, SLOT)
val pokemon = PartySlotArgumentType.getPokemonOf(context, SLOT, player)
val move = MoveArgumentType.getMove(context, MOVE)
if (pokemon.moveSet.getMoves().any { it.template == move } || pokemon.benchedMoves.any { it.moveTemplate == move }) {
throw ALREAD_KNOWS_EXCEPTION.create(pokemon.displayName, move.displayName)
if (!Cobblemon.permissionValidator.hasPermission(context.source, CobblemonPermissions.TEACH_BYPASS_LEARNSET) && !LearnsetQuery.ANY.canLearn(move, pokemon.form.moves)) {
throw CANT_LEARN_EXCEPTION.create(pokemon.displayName, move.displayName)
}
if (pokemon.moveSet.getMoves().any { it.template == move } || pokemon.benchedMoves.any { it.moveTemplate == move }) {
throw ALREADY_KNOWS_EXCEPTION.create(pokemon.displayName, move.displayName)
}
if (pokemon.moveSet.hasSpace()) {
pokemon.moveSet.add(move.create())
}
else {
pokemon.benchedMoves.add(BenchedMove(move, 0))
}
context.source.sendFeedback(commandLang(NAME, pokemon.species.translatedName, player.name, move.displayName), true)
val pokemonLearntMessage = commandLang(NAME, pokemon.species.translatedName, player.name, move.displayName)
context.source.sendFeedback(pokemonLearntMessage, true)
if (context.source.player?.equals(player) != true) {
player.sendMessage(pokemonLearntMessage)
}
return Command.SINGLE_SUCCESS
}
@@ -23,7 +23,7 @@ import java.util.concurrent.CompletableFuture
import net.minecraft.command.CommandSource
import net.minecraft.server.command.ServerCommandSource
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
class PartySlotArgumentType : ArgumentType<Int> {
override fun parse(reader: StringReader): Int {
@@ -21,11 +21,15 @@ import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatureAssignments
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.SpawnDetailPresets
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import com.cobblemon.mod.common.pokemon.SpeciesAdditions
import com.cobblemon.mod.common.pokemon.properties.PropertiesCompletionProvider
import com.cobblemon.mod.common.util.getServer
import com.cobblemon.mod.common.util.ifClient
import dev.architectury.registry.ReloadListenerRegistry
import java.util.UUID
import net.minecraft.resource.ResourceManager
import net.minecraft.resource.ResourceType
import net.minecraft.resource.SynchronousResourceReloader
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
@@ -33,7 +37,8 @@ import net.minecraft.util.Identifier
object CobblemonDataProvider : DataProvider {
// Both Forge n Fabric keep insertion order so if a registry depends on another simply register it after
var canReload = true
internal var canReload = true
// Both Forge n Fabric keep insertion order so if a registry depends on another simply register it after
private val registries = mutableListOf<DataRegistry>()
private val synchronizedPlayerIds = mutableListOf<UUID>()
@@ -53,7 +58,15 @@ object CobblemonDataProvider : DataProvider {
CobblemonSpawnPools.load()
CobblemonEvents.PLAYER_QUIT.subscribe { synchronizedPlayerIds.remove(it.uuid) }
ifClient(){
ReloadListenerRegistry.register(ResourceType.CLIENT_RESOURCES, SimpleResourceReloader(ResourceType.CLIENT_RESOURCES))
}
ReloadListenerRegistry.register(ResourceType.SERVER_DATA, SimpleResourceReloader(ResourceType.SERVER_DATA))
CobblemonEvents.PLAYER_QUIT.subscribe {
UnlockReloadPacket().sendToPlayer(it)
synchronizedPlayerIds.remove(it.uuid)
}
}
override fun <T : DataRegistry> register(registry: T): T {
@@ -61,7 +74,6 @@ object CobblemonDataProvider : DataProvider {
if (this.registries.isEmpty()) {
LOGGER.info("Note: Cobblemon data registries are only loaded once per server instance as Pokémon species are not safe to reload.")
}
ReloadListenerRegistry.register(registry.type, SimpleResourceReloader(registry))
this.registries.add(registry)
LOGGER.info("Registered the {} registry", registry.id.toString())
LOGGER.debug("Registered the {} registry of class {}", registry.id.toString(), registry::class.qualifiedName)
@@ -88,11 +100,18 @@ object CobblemonDataProvider : DataProvider {
}
}
private class SimpleResourceReloader(private val registry: DataRegistry) : SynchronousResourceReloader {
private class SimpleResourceReloader(private val type: ResourceType) : SynchronousResourceReloader {
override fun reload(manager: ResourceManager) {
if (canReload) {
this.registry.reload(manager)
// Check for a server running, this is due to the create a world screen triggering datapack reloads, these are fine to happen as many times as needed as players may be in the process of adding their datapacks.
val isInGame = getServer() != null
if (isInGame && this.type == ResourceType.SERVER_DATA && !canReload) {
return
}
registries.filter { it.type == this.type }
.forEach { it.reload(manager) }
if (isInGame && this.type == ResourceType.SERVER_DATA) {
canReload = false
}
}
}
}
}
@@ -43,7 +43,6 @@ import com.cobblemon.mod.common.util.setPositionSafely
import dev.architectury.extensions.network.EntitySpawnExtension
import dev.architectury.networking.NetworkManager
import java.util.concurrent.CompletableFuture
import java.util.concurrent.atomic.AtomicInteger
import net.minecraft.entity.EntityDimensions
import net.minecraft.entity.EntityPose
import net.minecraft.entity.EntityType
@@ -293,11 +292,11 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, EntitySpawnExtension {
}
private fun shakeBall(task: ScheduledTask, rollsRemaining: Int, captureResult: CaptureContext) {
if (capturingPokemon?.isAlive != true) {
discard()
}
if (!isAlive) {
if (this.capturingPokemon?.isAlive != true || !this.isAlive || this.owner == null|| owner?.isAlive != true) {
if (this.capturingPokemon?.isAlive == true) {
this.breakFree()
}
this.discard()
task.expire()
return
}
@@ -312,14 +311,17 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, EntitySpawnExtension {
val player = this.owner as? ServerPlayerEntity ?: return
afterOnMain(seconds = 1F) {
pokemon.discard()
discard()
captureFuture.complete(true)
val party = Cobblemon.storage.getParty(player.uuid)
pokemon.pokemon.caughtBall = pokeBall
pokeBall.effects.forEach { effect -> effect.apply(player, pokemon.pokemon) }
party.add(pokemon.pokemon)
CobblemonEvents.POKEMON_CAPTURED.post(PokemonCapturedEvent(pokemon.pokemon, player))
// Dupes occurred by double-adding Pokémon, this hopefully prevents it triple-condom style
if (pokemon.pokemon.isWild() && pokemon.isAlive && !captureFuture.isDone) {
pokemon.discard()
discard()
captureFuture.complete(true)
val party = Cobblemon.storage.getParty(player.uuid)
pokemon.pokemon.caughtBall = pokeBall
pokeBall.effects.forEach { effect -> effect.apply(player, pokemon.pokemon) }
party.add(pokemon.pokemon)
CobblemonEvents.POKEMON_CAPTURED.post(PokemonCapturedEvent(pokemon.pokemon, player))
}
}
return
} else {
@@ -415,4 +417,7 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, EntitySpawnExtension {
override fun getPoseType(): PoseType {
return PoseType.NONE
}
override fun canUsePortals() = false
}
@@ -53,9 +53,7 @@ import com.cobblemon.mod.common.util.*
import dev.architectury.extensions.network.EntitySpawnExtension
import dev.architectury.networking.NetworkManager
import net.minecraft.block.BlockState
import net.minecraft.entity.EntityDimensions
import net.minecraft.entity.EntityPose
import net.minecraft.entity.EntityType
import net.minecraft.entity.*
import net.minecraft.entity.ai.control.MoveControl
import net.minecraft.entity.ai.goal.EatGrassGoal
import net.minecraft.entity.ai.goal.Goal
@@ -75,6 +73,7 @@ import net.minecraft.item.Items
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ChunkTicketType
import net.minecraft.server.world.ServerWorld
import net.minecraft.sound.SoundCategory
import net.minecraft.sound.SoundEvent
@@ -85,6 +84,7 @@ import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.ChunkPos
import net.minecraft.util.math.Vec3d
import net.minecraft.world.World
import net.minecraft.world.event.GameEvent
@@ -98,7 +98,7 @@ class PokemonEntity(
world: World,
pokemon: Pokemon = Pokemon(),
type: EntityType<out PokemonEntity> = CobblemonEntities.POKEMON.get(),
) : TameableShoulderEntity(type, world), EntitySpawnExtension, Poseable {
) : TameableShoulderEntity(type, world), EntitySpawnExtension, Poseable, Shearable {
val removalObservable = SimpleObservable<RemovalReason?>()
/** A list of observable subscriptions related to this entity that need to be cleaned up when the entity is removed. */
val subscriptions = mutableListOf<ObservableSubscription<*>>()
@@ -270,7 +270,7 @@ class PokemonEntity(
* Prevents fire type Pokémon from taking fire damage.
*/
override fun isFireImmune(): Boolean {
return FIRE in pokemon.types || behaviour.moving.swim.canSwimInLava
return FIRE in pokemon.types || !behaviour.moving.swim.hurtByLava
}
/**
@@ -302,8 +302,11 @@ class PokemonEntity(
return super.isInvulnerableTo(damageSource)
}
var beingRecalled = false;
fun recallWithAnimation(): CompletableFuture<Pokemon> {
val owner = owner
beingRecalled = true
val future = CompletableFuture<Pokemon>()
if (phasingTargetId.get() == -1 && owner != null) {
owner.getWorld().playSoundServer(pos, CobblemonSounds.POKE_BALL_RECALL.get(), volume = 0.2F)
@@ -317,7 +320,7 @@ class PokemonEntity(
pokemon.recall()
future.complete(pokemon)
}
future.whenComplete{ _, _ -> beingRecalled = false }
return future
}
@@ -419,40 +422,13 @@ class PokemonEntity(
override fun interactMob(player: PlayerEntity, hand: Hand) : ActionResult {
val itemStack = player.getStackInHand(hand)
if (player is ServerPlayerEntity) {
if (itemStack.isOf(Items.SHEARS)) {
val feature = pokemon.getFeature<FlagSpeciesFeature>(DataKeys.HAS_BEEN_SHEARED)
val isShearable = feature?.enabled == false
if (isShearable) {
feature!!
world.playSoundFromEntity(
null,
this,
SoundEvents.ENTITY_SHEEP_SHEAR,
SoundCategory.PLAYERS,
1.0F,
1.0F
)
val i = 1 + random.nextInt(3)
for (j in 0 until i) {
val itemEntity = this.dropItem(Items.WHITE_WOOL, 1)
if (itemEntity != null) {
itemEntity.velocity = itemEntity.velocity.add(
((random.nextFloat() - random.nextFloat()) * 0.1f).toDouble(),
(random.nextFloat() * 0.05f).toDouble(),
((random.nextFloat() - random.nextFloat()) * 0.1f).toDouble()
)
}
}
this.emitGameEvent(GameEvent.SHEAR, player)
itemStack.damage(1, player) { it.sendToolBreakStatus(hand) }
feature.enabled = true
pokemon.markFeatureDirty(feature)
pokemon.updateAspects()
return ActionResult.SUCCESS
}
if (itemStack.isOf(Items.SHEARS) && this.isShearable) {
this.sheared(SoundCategory.PLAYERS)
this.emitGameEvent(GameEvent.SHEAR, player)
itemStack.damage(1, player) { it.sendToolBreakStatus(hand) }
return ActionResult.SUCCESS
}
if (itemStack.isOf(Items.BUCKET)) {
else if (itemStack.isOf(Items.BUCKET)) {
if (pokemon.getFeature<FlagSpeciesFeature>(DataKeys.CAN_BE_MILKED) != null) {
world.playSoundFromEntity(
null,
@@ -646,9 +622,8 @@ class PokemonEntity(
returned.isEmpty -> lang("held_item.give", this.pokemon.displayName, giving.name)
else -> lang("held_item.replace", returned.name, this.pokemon.displayName, giving.name)
}
player.giveItemStack(returned)
player.giveOrDropItemStack(returned)
player.sendMessage(text)
this.world.playSoundServer(position = this.pos, sound = SoundEvents.ENTITY_ITEM_PICKUP, volume = 0.7F, pitch = 1.4F)
return true
}
@@ -704,7 +679,8 @@ class PokemonEntity(
}
override fun updatePostDeath() {
super.updatePostDeath()
// Do not invoke super we need to keep a tight lid on this due to the Thorium mod forcing the ticks to a max of 20 on server side if we invoke a field update here
// Client delegate is mimicking expected behavior on client end.
delegate.updatePostDeath()
}
@@ -860,4 +836,43 @@ class PokemonEntity(
override fun breed(world: ServerWorld, other: AnimalEntity) {}
override fun sheared(shearedSoundCategory: SoundCategory) {
this.world.playSoundFromEntity(null, this,SoundEvents.ENTITY_SHEEP_SHEAR, shearedSoundCategory, 1.0F, 1.0F)
val feature = this.pokemon.getFeature<FlagSpeciesFeature>(DataKeys.HAS_BEEN_SHEARED) ?: return
feature.enabled = true
this.pokemon.markFeatureDirty(feature)
this.pokemon.updateAspects()
val i = this.random.nextInt(3)
for (j in 0 until i) {
val itemEntity = this.dropItem(Items.WHITE_WOOL, 1) ?: return
itemEntity.velocity = itemEntity.velocity.add(
((this.random.nextFloat() - this.random.nextFloat()) * 0.1f).toDouble(),
(this.random.nextFloat() * 0.05f).toDouble(),
((this.random.nextFloat() - this.random.nextFloat()) * 0.1f).toDouble()
)
}
}
override fun isShearable(): Boolean {
val feature = this.pokemon.getFeature<FlagSpeciesFeature>(DataKeys.HAS_BEEN_SHEARED) ?: return false
return !this.isBusy && !this.pokemon.isFainted() && !feature.enabled
}
override fun canUsePortals() = false
override fun onStoppedTrackingBy(player: ServerPlayerEntity?) {
if (player != null) {
if(this.ownerUuid == player.uuid) {
if(!beingRecalled) {
// We're loading the chunk because it creates a ghost if we don't.
val chunkPos = ChunkPos(BlockPos(x, y, z))
(world as ServerWorld).chunkManager.addTicket(
ChunkTicketType.POST_TELEPORT, chunkPos, 0,
id
)
pokemon.recall()
}
}
}
}
}
@@ -176,7 +176,7 @@ class PokemonServerDelegate : PokemonSideDelegate {
}
++entity.deathTime
if (entity.deathTime == 60) {
if (entity.deathTime == 30) {
val owner = entity.owner
if (owner != null) {
entity.world.playSoundServer(owner.pos, CobblemonSounds.POKE_BALL_RECALL.get(), volume = 0.2F)
@@ -185,7 +185,7 @@ class PokemonServerDelegate : PokemonSideDelegate {
}
}
if (entity.deathTime == 120) {
if (entity.deathTime == 60) {
if (entity.owner == null) {
entity.world.sendEntityStatus(entity, 60.toByte()) // Sends smoke effect
(entity.drops ?: entity.pokemon.form.drops).drop(entity, entity.world as ServerWorld, entity.pos, entity.killer)
@@ -77,7 +77,8 @@ class PokemonNavigation(val world: World, val pokemonEntity: PokemonEntity) : Mo
override fun tick() {
super.tick()
val node = getCurrentPath()?.lastNode
val currentPath = getCurrentPath()
val node = if (currentPath == null || currentPath.isFinished) null else currentPath.lastNode
val isFlying = pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)
val canWalk = pokemonEntity.behaviour.moving.walk.canWalk
@@ -35,9 +35,7 @@ class PokemonInBattleMovementGoal(val entity: PokemonEntity, val range: Int) : G
val closestPokemonEntity = getClosestPokemonEntity()
if (closestPokemonEntity != null) {
entity.navigation.stop()
// entity.lookAt(EntityAnchorArgumentType.EntityAnchor.EYES, closestPokemonEntity.eyePos)
entity.lookAtEntity(closestPokemonEntity, 30.0f, 30.0f)
// entity.lookControl.lookAt(closestPokemonEntity.x, closestPokemonEntity.y, closestPokemonEntity.z)
entity.lookControl.lookAt(closestPokemonEntity.x, closestPokemonEntity.eyeY, closestPokemonEntity.z)
}
}
}
@@ -45,7 +45,7 @@ class PokemonMoveIntoFluidGoal(private val mob: PokemonEntity) : Goal() {
override fun start() {
val appropriateFluids = mutableListOf<TagKey<Fluid>>()
val swim = mob.behaviour.moving.swim
if (swim.canSwimInLava) {
if (swim.canSwimInLava && !swim.hurtByLava) {
appropriateFluids.add(FluidTags.LAVA)
}
if (swim.canSwimInWater) {
@@ -36,6 +36,10 @@ class PokemonItem : CobblemonItem(Settings().maxCount(1)) {
}
}
fun getSpeciesAndAspects(stack: ItemStack): Pair<Species, Set<String>>? {
return (species(stack) ?: return null) to (aspects(stack) ?: return null)
}
fun asRenderablePokemon(stack: ItemStack): RenderablePokemon? = this.asPokemon(stack)?.asRenderablePokemon()
private fun species(stack: ItemStack): Species? {
@@ -20,7 +20,8 @@ class LinkCableItem : PokemonInteractiveItem(Settings().group(CobblemonItemGroup
val pokemon = entity.pokemon
pokemon.evolutions.filterIsInstance<TradeEvolution>().forEach { evolution ->
// If an evolution is possible non-optional or has been successfully queued we will consume the item and stop
if (evolution.evolve(pokemon)) {
// validate requirements to respect required held items and such.
if (evolution.requirements.all { it.check(pokemon) } && evolution.evolve(pokemon)) {
this.consumeItem(player, stack)
return true
}
@@ -86,6 +86,7 @@ class PokemonDTO : Encodable, Decodable {
this.status = pokemon.status?.status?.name
this.state = pokemon.state
this.caughtBall = pokemon.caughtBall.name
this.benchedMoves = pokemon.benchedMoves
this.aspects = pokemon.aspects
evolutionBuffer = PacketByteBuf(Unpooled.buffer())
pokemon.evolutionProxy.saveToBuffer(evolutionBuffer, toClient)
@@ -13,10 +13,6 @@ import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.battles.MoveTarget
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readSizedInt
import com.cobblemon.mod.common.util.writeSizedInt
import net.minecraft.network.PacketByteBuf
class MovesRegistrySyncPacket : DataRegistrySyncPacket<MoveTemplate> {
@@ -24,8 +20,8 @@ class MovesRegistrySyncPacket : DataRegistrySyncPacket<MoveTemplate> {
constructor(moves: List<MoveTemplate>): super(moves)
override fun encodeEntry(buffer: PacketByteBuf, entry: MoveTemplate) {
buffer.writeSizedInt(IntSize.U_SHORT, entry.id)
buffer.writeString(entry.name)
buffer.writeInt(entry.num)
buffer.writeString(entry.elementalType.name)
buffer.writeString(entry.damageCategory.name)
buffer.writeDouble(entry.power)
@@ -39,8 +35,8 @@ class MovesRegistrySyncPacket : DataRegistrySyncPacket<MoveTemplate> {
}
override fun decodeEntry(buffer: PacketByteBuf): MoveTemplate {
val id = buffer.readSizedInt(IntSize.U_SHORT)
val name = buffer.readString()
val num = buffer.readInt()
val type = ElementalTypes.getOrException(buffer.readString())
val damageCategory = DamageCategories.getOrException(buffer.readString())
val power = buffer.readDouble()
@@ -53,7 +49,7 @@ class MovesRegistrySyncPacket : DataRegistrySyncPacket<MoveTemplate> {
repeat(buffer.readVarInt()) {
effectChances += buffer.readDouble()
}
return MoveTemplate(name, type, damageCategory, power, target, accuracy, pp, priority, critRatio, effectChances.toTypedArray()).apply { this.id = id }
return MoveTemplate(name, num, type, damageCategory, power, target, accuracy, pp, priority, critRatio, effectChances.toTypedArray())
}
override fun synchronizeDecoded(entries: Collection<MoveTemplate>) {
@@ -0,0 +1,17 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.data
import com.cobblemon.mod.common.api.net.NetworkPacket
import net.minecraft.network.PacketByteBuf
internal class UnlockReloadPacket : NetworkPacket {
override fun encode(buffer: PacketByteBuf) {}
override fun decode(buffer: PacketByteBuf) {}
}
@@ -9,6 +9,8 @@
package com.cobblemon.mod.common.net.serverhandling.storage.party
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
import com.cobblemon.mod.common.net.messages.server.storage.pc.ReleasePCPokemonPacket
import com.cobblemon.mod.common.net.serverhandling.ServerPacketHandler
@@ -22,6 +24,15 @@ object ReleasePCPokemonHandler : ServerPacketHandler<ReleasePCPokemonPacket> {
return // Not what we expected
}
pc.remove(packet.position)
CobblemonEvents.POKEMON_RELEASED_EVENT_PRE.postThen(
event = ReleasePokemonEvent.Pre(player, pokemon, pc),
ifSucceeded = { preEvent ->
pc.remove(packet.position)
CobblemonEvents.POKEMON_RELEASED_EVENT_POST.post(ReleasePokemonEvent.Post(player, pokemon, pc))
},
ifCanceled = { preEvent ->
pc[packet.position] = pokemon
}
)
}
}
@@ -9,6 +9,8 @@
package com.cobblemon.mod.common.net.serverhandling.storage.pc
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
import com.cobblemon.mod.common.client.settings.ServerSettings
import com.cobblemon.mod.common.net.messages.server.storage.party.ReleasePartyPokemonPacket
import com.cobblemon.mod.common.net.serverhandling.ServerPacketHandler
@@ -23,10 +25,19 @@ object ReleasePartyPokemonHandler : ServerPacketHandler<ReleasePartyPokemonPacke
return // Desync
}
if (ServerSettings.preventCompletePartyDeposit && party.filterNotNull().size <= 1) {
return // Don't allow empty party
}
CobblemonEvents.POKEMON_RELEASED_EVENT_PRE.postThen(
event = ReleasePokemonEvent.Pre(player, pokemon, party),
ifSucceeded = { preEvent ->
if (ServerSettings.preventCompletePartyDeposit && party.filterNotNull().size <= 1) {
return // Don't allow empty party
}
party.remove(pokemon)
party.remove(pokemon)
CobblemonEvents.POKEMON_RELEASED_EVENT_POST.post(ReleasePokemonEvent.Post(player, pokemon, party))
},
ifCanceled = { preEvent ->
party[packet.position] = pokemon
}
)
}
}

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