Compare commits

...
129 Commits
Author SHA1 Message Date
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
1316 changed files with 56114 additions and 14811 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
@@ -98,7 +96,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 +268,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
}
/**
@@ -419,40 +417,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 +617,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 +674,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 +831,27 @@ 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
}
@@ -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