mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-11-01 13:58:54 -05:00
Compare commits
44
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72999ff600 | ||
|
|
ec18bb9154 | ||
|
|
1e1fba5f7f | ||
|
|
69497bc3cc | ||
|
|
64f7556bdc | ||
|
|
9fea42bce0 | ||
|
|
7abe83c413 | ||
|
|
2d70f48176 | ||
|
|
8f9c1c40d7 | ||
|
|
89c9cd9fb4 | ||
|
|
9335eb9650 | ||
|
|
7a7be6f25a | ||
|
|
76ed55ebe8 | ||
|
|
b004b13eda | ||
|
|
dbe8a1a9f5 | ||
|
|
25e8e3c990 | ||
|
|
b84012f98f | ||
|
|
a0979b28d3 | ||
|
|
f459c64073 | ||
|
|
8a7abb9a11 | ||
|
|
7be8beb71e | ||
|
|
637274c7ee | ||
|
|
63786357b9 | ||
|
|
fa3e85b054 | ||
|
|
61c1c6b21f | ||
|
|
a347844986 | ||
|
|
d78a92f069 | ||
|
|
ba3207d176 | ||
|
|
762f9ce5f1 | ||
|
|
6c12198344 | ||
|
|
99a0ec7f0d | ||
|
|
aacebc9726 | ||
|
|
378c85b7dd | ||
|
|
c4614ff418 | ||
|
|
8cbbd2de27 | ||
|
|
067c59a636 | ||
|
|
98a02438da | ||
|
|
26b25c8c39 | ||
|
|
bfd78c1a85 | ||
|
|
239485ccb5 | ||
|
|
ff959daaef | ||
|
|
32bb340260 | ||
|
|
b7bfc6d0a1 | ||
|
|
d86879095f |
No files matched your search
+260
-199
@@ -1,39 +1,134 @@
|
||||
# Changelog
|
||||
## [1.8.0 - Make Your Move Update (September 6th, 2026)](#1-8-0)
|
||||
#### I think the red eyes mean they're friendly!
|
||||
## [1.7.2 (January 25th, 2026)](#1-7-2)
|
||||
|
||||
### Additions
|
||||
- Added Alpha Pokémon
|
||||
- Alpha Pokémon are bigger, stronger, and more aggressive than regular Pokémon.
|
||||
- They are also scarier.
|
||||
- They always know a TM move and will adapt to be stronger than your party.
|
||||
- Alphas will always lead herds and spawn in herds.
|
||||
- Added alpha Pokémon attraction bait effect to Hopo Berries.
|
||||
- Added intrinsic scaling to all Pokémon (anywhere from 5% smaller to 5% bigger than normal). Some cases, such as shoulder mounting, will not scale so that the shoulder animations don't look silly.
|
||||
- Added scaling to "baby" Pokémon (10% smaller at level 1 → normally sized at level 10).
|
||||
- Added TMs.
|
||||
- Added TM Machine. TMs are made from elemental type gems, and their recipes are unlocked by having Pokémon that know the move.
|
||||
- Added config options for making TMs infinite-use and instant unlocking of all TM recipes.
|
||||
- Added Type Gem blocks as well as crystal clusters and deepslate core blocks that are used for growing elemental type gems.
|
||||
- Added a new Starter Selection menu that looks way better.
|
||||
- Added a "Move Dex" to the Pokédex to help identify what moves a Pokémon can learn and what Pokémon can get a move.
|
||||
- Added the Habitat Block, a spawner block for servers and adventure maps that controls spawning in an area.
|
||||
- Added 49 new habitat structures. In these habitats you will find rarer Pokémon and sometimes different Pokémon will appear on different days.
|
||||
- Added a toast to notify players of how they can scroll through their party. Did you know you can hold R and scroll instead of using arrow keys? It's true!!!
|
||||
- Added a way to obtain Porygon by inserting an Upgrade or Dubious Disc into a data monitor.
|
||||
- Added Disk Rack for storing disks and being an audio sequencer for Noteblocks. What does that have to do with Pokémon? How about you mind your own business!
|
||||
- Added missing crops to Botany Pots integration.
|
||||
- Added conditional seats for riding. Seats can be gated by Molang such as "is_alpha".
|
||||
- Added 10 new ruin structures.
|
||||
- Added a new shipwreck cove structure called the Magma Shipwreck Cove.
|
||||
- Added 7 new advancements.
|
||||
- Pokémon cries now play when clicking Pokémon in the summary screen.
|
||||
- Held item visibility can now be controlled by the `held_item_visible` command argument.
|
||||
- Added config option to disable Pokémon spawning on a per-world basis.
|
||||
- Added config options to award experience to fainted Pokémon for opponents they helped defeat (`awardExperienceToFaintedPokemon`) and to still award experience for defeated enemies even after battle loss/forfeit (`awardExperienceOnBattleLoss`).
|
||||
- Added `/givetm` command.
|
||||
- Added Saccharine wood furniture for Adorn.
|
||||
- Added the Partner Mark, which party Pokémon have a very small chance to earn as the player walks, provided they have enough friendship.
|
||||
- Added trades for Saccharine Saplings, Hearty Grains, Chipped Pot, and Masterpiece Teacup to the Wandering Trader.
|
||||
- Added brewing recipe for Throat Spray.
|
||||
- Added official Controlify Support
|
||||
- Added new effects
|
||||
- Mental Restoration: Reduces "Time Since Last Rest" statistic, which controls Insomnia, by half a minute per second per effect level
|
||||
- Negative Effect Cleanse: Removes all negative effects
|
||||
- Effect Cleanse: Removes all effects
|
||||
- Added more seasonings for sinister tea
|
||||
- Mental Herb - Mental Restoration I for 10 seconds
|
||||
- White Herb - Negative Effect Cleanse
|
||||
- Milk - Effect Cleanse
|
||||
- Moomoo Milk - Effect Cleanse
|
||||
- Added decorative block models for existing items: Potion, Super Potion, Hyper Potion, Max Potion, Full Restore, Antidote, Awakening, Burn Heal, Ice Heal, Paralyze Heal, Full Heal, Ether, Elixir, Max Ether, Max Elixir, X Accuracy, X Attack, X Defence, X Special Attack, X Special Defence, X Speed, Dire Hit, Guard Spec, Weakness Policy, Blunder Policy, Cleanse Tag, and Spell Tag.
|
||||
- Added decorative sign blocks called Plaques.
|
||||
- Added ride style labels to ride stats in Pokédex, as well as ride style icons in the summary GUI.
|
||||
- Added more seasonings for sinister tea:
|
||||
- Mental Herb - Mental Restoration I for 10 seconds, which staves off Insomnia.
|
||||
- White Herb - Removes negative effects.
|
||||
- Milk, Moomoo Milk - Removes effects.
|
||||
- Added dispenser support for:
|
||||
- Applying a Honey Bottle to Saccharine Logs.
|
||||
- Washing honey off of Saccharine Slathered Logs.
|
||||
- Applying and removing honey on Saccharine Leaves.
|
||||
- Added riding statistics: `Distance by Pokémon on Land`, `Distance by Pokémon in Air` and `Distance by Pokémon in Liquid`.
|
||||
|
||||
### New rideable Pokémon
|
||||
- Honchkrow
|
||||
- Mantine
|
||||
|
||||
### Pokémon Added
|
||||
|
||||
#### Gen 5
|
||||
- Throh
|
||||
- Sawk
|
||||
|
||||
#### Gen 6
|
||||
- Litleo
|
||||
- Pyroar
|
||||
- Spritzee
|
||||
- Aromatisse
|
||||
- Swirlix
|
||||
- Slurpuff
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Falinks
|
||||
- Cottonee
|
||||
- Whimsicott
|
||||
- Ferroseed
|
||||
- Ferrothorn
|
||||
- Chingling
|
||||
- Chimecho
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
- Mantine (ride animations)
|
||||
- Flygon (ride jump animation)
|
||||
- Honchkrow (ride animations)
|
||||
- Croagunk
|
||||
- Toxicroak
|
||||
|
||||
### Changes
|
||||
- Added a new config option, `Enable In-Flight Dismounting` (default: off), which lets you dismount while riding a Pokémon in the air. This is the textbook definition of caveat emptor.
|
||||
- Ride sounds have been separated into stereo for passengers and mono for other players. Riding will now sound more spacious.
|
||||
- Battle AI now uses a smarter threshold for switching, reducing unnecessary switches.
|
||||
- AI will always use the most damaging move when at low HP and when it is not switching, improving endgame decision-making.
|
||||
- Items given from interacting with Saccharine Leaves are placed in the active hotbar slot if possible.
|
||||
- Added `label` as an alternate key to the `tag` property.
|
||||
## [1.7.3 (January 31st, 2026)](#1-7-3)
|
||||
## [1.8.0 (MONTH Xth, 2026)](#1-8-0)
|
||||
|
||||
### Additions
|
||||
- Added config options to award experience to fainted Pokémon for opponents they helped defeat (`awardExperienceToFaintedPokemon`) and to still award experience for defeated enemies even after battle loss/forfeit (`awardExperienceOnBattleLoss`)
|
||||
- Party Overlay Exp Gained Animation will now display the amount of EXP the Pokémon gained
|
||||
- Added a toast to notify players of how they can scroll through their party. Did you know you can hold R and scroll instead of using arrow keys??!
|
||||
- Added a new layer property that makes a layer scroll in a direction. "scrolling": {"speedU": 0.1, "speedV": 0.1}
|
||||
- Added missing crops to Botany Pots integration
|
||||
- Added a way to obtain Porygon by inserting the Upgrade or Dubious Disc into a data monitor
|
||||
- Added a new `cobblemon:structure`, which is a feature without any restrictions allowing the Builders to place certain structures in much more specific manner.
|
||||
- Added Configs for Infinite Use TMs and Unlocking all TMs
|
||||
- Added Type Gem blocks
|
||||
- Added Type Gem Clusters
|
||||
- Added TMs
|
||||
- Added TM Machine
|
||||
- Added Deepslate Core Block
|
||||
- Added /givetm Command
|
||||
- Added advancement triggers for learning TMs: `cobblemon:has_learn_specific_tm` (per-TM), `cobblemon:has_learn_all_tm` (all TMs learned), and `cobblemon:tms_learned` (count-based), backed by a new `totalTMLearnedCount` on player advancement data.
|
||||
- Added `cobblemon:catch_alpha_pokemon` advancement trigger and `totalAlphaCaptureCount` stat for captured Alpha Pokémon.
|
||||
- Added `cobblemon:catch_alpha_pokemon` advancement trigger and `totalAlphaCaptureCount` tracking for capturing Alpha Pokémon.
|
||||
- Added Move Dex to Pokédex
|
||||
- Added the Habitat Block, a spawner block for servers and adventure maps that controls spawning in an area.
|
||||
- Added Disk Rack for storing disks and being an audio sequencer for Noteblocks
|
||||
- Added intrinsic scaling to all Pokémon (+-5%). Some rendering cases, such as shoulder mounting, revert to 1.0 scaling to preserve animation accuracy.
|
||||
- Added scaling to "baby" Pokémon (smaller (-20%) at level 1 → normally sized at level 10)
|
||||
- Added conditional seats for riding. Seats can be gated by molang such as "is_alpha".
|
||||
- Added Alpha Pokémon
|
||||
- Added alpha scaling (alphas are larger than their normal counterparts)
|
||||
- Alphas will always lead herds and will spawn in herds
|
||||
- Alphas spawn with two random TM moves from their learnset
|
||||
- Alpha Pokémon will always defend themselves from players that attack them
|
||||
- Added alpha level matching and stat boost (based on level)
|
||||
- Added pitch-down and reverb to alpha cries
|
||||
- Added alpha Pokémon bait effect
|
||||
- Added alpha eye rendering including eye overlay, bloom, eye trail, and particles.
|
||||
- Added cobweb block slowness immunity for spider Pokémon
|
||||
- Added 49 new habitat structures. 34 as typical Minecraft structures and 15 as structures that get placed as biome features to allow for more natural placement in generation. Structures placed as biome features will not be able to be located with the `/locate structure` command.
|
||||
- Vanilla village house template pools have been adjusted to account for the new abandoned village structure habitat. Weights have changed within the JSON file for every piece to ensure too many habitats don't generate in a single village.
|
||||
- Added 10 new ruin structures.
|
||||
- rooted_arch_ruins & crumbling_arch_ruins have been transferred over to biome features, allowing for better placement in generation.
|
||||
- All ruin structures that did not generate with a gimmighoul/gilded chest before will now generate with one.
|
||||
- All ruin structures gilded chest loot tables have been updated to include a guaranteed TM not learned naturally by any Pokémon through their level-up moveset. Each ruin gets its own specific TM, so you can now go hunting ruins to complete your move dex!
|
||||
- All fossil structures have been transferred over to biome features, allowing for better placement in generation.
|
||||
- The prehistoric_underwater_fissure & prehistoric_submerged_impact fossil structures now use the biome tag `#cobblemon:is_temperate_ocean`.
|
||||
- Gimmighoul towers now generate at ground level rather than sinking one block into the ground.
|
||||
- A new shipwreck cove structure called the "magma_shipwreck_cove" has been added.
|
||||
- Shipwreck coves now generate slightly more frequently.
|
||||
- Added equipment loot tables for shipwreck cove enemies.
|
||||
- Maps from abandoned fishing boats leading to shipwreck coves now will skip explored chunks, allowing the player to find more shipwreck coves easier.
|
||||
- The lush shipwreck cove's phantom mob spawner has been replaced with breezes.
|
||||
- The lush shipwreck cove structure itself has been slightly cleaned up to be less noisy with the blocks it is built out of.
|
||||
- All shipwreck coves' spawners have been adjusted to give compatible mobs enchanted iron or diamond armour and weapons while in its ominous state. The armour given to the mobs will also use the automaton trim. We upped the difficulty because it was too easy now that your Pokémon can fight for you.
|
||||
- One TM of the elemental hyper beams has been added as guaranteed loot to the main treasure chest in every shipwreck cove. (Magma cove gets blast burn, lush cove gets frenzy plant, and submerged cove gets hydro cannon)
|
||||
- Added 7 new advancements
|
||||
- Pressing the inventory key now closes the Pokémon interact wheel GUI
|
||||
- Pokémon cries now play when clicking Pokémon in the summary screen.
|
||||
- Pressing the Summary key while either the starter selection or summary screen are open now closes those screens
|
||||
- Ice type Pokémon are now immune to Packed Snow
|
||||
- Held item visibility can now be controlled by the held_item_visible command argument.
|
||||
|
||||
### New rideable Pokémon
|
||||
- Crobat
|
||||
@@ -147,6 +242,7 @@
|
||||
- ... and Substitute
|
||||
|
||||
#### Model changes to the regional bias resource pack:
|
||||
|
||||
- Added Mime Jr. (Galar Bias)
|
||||
- Added Bergmite (Hisui Bias)
|
||||
- Changed models for every existing regional bias
|
||||
@@ -239,27 +335,14 @@
|
||||
- Tail Whip
|
||||
|
||||
### Changes
|
||||
- Removed the penalty for block destruction speed when on a hover mount, making them better for digging.
|
||||
- Golden Apples can now be crafted using the Applin Apples.
|
||||
- Prismarine Crystals and Amethyst Shards can now be used to craft Pokedexes.
|
||||
- Updated Pokédex Screens item tag to support tagged variants of Redstone and Glowstone Dust.
|
||||
- Sharp Beak can now be crafted using bones and Flying Gems.
|
||||
- Metal Alloy can now be crafted using iron and Thunder Stones.
|
||||
- Ability Capsules can now be brewed from Medicinal Brew and Dragon Breath.
|
||||
- Using an Ability Capsule will now return an Empty Bottle.
|
||||
- Decreased the default pastured wander radius to 32 blocks.
|
||||
- Added alternate versions of the basic PC Wallpapers based on the original design of Cobblemon's PC, before public release.
|
||||
- Rooted Arch Ruins, Crumbling Arch Ruins, and all fossil structures have been improved so that they generate more naturally in terrain.
|
||||
- Gimmighoul towers now generate at ground-level rather than sinking one block into the ground.
|
||||
- All ruin structures that did not generate with a gimmighoul/gilded chest before will now generate with one.
|
||||
- All ruin structures gilded chest loot tables have been updated to include a guaranteed TM not learned naturally by any Pokémon through their level-up moveset. Each ruin gets its own specific TM, so you can now go hunting ruins to complete your move dex!
|
||||
- The Prehistoric Underwater Fissure and Prehistoric Submerged Impact fossil structures now use the biome tag `#cobblemon:is_temperate_ocean`.
|
||||
- The lush shipwreck cove's phantom mob spawner has been replaced with breezes.
|
||||
- Shipwreck coves now generate slightly more frequently.
|
||||
- Added equipment loot tables for shipwreck cove enemies.
|
||||
- Maps from abandoned fishing boats leading to shipwreck coves now will skip explored chunks, allowing the player to find more shipwreck coves more easily.
|
||||
- All shipwreck coves' spawners have been adjusted to give compatible mobs enchanted iron or diamond armour and weapons while in its ominous state. The armour given to the mobs will also use the automaton trim. We upped the difficulty because it was too easy now that your Pokémon can fight for you.
|
||||
- One TM of the elemental hyper beams has been added as guaranteed loot to the main treasure chest in every shipwreck cove. (Magma Cove gets Blast Burn, Lush Cove gets Frenzy Plant, and Submerged Cove gets Hydro Cannon.)
|
||||
- Stat increase and decrease particles have been changed, with the addition of alpha boost particles.
|
||||
- Dragonite's mailbag cosmetic can now also be obtained with a chest in the cosmetic slot
|
||||
- Adjusted Croagunk's walkspeed
|
||||
- Adjusted Aerodactyl's walkspeed
|
||||
- Sharp Beak can now be crafted
|
||||
- Metal Alloy can now be crafted
|
||||
- Ability Capsules can now be brewed
|
||||
- Using an Ability Capsule will now return an Empty Bottle
|
||||
- The following items can now be obtained by brushing the following Pokémon:
|
||||
- Pink Wool - Wigglytuff
|
||||
- White Wool - Swablu, Altaria
|
||||
@@ -268,147 +351,129 @@
|
||||
- The following items can now be obtained by evolving the following Pokémon into any of their evolutions:
|
||||
- Turtle Scute - Squirtle, Wartortle, Turtwig, Grotle, Tirtouga, Chewtle
|
||||
- Shed Shell - Metapod, Kakuna, Ekans, Paras, Venonat, Krabby, Scyther, Kabuto, Dratini, Dragonair, Ledyba, Spinarak, Yanma, Pineco, Dunsparce, GLigar, Pupitar, Treecko, Grovyle, Silcoon, Cascoon, Surskit, Nincada, Trapinch, Vibrava, Corphish, Anorith, Bagon, Shelgon, Kricketot, Burmy, Combee, Skorupi, Swadloon, Whirlipede, Dwebble, Scraggy, Karrablast, Joltik, Larvesta, Spewpa, Clauncher, Helioptile, Charjabug, Crabrawler, Cutiefly, Dewpider, Salandit, Wimpod, Poipole, Sobble, Drizzile, Dottler, Silicobra, Toxel, Sizzlipede, Snom, Tarountula, Nymble, Rellor
|
||||
- Saccharine Leaves now drop Tart, Sweet, or Syrupy Apples, depending on their honey coating stage, instead of vanilla Apples.
|
||||
- Fixed the height of the Tumblestone Bud items when held in hand, placed on head, viewed in a GUI, and placed in an Item Frame.
|
||||
- Refactored all advancements, removing several deemed unnecessary. Due to this cleanup, all previously obtained advancements will be reset besides the root (for picking a starter Pokémon). This sucks, and we're sorry. We had to do this eventually so better now than later.
|
||||
- Forces between colliding hitboxes now factors in the weight of Pokémon.
|
||||
- Improved berry tree performance on singleplayer and servers.
|
||||
- Updated stat increase and decrease particle effects with the addition of alpha boost particles.
|
||||
- Improved the performance of pasture blocks on singleplayer and servers.
|
||||
- Changed the bait effects of the following items: (Vanilla) Sweet & Glow Berries, Golden & Enchanted Golden Apples (Cobblemon) Custap, Eggant, Hopo & Micle Berries, Sweet Hearts.
|
||||
- Wild Pokémon now reliably have at least one offensive move if they can have offensive moves at that level. No more free lunches with Pokémon that can't even hurt you.
|
||||
- Data Monitor can accept Disks (TMs, Music Disks, Upgrade, Dubious Disk, etc.) to change what's being displayed on the screen.
|
||||
- The party overlay's experience-gained animation now displays the amount of EXP the Pokémon gained.
|
||||
- Pressing the inventory key now closes the Pokémon interact wheel GUI.
|
||||
- Updated the sound for harvesting Apricorns.
|
||||
- Lowered the volume of harvesting berries.
|
||||
- Saccharine Leaves now drop Tart, Sweet, or Syrupy Apples, depending on their honey coating stage, instead of vanilla Apples
|
||||
- Fixed the height of the Tumblestone Bud items when held in hand, placed on head, viewed in a GUI, and placed in an Item Frame
|
||||
- Refactored all advancements, removing several deemed unnecessary. Due to this refactor, all previously obtained advancements will be reset besides the root (for picking a starter Pokémon). We are sorry for the inconvience, but it makes the tab cleaner and simplifies future advancements.
|
||||
- Restructured Botany Pots integration folder structure
|
||||
- Decrease the default pastured mon wander radius to 32.
|
||||
- Factor in the weight of Pokémon when calculating pushing forces between hitboxes.
|
||||
- Remade the Starter Selection Screen with new assets
|
||||
- Refactored berry trees to be less impactful on TPS (more performant)
|
||||
- Changed the bait effects of the following items: (Vanilla) Sweet & Glow Berries, Golden & Enchanted Golden Apples (Cobblemon) Custap, Eggant, Hopo & Micle Berries, Sweet Hearts
|
||||
- Wild Pokémon now reliably have at least one offensive move if they can have offensive moves at that level.
|
||||
- Data Monitor can accept Disks (TMs, Music Disks, Upgrade, Dubious Disk, etc.) to change what's being displayed on the screen
|
||||
- Rider positioning is now client-driven (Remove poseOffsets and offset per seat/pose) in favour of sending real-time passenger position.
|
||||
- Cleaned up seat data. Removed all now unused fields from seats in all species files. Also made locator seat names explicit in the species files (however, this doesn't break the legacy format)
|
||||
- Marks can now provide aspects.
|
||||
- Added new shiny and Alpha chance configs
|
||||
- Apricorns now make a new sound when harvested
|
||||
- Lowered the volume of harvesting berries
|
||||
- Added alternate versions of the basic PC Wallpapers based on the original design of Cobblemon's PC, before public release
|
||||
- Improved compatibility with various mods
|
||||
- Farmer's Delight's cutting board can now be used to strip Apricorn and Saccharine's logs, or salvage their planks from various blocks. It can also separate their boats from chests.
|
||||
- Farmer's Delight's cutting board can be used to get two white dye from Pep-Up Flowers and two magenta dye from Medicinal Leeks.
|
||||
- Farmer's Delight's cutting board can separate Tumblestone Blocks back into Tumblestones.
|
||||
- Farmer's Delight's cutting board can salvage Type Gems from their Gem Blocks.
|
||||
- Farmer's Delight's Onions, Tomatoes, and Cabbages can be used to cook Open-Faced Sandwiches. Nature's Spirit's Olives also can be used.
|
||||
- Farmer's Delight's cutting board can be used on Hearty Grains to obtain rice and straw, similarly to Farmer's Delight's Rice Panicles.
|
||||
- Farmer's Delight's Apple Pies and Apple Ciders can now be made using Applin Apples, alongside the Vanilla counterpart.
|
||||
- Amendments allows for cooking Leek and Potato Stew in a Cauldron.
|
||||
- Any mods that utilise Moonlight's Soft Fluid system can store Leek and Potato Stew, Medicinal Brew, Antidote, Awakening, Burn Heal, Ice Heal, Paralyze Heal, Full Heal, Potion, Super Potion, Hyper Potion, Max Potion, Full Restore, Ability Capsules, Moomoo Milk, and Berry Juice in liquid form (such as Suplementaries' Jar or Goblet, or Cauldrons while using Amendments).
|
||||
- Supplementaries' Hourglass can now use Bright Powder, Quick Powder, Metal Powder, Heal Powder, Silver Powder, and Remedy.
|
||||
- Farmer's Delight's cutting board can now be used to strip Apricorn and Saccharine's logs, or salvage their planks from various blocks. It can also separate their boats from chests
|
||||
- Farmer's Delight's cutting board can be used to get two white dye from Pep-Up Flowers and two magenta dye from Medicinal Leeks
|
||||
- Farmer's Delight's cutting board can separate Tumblestone Blocks back into Tumblestones
|
||||
- Farmer's Delight's cutting board can salvage Type Gems from their Gem Blocks
|
||||
- Farmer's Delight's Onions, Tomatoes, and Cabbages can be used to cook Open-Faced Sandwiches. Nature's Spirit's Olives also can be used
|
||||
- Farmer's Delight's cutting board can be used on Hearty Grains to obtain rice and straw, similarly to Farmer's Delight's Rice Panicles
|
||||
- Farmer's Delight's Apple Pies and Apple Ciders can now be made using Applin Apples, alongside the Vanilla counterpart
|
||||
- Amendments allows for cooking Leek and Potato Stew in a Cauldron
|
||||
- Any mods that utilise Moonlight's Soft Fluid system can store Leek and Potato Stew, Medicinal Brew, Antidote, Awakening, Burn Heal, Ice Heal, Paralyze Heal, Full Heal, Potion, Super Potion, Hyper Potion, Max Potion, Full Restore, Ability Capsules, Moomoo Milk, and Berry Juice in liquid form (such as Suplementaries' Jar or Goblet, or Cauldrons while using Amendments)
|
||||
- Supplementaries' Hourglass can now use Bright Powder, Quick Powder, Metal Powder, Heal Powder, Silver Powder, and Remedy
|
||||
- Berry Trees are now compatible with the Planter properly
|
||||
- Saccharine Way Signs (added by Supplementaries automagically) now use a premade texture to prevent odd visual issues.
|
||||
- Supplementaries' Jar can now store various Cobblemon cookies, candies, and similar items.
|
||||
- Some of Supplementaries' items have been added to the `#cobblemon:held/container_held_items` item tag, including sacks, safes, and lunch baskets.
|
||||
- Experience Nuggets can be used in place of Sculk for making the EXP Candies if using Create.
|
||||
- Rage Candy Bar has a Create Mixing recipe.
|
||||
- All Cobblemon ores can now be crushed in Create's Crushing Wheels. All Gem and Tumblestone Clusters can be crushed for automatic, low return, gems.
|
||||
- Hearty Grains can be milled like Farmer's Delight Rice Panicles if both Farmer's Delight and Create are installed.
|
||||
- Big Roots and Revival Herbs can be milled to their respective powders.
|
||||
- Pep-Up Flowers, Medicinal Leeks, and all 70 berries can be milled in Create's Millstone for additional dye drops.
|
||||
- Auspicious Armour and Upgrades can be Haunted using Create's crafting systems.
|
||||
- Applin Apples can be used to make Honeyed Apples in Create.
|
||||
- Sounds for interacting with various Cobblemon blocks now play from the centre of the block, rather than the corner. You probably didn't notice.
|
||||
- Dragonite's mailbag cosmetic can now also be obtained with a chest in the cosmetic slot.
|
||||
- Adjusted the walk speed for Croagunk and Aerodactyl.
|
||||
- Saccharine Way Signs (added by Supplementaries automagically) now use a premade texture to prevent odd visual issues
|
||||
- Supplementaries' Jar can now store various Cobblemon cookies, candies, and similar items
|
||||
- Some of Supplementaries' items have been added to the `#cobblemon:held/container_held_items` item tag, including sacks, safes, and lunch baskets
|
||||
- Experience Nuggets can be used in place of Sculk for making the EXP Candies if using Create
|
||||
- Rage Candy Bar has a Create Mixing recipe
|
||||
- All Cobblemon ores can now be crushed in Create's Crushing Wheels
|
||||
- All Gem and Tumblestone Clusters can be crushed for automatic, low return, gems
|
||||
- Hearty Grains can be milled like Farmer's Delight Rice Panicles if both Farmer's Delight and Create are installed
|
||||
- Big Roots and Revival Herbs can be milled to their respective powders
|
||||
- Pep-Up Flowers, Medicinal Leeks, and all 70 berries can be milled in Create's Millstone for additional dye drops
|
||||
- Auspicious Armour and Upgrades can be Haunted using Create's crafting systems
|
||||
- Applin Apples can be used to make Honeyed Apples in Create
|
||||
- Golden Apples can now be crafted using the Applin Apples
|
||||
- Prismarine Crystals and Amethyst Shards can now be used to craft Pokedexes
|
||||
- Updated Pokédex Screens item tag to support tagged variants of Redstone and Glowstone Dust
|
||||
- Sounds for interacting with various Cobblemon blocks now play from the centre of the block, rather than the corner
|
||||
- The `/pc` command no longer plays PC activation sounds
|
||||
- Pressing the Summary key while either the starter selection or summary screen are open now closes those screens.
|
||||
- Ice type Pokémon are now immune to Packed Snow.
|
||||
- Added cobweb block slowness immunity for spider Pokémon
|
||||
|
||||
### Fixes
|
||||
- Fixed rare Pokémon being far too likely to spawn multiple times in a single spawn cycle. The dev told me that the exact bug is too complicated to explain.
|
||||
- Fixed Blitzle's base scale being `1` rather than the intended `0.8`
|
||||
- Fixed Magnezone's emissive layer for the shiny having the wrong name, thus displaying wrongly
|
||||
- Fixed Litleo not blinking
|
||||
- Fixed Omanyte and Omastar incorrectly using water animations on land.
|
||||
- Fixed Pokédex layout for genderless variations.
|
||||
- Fixed odd stacking behaviour with Poké Baits.
|
||||
- Fixed Pokédex layout for genderless variations
|
||||
- Fixed odd stacking behaviour with the poke_bait item.
|
||||
- Fixed a compatibility issue with World Game Rules which caused some log spam in specific cases.
|
||||
- Fixed improper validation of Pokémon when performing a trade.
|
||||
- Fixed an issue where particles like beams can disappear when you don't see the beginning of the beam, or you look away.
|
||||
- Fixed Bedrock particle materials sometimes being incorrectly invisible from some sides.
|
||||
- Fixed an occasional crash on retrieving revived fossils.
|
||||
- Fixed brushing not working on Neoforge. Again.
|
||||
- Fixed braking not working for submarine-style mounts (whoops).
|
||||
- Fixed an issue where particle like beam can disappear when you don't see the beginning of the beam, or you look away
|
||||
- Fixed Bedrock particle materials (alpha/opaque/blend/add) sharing one render batch, which caused incorrect culling on mixed batches; each material now batches and draws with its own blend and cull state, and the alpha material renders as a true cutout
|
||||
- Fixed an occasional crash on retrieving revived fossils
|
||||
- Fixed brushing not working on Neoforge
|
||||
- Fixed braking not working for submarines (whoops).
|
||||
- Fixed strafing not having the same friction as forward movement (funny whoops).
|
||||
- Fixed submarine-style mounts' surface-water animations not playing when supposed to.
|
||||
- Fixed submarine-style mounts' camera freaking out when looking up or down on the surface.
|
||||
- Fixed pastured Pokémon despawning and running away.
|
||||
- Fixed Apricorn trees crashing when other mods bonemeal them.
|
||||
- Fixed the issue that caused hover-style mounts without a ground style to be rendered useless upon touching the ground on a server.
|
||||
- Fixed horizontal and vertical collisions with hover-style mounts so they no longer accumulate or retain momentum.
|
||||
- Fixed NeoForge brewing stand unexpectedly crashing users when shift clicking items within its menu. Well, I suppose all crashes are unexpected. You know what I mean.
|
||||
- Fixed NPCs switching Pokémon very often during battles.
|
||||
- Fixed Campfire Pot causing crashes under certain circumstances.
|
||||
- Fixed Type Gems not being consumed upon use in battles.
|
||||
- Fixed several Ancient Pokéball's not using the correct capture multiplier.
|
||||
- Fixed a server warning that could occur when recalling Pokémon.
|
||||
- Fixed sliding particles incorrectly handling block collision.
|
||||
- Fixed Full Heal not curing status conditions in battle. Literally it's one job.
|
||||
- Fixed submarine's surface-water animations not playing when supposed to.
|
||||
- Fixed submarine camera freaking out when looking up or down on the surface.
|
||||
- Fixed pastured mons despawning and running away.
|
||||
- Fixed Apricorn trees crashing when other mods bonemeal it
|
||||
- Fixed the issue that caused Hover Behaviour rides without a ground style to be rendered useless upon touching the ground on a server.
|
||||
- Removed the "inAir" penalty for block destruction speed when on a hover mount.
|
||||
- Fixed horizontal and vertical collisions with hover mounts so they no longer accumulate or retain momentum
|
||||
- Fixed NeoForge brewing stand unexpectedly crashing users when shift clicking items within its menu
|
||||
- Fixed NPCs switching Pokémon very often during battles
|
||||
- Fixed Campfire pot causing crashes under certain circumstances
|
||||
- Fixed Type Gems not being consumed upon use
|
||||
- Fixed Ancient Pokéball's to their respective modifiers
|
||||
- Fixed a Server warning when recalling Pokémon
|
||||
- Fixed sliding particles incorrectly handling block collision
|
||||
- Fixed Full Heal not curing status conditions in battle
|
||||
- Fixed a rare issue with battles locking up when you are forced to switch, with the switch menu appearing instantly.
|
||||
- Fixed the combined effects of Pledge moves having broken Showdown message handling and localisation.
|
||||
- Fixed a very niche issue where structures would spawn in an incorrect location, creating very funky world gen.
|
||||
- Fixed the combined effects of Pledge moves having broken Showdown message handling and localisation
|
||||
- Fixed rare Pokémon being far too likely to spawn multiple times in a single spawn cycle. The dev told me that the exact bug is too complicated to explain.
|
||||
- Resolved the niche issue where Structures would spawn in an incorrect location, creating very funky world gen.
|
||||
- Fixed Galarian Ponyta missing its cry.
|
||||
- Fixed various log warnings caused by redundant files and sound events.
|
||||
- Traded Pokémon will now retain the original friendship level when traded back to its OT.
|
||||
- Tumblestone Clusters now display correctly on maps.
|
||||
- Fixed removing mulch from berry trees with a shovel not working. That was meant to be a thing.
|
||||
- Fixed collecting honey from Saccharine Leaf blocks deleting Honey Bottles when the inventory was full.
|
||||
- Fixed Stripped Saccharine Wood recipe being... not there.
|
||||
- Fixed some performance issues with a kind of sound event causing lag by attempting to create sounds every tick when out of range of hearing.
|
||||
- Fixed stat-reducing Aprijuice not being possible to apply on a maxed out Pokémon. Also fixed the possibility of going into negatives.
|
||||
- Fixed exploit where you could take out the bait or drop your Pokérod with bait to avoid consuming bait, now the bobber will just disappear if you do so.
|
||||
- Fixed Apricorn and Saccharine boats incorrectly using chest boat models.
|
||||
- Fixed wild and trainer battle AI refusing to select moves with the `adjacentAlly` target type, even when it's their only option.
|
||||
- Fixed gilded chests being more accessible than intended.
|
||||
- Fixed fished up Pokémon not being added as "seen" to the Pokédex.
|
||||
- Fixed surprised Gimmighoul encounters not being added as "seen" to the Pokédex.
|
||||
- Traded Pokémon will now retain the original friendship level when traded back to its OT
|
||||
- Clusters now display correctly on maps
|
||||
- Fixed removing mulch from berry trees with a shovel not working.
|
||||
- Fixed collecting honey from Saccharine Leaf blocks deleting Honey Bottles when the inventory was full
|
||||
- Fixed Stripped Saccharine Wood recipe being missing
|
||||
- Fixed CancelableSoundInstance causing lag by attempting to create sounds every tick when out of range of hearing
|
||||
- Fixed Stat Reducing Aprijuice not being possible to apply on a max out Pokémon, and the possibility of going into negatives
|
||||
- Fixed Exploit where you could take out the bait or drop your Pokérod with bait to avoid consuming bait, now the bobber will just disappear if you do so
|
||||
- Fixed Apricorn and Saccharine boats incorrectly using chest boat models
|
||||
- Fixed wild and trainer battle AI refusing to select moves with the `adjacentAlly` target type, even when it's their only option
|
||||
- Fixed gilded chests being more accessible than intended
|
||||
- Fixed fished up Pokémon not being added as "seen" to the Pokedex
|
||||
- Fixed surprised Gimmighoul encounters not being added as "seen" to the Pokedex
|
||||
- Fixed a crash involving berry trees that have been world edited.
|
||||
- Fixed visual desync in multiplayer for players mounting Pokémon outside other players' loaded chunks.
|
||||
- Fixed visual desync in multiplayer for players mounting Pokémon outside other players' loaded chunks
|
||||
- Fixed Pokémon entities leveled by rare candy not getting stronger as Minecraft entities unless recalled.
|
||||
- Fixed a freeze during game load that occurred when multiple mods tried to load Cobblemon API classes at the same time.
|
||||
- Lowered the volume of Blitzle and Zebstrika's cry (shut up!).
|
||||
- Regional Foods will now prioritise being eaten instead of used for healing Pokémon - shift clicking will use it as healing.
|
||||
- Resolved z-fighting (texture flickering) on Pokédex Models.
|
||||
- Restored Ninjask's ability to do barrel rolls.
|
||||
- Fixed random tick crashes from berry trees
|
||||
- Lowered the volume of Blitzle and Zebstrika's cry
|
||||
- Regional Foods will now prioritise being eaten instead of used for healing Pokémon, shift clicking will use it as healing
|
||||
- Resolved Z-Fighting on Pokédex Models
|
||||
- Restored Ninjask's ability to do barrel rolls
|
||||
- Fixed Raichu (male) and Alolan Raichu (both genders) not displaying the correct Partner Cap when Pewter Crunchies are in the cosmetic slot.
|
||||
- Fixed a crash caused by adding enchanted items to the Resurrection Machine.
|
||||
- Fixed a crash caused by adding enchanted items to the Resurrection Machine
|
||||
- Fixed a crash that could occur when starting a PvN battle with only fainted Pokémon.
|
||||
- Fixed the Remember Riding Camera config not being respected when dismounting and resolved the incompatibility with the Auto Third Person mod.
|
||||
- Fixed Full Heal block not dropping Full Heals. Lmao.
|
||||
- Fixed status cure items not returning an empty bottle when used.
|
||||
- Fixed Panpour consuming its held item when evolving with a Water Stone.
|
||||
- Fixed Smoked Tail Curry returning a Wooden Bowl when consumed despite it not being included in the recipe. Free bowls!
|
||||
- Fixed `/giveallpokemon` not respecting the min and max arguments.
|
||||
- Fixed the choose starter toast not disappearing when capturing a Pokémon instead of opening the starter selection screen.
|
||||
- Fixed an issue where certain Sinister Tea seasonings were only applying as instant effects and would not apply over time.
|
||||
- Long config option labels now scroll instead of overflowing in the configuration menu.
|
||||
- Fixed some z-fighting and clipping on Dewott and Hisui-Bias Dewott.
|
||||
- Eggant Berries can now be composted properly on NeoForge.
|
||||
- Composting chances are now the same on NeoForge as on Fabric.
|
||||
- Fixed problem where furnaces which had smelted Revival Herbs into Heal Powder in older versions would glitch out on trying to get the experience out of the block.
|
||||
- Fixed the Battle AI not recognizing Surging Strikes as a multistrike move.
|
||||
- Fixed Tentacool not using its swim animation when on water surface.
|
||||
- Fixed Eevee using its left shoulder pose when in battle.
|
||||
- Fixed Magikarp and Gyarados Jump Pattern resourcepacks replacing shiny textures of addons targeting Magikarp or Gyarados.
|
||||
- Fixed Combee's pollinated texture overwriting the shiny and female textures.
|
||||
- Fixed a colour mistake with Dracovish's shiny.
|
||||
- Fixed Togedemaru's back spikes z-fighting.
|
||||
- Fixed Sliggoo (+ Hisuian) evolving in the Nether and End where it can't actually rain.
|
||||
- Corrected the scales, hitbox sizes, and eye heights for Ariados, Cetoddle, Durant, Galvantula, Klink, Lanturn, Lumineon, Mantine, Mantyke, Mudbray, Numel, Staravia, and Torkoal.
|
||||
- Fixed Torkoal's walk speed not matching its animation. He will no longer move faster than Bunnelby and Buneary. This tortoise would've won.
|
||||
- Fixed Lanturn T-posing due to missing ground walk and sleep animations.
|
||||
- Fixed Oshawott and Dewott using their sleep animation in unintended scenarios.
|
||||
- Fixed Dudunsparce and Maushold always having the same form instead of having the 1 in 100 chance of being different.
|
||||
- Fixed bird mounts rapidly switching between their flying and gliding animations when flying at low speed.
|
||||
- Fixed some battles being startable with fainted Pokémon in the initial sendout.
|
||||
- Fixed random gender selection for forms with strict gender divides (such as Meowstic and Oinkologne) never choosing female (that's sexist).
|
||||
- Fixed the "Press R to battle" prompt not disappearing after you finish your first battle, at least until you relogged.
|
||||
- Fixed the composter block being listed as "composer" in the cosmetic items species feature
|
||||
- Fixed Castform's forms being tagged as introduced in generation 4 instead of 3.
|
||||
- Fixed Blitzle's base scale being `1` rather than the intended `0.8`.
|
||||
- Fixed Magnezone's emissive layer for the shiny having the wrong name, thus displaying wrongly.
|
||||
- Fixed Litleo not blinking.
|
||||
- Fixed the Remember Riding Camera config not being respected when dismounting and resolved the incompatibility with the Auto Third Person mod
|
||||
- Fixed Full Heal block not dropping Full Heals
|
||||
- Fixed status cure items not returning an empty bottle when used
|
||||
- Fixed Panpour consuming its held item when evolving with a Water Stone
|
||||
- Fixed Smoked Tail Curry returning a Wooden Bowl when consumed despite it not being included in the recipe
|
||||
- Fixed `/giveallpokemon` not respecting the min and max parameters
|
||||
- Fixed the choose starter toast not disappearing when capturing a Pokémon instead of opening the starter selection screen
|
||||
- Fixed an issue where certain Sinister Tea seasonings were only applying as instant effects and would not apply over time
|
||||
- Long config option labels now scroll instead of overflowing
|
||||
|
||||
### Developer
|
||||
- Added `PokeSnackSpawnerFactory` which allows influence registration on PokeSnack encounters.
|
||||
- Added `FishingSpawnerFactory` which allows influence registration on fishing encounters.
|
||||
- Added `BattleFleeAttemptEvent` fired from `PokemonBattle.checkFleeAttempt`, allowing developers to intercept and control flee attempts.
|
||||
- Added `PokeSnackSpawnerFactory` which allows influence registration on PokeSnack encounters
|
||||
- Added `FishingSpawnerFactory` which allows influence registration on fishing encounters
|
||||
- Added `BattleFleeAttemptEvent` fired from `PokemonBattle.checkFleeAttempt`, allowing developers to intercept and control flee attempts
|
||||
- Tweaked the spawning selector interface to take a bucket function rather than a bucket directly.
|
||||
- Added a new `cobblemon:height_range` processor type to allow developers to restrict structure-spawning ranges.
|
||||
- Separated all the functions of MoLangFunctions.kt into their own files.
|
||||
@@ -416,36 +481,33 @@
|
||||
- Removed "SpawnBucket" class in favour of just storing maps of bucket names to weights.
|
||||
- Fixed nicknames not being applied to Pokémon spawned from `spawn_pool_world` files.
|
||||
- Added support for `profilePokedexScale`, `profilePokedexTranslation`, `profileSummaryScale`, and `profileSummaryTranslation` in poser files for finer control over profile displays.
|
||||
- Fixed riding camera offsets defined on Pokémon forms not being applied.
|
||||
- Marks can now provide aspects.
|
||||
- Fixed riding camera offsets defined on Pokémon forms not being applied
|
||||
|
||||
### Molang & Datapacks
|
||||
- Added `moveset_builders` datapack folder for different styles of movesets.
|
||||
- Added `party_pools` datapack folder for defining groups of Pokémon for complex NPC party definitions.
|
||||
- Added `party_compositions` datapack folder for arranging parties on NPCs that rely on party pools.
|
||||
- Added `composed_pool` NPC party provider type which combines a party pool with a party composition to create more complex NPC parties.
|
||||
- Added basic chatter NPC Behaviour.
|
||||
- Added `q.item.is_enchanted()`.
|
||||
- Added `q.item.has_enchantment(minecraft:sharpness, 3)`.
|
||||
- Added `has_chosen_starter` and `get_starter_uuid` as available Molang functions.
|
||||
- Added `has_tm_move_unlocked`, `unlock_tm_move`, and `lock_tm_move` as available Molang functions for Players.
|
||||
- Added `marks`, `has_mark`, and `remove_marks` as available Molang functions for Pokémon.
|
||||
- Added basic chatter NPC Behaviour
|
||||
- Added `q.item.is_enchanted()`
|
||||
- Added `q.item.has_enchantment(minecraft:sharpness, 3)`
|
||||
- Added `has_chosen_starter` & `get_starter_uuid` as available Molang functions
|
||||
- Added `has_tm_move_unlocked`, `unlock_tm_move`, & `lock_tm_move` as available Molang functions for Players
|
||||
- Added `marks`, `has_mark`, & `remove_marks` as available Molang functions for Pokémon
|
||||
- Added Data Component support for species drops, eg `"components": {"minecraft:custom_model_data": 123}`
|
||||
- Added the new `sortOrder` field to mark entries, (defaults to 0 if not specified) which controls the order in which marks are rendered on the HUD, with higher numbers being rendered on top of lower numbers.
|
||||
- Added optional `order` field to starter categories. It will allow explicitly sorting categories in the starter selection screen.
|
||||
- SpeciesAdditions append vs override now matches the wiki documentation.
|
||||
- Added `weighted_choice` as a new feature type. It requires setting weights for each choice and allows developers to simplify the process of assigning random aspect choice based on marble chance.
|
||||
- Added `q.world.spawn_loot_table_items(loot_table_id, x, y, z)` which spawns the items from the loot table at the position. Use type: `minecraft:chest` for the loot table.
|
||||
- Added the new `sortOrder` field to mark entries, (defaults to 0 if not specified) which controls the order in which marks are rendered on the HUD, with higher numbers being rendered on top of lower numbers
|
||||
- Added optional `order` field to starter categories. It will allow explicitly sorting categories in the starter selection screen
|
||||
- SpeciesAdditions append vs override now matches wiki documentation
|
||||
- Added `weighted_choice` as a new feature type. It requires setting weights for each choice and allows developers to simplify the process of assigning random aspect choice based on marble chance
|
||||
- Added `q.world.spawn_loot_table_items(loot_table_id, x, y, z)` which spawns the items from the loot table at the position. (Use type: "minecraft:chest" for the loot table.)
|
||||
- Added `q.pokemon.entity` to access the entity of Pokémon.
|
||||
- Flamethrower and Seismic Toss had their action effect JSON cleaned up.
|
||||
- Ride sounds can now be set as exclusive to passengers.
|
||||
- Ride sounds no longer play when submerged unless specified with a new setting.
|
||||
- Flamethrower and Seismic Toss had their action effect JSON cleaned up
|
||||
- Ride sounds can now be set as exclusive to passengers
|
||||
- Ride sounds no longer play when submerged unless specified with a new setting
|
||||
- NPC Trainers can now call a `command` animation when issuing a command in battle.
|
||||
- Added a q.failed function to move action effects.
|
||||
- Herd spawned Pokémon have their levels controlled differently. The `levelRange` property now acts as a pre-offset clamp instead of a filter, with a new `herdLevelRange` property to do the filter behaviour.
|
||||
- `q.sound` now accepts three parameters (sound event, volume, and pitch) instead of just the sound event and pitch.
|
||||
- Added a new layer property that makes a layer scroll in a direction. "scrolling": {"speedU": 0.1, "speedV": 0.1}
|
||||
- Cleaned up seat data. Removed all now unused fields from seats in all species files. Also made locator seat names explicit in the species files (however, this doesn't break the legacy format)
|
||||
- Herd spawned Pokémon have their levels controlled differently. The `levelRange` property now acts as a clamp instead of a filter, with a new `herdLevelRange` property to do the filter behaviour.
|
||||
- `q.sound` now accepts three parameters (sound event, volume and pitch) instead of just the sound event and pitch.
|
||||
|
||||
### Particles
|
||||
|
||||
@@ -512,8 +574,8 @@
|
||||
|
||||
#### Miscellaneous Particles
|
||||
|
||||
- Added Wild Might particles for Alpha Pokémon.
|
||||
- Updated stat increase and decrease particles.
|
||||
+ Added Wild Might particles for Alpha Pokémon
|
||||
+ Updated stat increase and decrease particles
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
@@ -525,5 +587,4 @@
|
||||
- Brazilian Portuguese
|
||||
- Simplified Chinese
|
||||
- Traditional Chinese
|
||||
- Korean
|
||||
- Russian
|
||||
- Korean
|
||||
@@ -32,6 +32,10 @@ repositories {
|
||||
maven(url = "https://api.modrinth.com/maven")
|
||||
maven(url = "https://maven.neoforged.net/releases")
|
||||
maven(url = "https://maven.gegy.dev")
|
||||
exclusiveContent {
|
||||
forRepository { maven(url = "https://maven.isxander.dev/releases") }
|
||||
filter { includeGroup("dev.isxander") }
|
||||
}
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
@@ -62,6 +66,11 @@ dependencies {
|
||||
|
||||
// Unit Testing
|
||||
testImplementation(libs.bundles.unitTesting)
|
||||
|
||||
// Controlify Compat, same as flywheel so we pick one and only use the api
|
||||
modCompileOnlyApi("dev.isxander:controlify:2.4.3+1.21.1-fabric") {
|
||||
isTransitive = false
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<Test> {
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.duck;
|
||||
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Duck that facilitates getting a level key for a given level.
|
||||
*
|
||||
* @author dualspiral
|
||||
* @since August 30th, 2026
|
||||
*/
|
||||
public interface MinecraftServerDuck {
|
||||
Optional<ResourceKey<Level>> cobblemon$getLevelKey(Level level);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.duck;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
/**
|
||||
* Duck that facilitates getting a level key that we store on the level.
|
||||
*
|
||||
* @author dualspiral
|
||||
* @since August 30th, 2026
|
||||
*/
|
||||
public interface ServerLevelDuck {
|
||||
ResourceLocation cobblemon$getLevelResourceLocation();
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.multiplayer.ClientPacketListener;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetPassengersPacket;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
@Mixin(ClientPacketListener.class)
|
||||
public class ClientPacketListenerMixin {
|
||||
|
||||
@Shadow private ClientLevel level;
|
||||
|
||||
@Inject(method = "handleSetEntityPassengersPacket", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/world/entity/Entity;hasIndirectPassenger(Lnet/minecraft/world/entity/Entity;)Z", shift = At.Shift.BEFORE), cancellable = true)
|
||||
private void cobblemon$handlePokemonPassengers(ClientboundSetPassengersPacket packet, CallbackInfo ci) {
|
||||
Entity entity = this.level.getEntity(packet.getVehicle());
|
||||
if (entity instanceof PokemonEntity pokemon) {
|
||||
var passengers = new ArrayList<Entity>();
|
||||
for (int passenger : packet.getPassengers()) {
|
||||
passengers.add(this.level.getEntity(passenger));
|
||||
}
|
||||
pokemon.getOccupiedSeats().values().stream()
|
||||
.filter(x -> !passengers.contains(x))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(Entity::stopRiding);
|
||||
|
||||
// Mount passengers not yet in the map
|
||||
var currentPassengers = new ArrayList<>(pokemon.getOccupiedSeats().values());
|
||||
passengers.stream()
|
||||
.filter(x -> !currentPassengers.contains(x))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(x -> x.startRiding(pokemon, true));
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.mixin.accessor.SinglePoolElementAccessor;
|
||||
import com.cobblemon.mod.common.world.structureprocessors.HeightRangeStructureProcessor;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.*;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.alias.PoolAliasLookup;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.LiquidSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(JigsawPlacement.class)
|
||||
public class JigsawPlacementMixin {
|
||||
|
||||
@Inject(
|
||||
method = "addPieces(Lnet/minecraft/world/level/levelgen/structure/Structure$GenerationContext;Lnet/minecraft/core/Holder;Ljava/util/Optional;ILnet/minecraft/core/BlockPos;ZLjava/util/Optional;ILnet/minecraft/world/level/levelgen/structure/pools/alias/PoolAliasLookup;Lnet/minecraft/world/level/levelgen/structure/pools/DimensionPadding;Lnet/minecraft/world/level/levelgen/structure/templatesystem/LiquidSettings;)Ljava/util/Optional;",
|
||||
at = @At("RETURN"),
|
||||
cancellable = true
|
||||
)
|
||||
private static void onAddPieces(
|
||||
Structure.GenerationContext context,
|
||||
Holder<StructureTemplatePool> startPool,
|
||||
Optional<ResourceLocation> startJigsawName,
|
||||
int maxDepth,
|
||||
BlockPos pos,
|
||||
boolean useExpansionHack,
|
||||
Optional<Heightmap.Types> projectStartToHeightmap,
|
||||
int maxDistanceFromCenter,
|
||||
PoolAliasLookup aliasLookup,
|
||||
DimensionPadding dimensionPadding,
|
||||
LiquidSettings liquidSettings,
|
||||
CallbackInfoReturnable<Optional<Structure.GenerationStub>> cir
|
||||
) {
|
||||
Optional<Structure.GenerationStub> result = cir.getReturnValue();
|
||||
if (result.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
BlockPos spawnPos = result.get().position();
|
||||
boolean canPlace = cobblemon$canAnyStartElementPlace(context, startPool, spawnPos);
|
||||
|
||||
if (canPlace) {
|
||||
return;
|
||||
}
|
||||
|
||||
cir.setReturnValue(Optional.empty());
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static boolean cobblemon$canAnyStartElementPlace(
|
||||
Structure.GenerationContext generationContext,
|
||||
Holder<StructureTemplatePool> startPool,
|
||||
BlockPos startPos
|
||||
) {
|
||||
for (StructurePoolElement element : startPool.value().templates) {
|
||||
Optional<HeightRangeStructureProcessor> heightRangeProcessor = cobblemon$getHeightRangeProcessor(element);
|
||||
|
||||
if (heightRangeProcessor.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
HeightRangeStructureProcessor processor = heightRangeProcessor.get();
|
||||
boolean valid = processor.isValidPlacement(
|
||||
generationContext.chunkGenerator(),
|
||||
generationContext.heightAccessor(),
|
||||
generationContext.randomState(),
|
||||
startPos
|
||||
);
|
||||
|
||||
if (valid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static Optional<HeightRangeStructureProcessor> cobblemon$getHeightRangeProcessor(StructurePoolElement element) {
|
||||
if (!(element instanceof SinglePoolElement)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
StructureProcessorList processorList = ((SinglePoolElementAccessor) element).getProcessors().value();
|
||||
for (StructureProcessor processor : processorList.list()) {
|
||||
if (processor instanceof HeightRangeStructureProcessor heightRangeStructureProcessor) {
|
||||
return Optional.of(heightRangeStructureProcessor);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.duck.MinecraftServerDuck;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(MinecraftServer.class)
|
||||
public class MinecraftServerMixin implements MinecraftServerDuck {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Map<ResourceKey<Level>, ServerLevel> levels;
|
||||
|
||||
@Override
|
||||
public Optional<ResourceKey<Level>> cobblemon$getLevelKey(final Level level) {
|
||||
return this.levels.entrySet()
|
||||
.stream()
|
||||
// Reference equality is intended here, the level we're handed is the same one as in the
|
||||
// map so we don't need to check value semantics.
|
||||
.filter(entry -> entry.getValue() == level)
|
||||
.map(Map.Entry::getKey)
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,6 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -115,13 +114,15 @@ public abstract class ServerEntityMixin {
|
||||
if (currentPassengers.equals(cobblemon$lastSeatPassengers)) return;
|
||||
cobblemon$lastSeatPassengers = List.copyOf(currentPassengers);
|
||||
|
||||
var assignments = new LinkedHashMap<String, Integer>();
|
||||
pokemon.getOccupiedSeats().forEach((seat, passenger) -> {
|
||||
if (seat.getLocator() == null) return;
|
||||
if (passenger == null) return;
|
||||
assignments.put(seat.getLocator(), passenger.getId());
|
||||
cobblemon$broadcast(new ClientboundSeatAssignmentPacket(
|
||||
passenger.getId(),
|
||||
pokemon.getId(),
|
||||
seat.getLocator()
|
||||
));
|
||||
});
|
||||
cobblemon$broadcast(new ClientboundSeatAssignmentPacket(pokemon.getId(), assignments));
|
||||
}
|
||||
|
||||
private void cobblemon$broadcast(NetworkPacket<?> packet) {
|
||||
|
||||
@@ -8,69 +8,22 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.duck.MinecraftServerDuck;
|
||||
import com.cobblemon.mod.common.duck.ServerLevelDuck;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import net.minecraft.world.level.storage.WritableLevelData;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
||||
@Mixin(ServerLevel.class)
|
||||
public abstract class ServerLevelMixin extends Level implements ServerLevelDuck {
|
||||
|
||||
// Ignored by Mixin, fulfils Java contract
|
||||
protected ServerLevelMixin(
|
||||
WritableLevelData levelData,
|
||||
ResourceKey<Level> dimension,
|
||||
RegistryAccess registryAccess,
|
||||
Holder<DimensionType> dimensionTypeRegistration,
|
||||
Supplier<ProfilerFiller> profiler,
|
||||
boolean isClientSide,
|
||||
boolean isDebug,
|
||||
long biomeZoomSeed,
|
||||
int maxChainedNeighborUpdates
|
||||
) {
|
||||
super(levelData, dimension, registryAccess, dimensionTypeRegistration, profiler, isClientSide, isDebug, biomeZoomSeed, maxChainedNeighborUpdates);
|
||||
}
|
||||
|
||||
@Shadow
|
||||
public abstract MinecraftServer shadow$getServer();
|
||||
|
||||
/**
|
||||
* Cached to speed up level world key checks every tick.
|
||||
*/
|
||||
private ResourceLocation cobblemon$levelResourceLocation;
|
||||
|
||||
@Override
|
||||
public ResourceLocation cobblemon$getLevelResourceLocation() {
|
||||
if (this.cobblemon$levelResourceLocation == null) {
|
||||
this.cobblemon$levelResourceLocation = ((MinecraftServerDuck) this.shadow$getServer())
|
||||
.cobblemon$getLevelKey(this)
|
||||
.map(ResourceKey::location)
|
||||
.orElse(null);
|
||||
}
|
||||
return this.cobblemon$levelResourceLocation;
|
||||
}
|
||||
public abstract class ServerLevelMixin {
|
||||
|
||||
@Inject(method = "findLightningRod", at = @At(value = "RETURN"), cancellable = true)
|
||||
private void cobblemon$findLightningRod(BlockPos blockPos, CallbackInfoReturnable<Optional<BlockPos>> ci) {
|
||||
|
||||
@@ -15,18 +15,15 @@ import com.cobblemon.mod.common.api.riding.RidingStyle;
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner;
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawnerFactory;
|
||||
import com.cobblemon.mod.common.api.stats.CobblemonStats;
|
||||
import com.cobblemon.mod.common.duck.ServerLevelDuck;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
@@ -36,13 +33,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ServerPlayer.class)
|
||||
public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
|
||||
|
||||
@Shadow
|
||||
public abstract ServerLevel serverLevel();
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
public MinecraftServer server;
|
||||
@Unique
|
||||
public PlayerSpawner cobblemon$spawner;
|
||||
|
||||
@@ -62,9 +55,12 @@ public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
|
||||
|
||||
@Inject(method = "tick", at = @At("TAIL"))
|
||||
private void cobblemon$tickSpawner(CallbackInfo ci) {
|
||||
if (cobblemon$canSpawn()) {
|
||||
getPlayerSpawner().tick();
|
||||
if (!Cobblemon.config.getEnableSpawning()) {
|
||||
return;
|
||||
} else if (!serverLevel().getGameRules().getBoolean(CobblemonGameRules.DO_POKEMON_SPAWNING)) {
|
||||
return;
|
||||
}
|
||||
getPlayerSpawner().tick();
|
||||
}
|
||||
|
||||
@Inject(method = "rideTick", at = @At("HEAD"))
|
||||
@@ -125,21 +121,4 @@ public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether we should spawn Cobblemon entities here.
|
||||
*
|
||||
* @return true if we can spawn cobblemon in the current level,
|
||||
* false otherwise
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
@Unique
|
||||
private boolean cobblemon$canSpawn() {
|
||||
return Cobblemon.config.getEnableSpawning() &&
|
||||
this.serverLevel().getGameRules().getBoolean(CobblemonGameRules.DO_POKEMON_SPAWNING) &&
|
||||
// intentionally last as this is a more expensive check, even with all the caching
|
||||
!Cobblemon.config.getWorldSpawningBlocklist().contains(
|
||||
((ServerLevelDuck) this.serverLevel()).cobblemon$getLevelResourceLocation()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.mixin.accessor.SinglePoolElementAccessor;
|
||||
import com.cobblemon.mod.common.world.structureprocessors.HeightRangeStructureProcessor;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece;
|
||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||
import net.minecraft.world.level.levelgen.structure.StructurePiece;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Mixin(Structure.class)
|
||||
public abstract class StructureMixin {
|
||||
|
||||
/**
|
||||
* Discards a structure start when any of its pieces carries a {@link HeightRangeStructureProcessor}
|
||||
* whose range the piece does not fit in.
|
||||
* Crab Note: We have to make this has to happen here rather than in the processor itself. Terrain adaptation and {@code /locate}
|
||||
* both read the start, so by the time processors run the terrain has already been generated and the
|
||||
* structure is already on the map which previously were left behdind when we were trying to make some structures not generate via processor. Now
|
||||
* Returning {@link StructureStart#INVALID_START} keeps the start from ever existing, which makes sure that weird generation isn't left behind.
|
||||
*/
|
||||
@Inject(
|
||||
method = "generate",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/structure/StructureStart;isValid()Z"),
|
||||
cancellable = true
|
||||
)
|
||||
private void cobblemon$rejectStartsOutsideHeightRange(
|
||||
RegistryAccess registryAccess, ChunkGenerator chunkGenerator, BiomeSource biomeSource,
|
||||
RandomState randomState, StructureTemplateManager structureTemplateManager, long seed,
|
||||
ChunkPos chunkPos, int references, LevelHeightAccessor heightAccessor,
|
||||
Predicate<Holder<Biome>> validBiome,
|
||||
CallbackInfoReturnable<StructureStart> cir,
|
||||
@Local StructureStart structureStart
|
||||
) {
|
||||
for (StructurePiece piece : structureStart.getPieces()) {
|
||||
if (!(piece instanceof PoolElementStructurePiece poolPiece)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Optional<HeightRangeStructureProcessor> heightRangeProcessor = cobblemon$getHeightRangeProcessor(poolPiece.getElement());
|
||||
if (heightRangeProcessor.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean valid = heightRangeProcessor.get().isValidPlacement(
|
||||
chunkGenerator,
|
||||
heightAccessor,
|
||||
randomState,
|
||||
piece.getBoundingBox()
|
||||
);
|
||||
|
||||
if (!valid) {
|
||||
cir.setReturnValue(StructureStart.INVALID_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static Optional<HeightRangeStructureProcessor> cobblemon$getHeightRangeProcessor(StructurePoolElement element) {
|
||||
if (!(element instanceof SinglePoolElement)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
StructureProcessorList processorList = ((SinglePoolElementAccessor) element).getProcessors().value();
|
||||
for (StructureProcessor processor : processorList.list()) {
|
||||
if (processor instanceof HeightRangeStructureProcessor heightRangeStructureProcessor) {
|
||||
return Optional.of(heightRangeStructureProcessor);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -21,12 +21,14 @@ import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.mojang.blaze3d.Blaze3D;
|
||||
import kotlin.Pair;
|
||||
import kotlin.jvm.functions.Function4;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.MouseHandler;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.SmoothDouble;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.joml.Vector2d;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
@@ -149,6 +151,12 @@ public abstract class MouseHandlerMixin {
|
||||
pokeVehicle.clampPassengerRotation(player);
|
||||
}
|
||||
|
||||
for(Function4<LocalPlayer, Double, Double, Double, Vector2d> rideFun : CobblemonClient.INSTANCE.getRidingTurnInputHandler().values()) {
|
||||
Vector2d vec = rideFun.invoke(player, cursorDeltaX, cursorDeltaY, d);
|
||||
cursorDeltaX = vec.x;
|
||||
cursorDeltaY = vec.y;
|
||||
}
|
||||
|
||||
// If the ride is not orientation controllable then return and perform the normal turnPlayer logic
|
||||
if (!(vehicle instanceof OrientationControllable controllableVehicle) || !controllableVehicle.getOrientationController().isActive()) {
|
||||
xMouseSmoother.reset();
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.integration.controlify;
|
||||
|
||||
import com.cobblemon.mod.common.client.CobblemonClient;
|
||||
import com.cobblemon.mod.common.client.integration.controlify.CobblemonControlifyEntrypoint;
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext;
|
||||
import dev.isxander.controlify.controller.ControllerEntity;
|
||||
import dev.isxander.controlify.ingame.InGameInputHandler;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(InGameInputHandler.class)
|
||||
public class InGameInputHandlerMixin {
|
||||
@Shadow @Final private ControllerEntity controller;
|
||||
|
||||
@Redirect(
|
||||
method = "handleKeybinds()V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/entity/player/Inventory;swapPaint(D)V"
|
||||
)
|
||||
)
|
||||
public void cobblemon$onHotbarSwitch(Inventory instance, double direction) {
|
||||
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
|
||||
if (usageContext.getScanningGuiOpen()) {
|
||||
usageContext.adjustZoom(direction);
|
||||
return;
|
||||
}
|
||||
|
||||
if (CobblemonControlifyEntrypoint.Companion.getSEND_OUT_POKEMON().on(controller).digitalNow()) {
|
||||
CobblemonClient.INSTANCE.getStorage().shiftSelected(direction < 0);
|
||||
PartySendBinding.INSTANCE.actioned();
|
||||
return;
|
||||
}
|
||||
|
||||
instance.swapPaint(direction);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
// There appears to be a data driven way to do this now, however I am not sure how it works since there is no wiki or anything so yolo
|
||||
//This is a yucky hack but better than what we were doing before
|
||||
@Mixin(BlockVariantSets.class)
|
||||
public abstract class AdornRegisterInvoker {
|
||||
@Final
|
||||
|
||||
@@ -133,7 +133,7 @@ open class PokemonBattle(
|
||||
set(value) { field = value.coerceAtMost(Int.MAX_VALUE) } // go outside
|
||||
|
||||
/** The current duration of the battle in seconds. */
|
||||
val time: Int get() = ticks / 20
|
||||
val time: Int get() = ticks % 20
|
||||
|
||||
var dispatchResult = GO
|
||||
val dispatches = ConcurrentLinkedDeque<BattleDispatch>()
|
||||
|
||||
@@ -65,7 +65,7 @@ class HabitatSpawn {
|
||||
/** The maximum light level that the Pokémon can spawn in. */
|
||||
var maxLight: Int? = null
|
||||
|
||||
fun writeToNBT(nbt: CompoundTag) {
|
||||
open fun writeToNBT(nbt: CompoundTag) {
|
||||
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_SPECIES, species.resourceIdentifier.toString())
|
||||
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_POSITION_TYPE, spawnablePositionType)
|
||||
nbt.putFloat(DataKeys.HABITAT_POOL_SPAWN_WEIGHT, weight)
|
||||
@@ -78,7 +78,7 @@ class HabitatSpawn {
|
||||
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_BUCKET, bucket)
|
||||
}
|
||||
|
||||
fun readFromNBT(nbt: CompoundTag) {
|
||||
open fun readFromNBT(nbt: CompoundTag) {
|
||||
species = PokemonSpecies.getByIdentifier(nbt.getString(DataKeys.HABITAT_POOL_SPAWN_SPECIES).asIdentifierDefaultingNamespace())!!
|
||||
spawnablePositionType = nbt.getString(DataKeys.HABITAT_POOL_SPAWN_POSITION_TYPE)
|
||||
weight = nbt.getFloat(DataKeys.HABITAT_POOL_SPAWN_WEIGHT)
|
||||
@@ -91,7 +91,7 @@ class HabitatSpawn {
|
||||
bucket = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_BUCKET)) nbt.getString(DataKeys.HABITAT_POOL_SPAWN_BUCKET) else "common"
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
open fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(species.resourceIdentifier)
|
||||
buffer.writeUtf(spawnablePositionType)
|
||||
buffer.writeFloat(weight)
|
||||
@@ -104,7 +104,7 @@ class HabitatSpawn {
|
||||
buffer.writeUtf(bucket)
|
||||
}
|
||||
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
open fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
species = PokemonSpecies.getByIdentifier(buffer.readResourceLocation())!!
|
||||
spawnablePositionType = buffer.readUtf()
|
||||
weight = buffer.readFloat()
|
||||
@@ -118,7 +118,7 @@ class HabitatSpawn {
|
||||
}
|
||||
|
||||
/** Creates the [PokemonSpawnDetail] that will be used in the spawner. This is done per habitat block. */
|
||||
fun createSpawnDetail(habitatBlockEntity: HabitatBlockEntity): PokemonSpawnDetail? {
|
||||
open fun createSpawnDetail(habitatBlockEntity: HabitatBlockEntity): PokemonSpawnDetail? {
|
||||
val spawnDetail = PokemonSpawnDetail()
|
||||
|
||||
// If the habitat block specifies a level range, it restricts how and if this spawn exists
|
||||
|
||||
@@ -109,10 +109,8 @@ open class Learnset {
|
||||
evolutionMoves
|
||||
}
|
||||
|
||||
// Syncs level up, TM, and egg moves.
|
||||
// We only sync level up moves atm
|
||||
fun shouldSynchronize(other: Learnset) = other.levelUpMoves != this.levelUpMoves
|
||||
|| other.tmMoves != this.tmMoves
|
||||
|| other.eggMoves != this.eggMoves
|
||||
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
this.levelUpMoves.clear()
|
||||
@@ -124,8 +122,6 @@ open class Learnset {
|
||||
}
|
||||
levelUpMoves[level] = moves
|
||||
}
|
||||
decodeMoveList(buffer, this.tmMoves)
|
||||
decodeMoveList(buffer, this.eggMoves)
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
@@ -137,21 +133,5 @@ open class Learnset {
|
||||
buffer.writeInt(move.num)
|
||||
}
|
||||
}
|
||||
encodeMoveList(buffer, this.tmMoves)
|
||||
encodeMoveList(buffer, this.eggMoves)
|
||||
}
|
||||
|
||||
private fun decodeMoveList(buffer: RegistryFriendlyByteBuf, list: MutableList<MoveTemplate>) {
|
||||
list.clear()
|
||||
repeat(times = buffer.readSizedInt(IntSize.U_SHORT)) {
|
||||
Moves.getByNumericalId(buffer.readInt())?.let(list::add)
|
||||
}
|
||||
}
|
||||
|
||||
private fun encodeMoveList(buffer: RegistryFriendlyByteBuf, list: List<MoveTemplate>) {
|
||||
buffer.writeSizedInt(IntSize.U_SHORT, list.size)
|
||||
for (move in list) {
|
||||
buffer.writeInt(move.num)
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-24
@@ -40,10 +40,6 @@ import kotlin.math.*
|
||||
class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("air/bird")
|
||||
|
||||
private const val FLY_POSE_SPEED = 0.1
|
||||
private const val GLIDE_ENTRY_SPEED = 0.19
|
||||
private const val GLIDE_EXIT_SPEED = 0.13
|
||||
}
|
||||
|
||||
override val key: ResourceLocation = KEY
|
||||
@@ -53,7 +49,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<BirdSettings, BirdState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > FLY_POSE_SPEED })
|
||||
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.2 })
|
||||
|
||||
val globalBird: BirdSettings
|
||||
get() = CobblemonRideSettings.bird
|
||||
@@ -196,23 +192,21 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
}
|
||||
|
||||
// speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
|
||||
activeInput = true
|
||||
|
||||
if (newVelocity.length() < glideTopSpeed) {
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
state.stamina.get() == 0.0f -> 0.0
|
||||
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
|
||||
driver.zza < 0 && newVelocity.z < (-topSpeed / 3.0) -> 0.0
|
||||
else -> driver.zza.sign
|
||||
}
|
||||
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
(newVelocity.z + (accel * forwardInput.toDouble())))
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0 && newVelocity.length() < glideTopSpeed) {
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
state.stamina.get() == 0.0f -> 0.0
|
||||
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
|
||||
driver.zza < 0 && newVelocity.z < (-topSpeed / 3.0) -> 0.0
|
||||
else -> driver.zza.sign
|
||||
}
|
||||
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
(newVelocity.z + (accel * forwardInput.toDouble())))
|
||||
|
||||
activeInput = true
|
||||
}
|
||||
|
||||
val controller = (vehicle as? OrientationControllable)?.orientationController
|
||||
@@ -270,9 +264,11 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
|
||||
//Check if the ride should be gliding
|
||||
if (driver.isLocalPlayer) {
|
||||
val powered = activeInput && state.stamina.get() > 0.0
|
||||
val requiredSpeed = if (state.gliding.get()) GLIDE_EXIT_SPEED else GLIDE_ENTRY_SPEED
|
||||
state.gliding.set(!powered && newVelocity.z >= requiredSpeed)
|
||||
if ((activeInput && state.stamina.get() > 0.0) || state.rideVelocity.get().z < 0.2) {
|
||||
state.gliding.set(false)
|
||||
} else {
|
||||
state.gliding.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
//air resistance
|
||||
|
||||
+9
-7
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
@@ -144,11 +145,12 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
) {
|
||||
val sprintTimerTickLength = 7 // Emulate vanillas tick timer length
|
||||
var hasDoubleTapped = false
|
||||
val forward = Minecraft.getInstance().player?.input?.up == true
|
||||
|
||||
// If you are not sprinting and you just pressed down and theres no
|
||||
if (!state.forwardHeldLastTick.get() && Minecraft.getInstance().options.keyUp.isDown() && !state.sprinting.get() && state.sprintTickTimer.get() == 0) {
|
||||
if (!state.forwardHeldLastTick.get() && forward && !state.sprinting.get() && state.sprintTickTimer.get() == 0) {
|
||||
state.sprintTickTimer.set(sprintTimerTickLength)
|
||||
} else if (!state.forwardHeldLastTick.get() && Minecraft.getInstance().options.keyUp.isDown() && state.sprintTickTimer.get() != 0) { // If has double tapped
|
||||
} else if (!state.forwardHeldLastTick.get() && forward && state.sprintTickTimer.get() != 0) { // If has double tapped
|
||||
hasDoubleTapped = true
|
||||
}else if(!state.sprinting.get() && state.sprintTickTimer.get() > 0) {
|
||||
state.sprintTickTimer.set(state.sprintTickTimer.get() - 1)
|
||||
@@ -156,9 +158,9 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
state.sprintTickTimer.set(0)
|
||||
}
|
||||
|
||||
val tryingToSprint = (Minecraft.getInstance().options.keySprint.isDown() && Minecraft.getInstance().options.keyUp.isDown()) || hasDoubleTapped
|
||||
val tryingToSprint = hasDoubleTapped || CobblemonClient.ridingSprintHandler.any { it.value.invoke() }
|
||||
|
||||
if (state.stamina.get() <= 0.0f || !Minecraft.getInstance().options.keyUp.isDown()) {
|
||||
if (state.stamina.get() <= 0.0f || !forward) {
|
||||
// If stamina runs out or the player is not holding forward then stop sprinting
|
||||
state.sprinting.set(false)
|
||||
if (state.stamina.get() <= 0.0f) {
|
||||
@@ -172,8 +174,8 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
state.sprinting.set(true)
|
||||
}
|
||||
|
||||
// Take not of whether forward was held last tick
|
||||
state.forwardHeldLastTick.set(Minecraft.getInstance().options.keyUp.isDown())
|
||||
// Take note of whether forward was held last tick
|
||||
state.forwardHeldLastTick.set(forward)
|
||||
}
|
||||
|
||||
fun tickStamina(
|
||||
@@ -193,7 +195,7 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
val newStam = if (state.sprinting.get()) {
|
||||
max(0.0f,stam - stamDrainRate)
|
||||
} else {
|
||||
min(1.0f,stam + stamDrainRate * 8) // Need to change this to probably be in seconds eventually? idk something that isn't this magic number.
|
||||
min(1.0f,stam + stamDrainRate * 4)
|
||||
}
|
||||
|
||||
state.stamina.set(newStam.coerceIn(0F, 1F))
|
||||
|
||||
+1
-1
@@ -147,7 +147,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
val stamDrainRate = (1.0f / boostTime).toFloat()
|
||||
|
||||
val newStam = if (state.boosting.get()) max(0.0f,stam - stamDrainRate)
|
||||
else (stam + stamDrainRate * 2).coerceIn(0F, 1F)
|
||||
else (stam + stamDrainRate).coerceIn(0F, 1F)
|
||||
|
||||
state.stamina.set(newStam)
|
||||
}
|
||||
|
||||
+1
-5
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.spawning.MoonPhaseRange
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.block.entity.PokeSnackBlockEntity
|
||||
import com.cobblemon.mod.common.util.Merger
|
||||
import com.cobblemon.mod.common.util.math.orMax
|
||||
import com.cobblemon.mod.common.util.math.orMin
|
||||
@@ -60,7 +59,6 @@ abstract class SpawningCondition<T : SpawnablePosition> {
|
||||
var structures: MutableList<Either<ResourceLocation, TagKey<Structure>>>? = null
|
||||
var isSlimeChunk: Boolean? = null
|
||||
var markers: MutableList<String>? = null
|
||||
var isPokeSnack: Boolean? = null
|
||||
|
||||
@Transient
|
||||
var appendages = mutableListOf<AppendageCondition>()
|
||||
@@ -77,9 +75,7 @@ abstract class SpawningCondition<T : SpawnablePosition> {
|
||||
}
|
||||
|
||||
protected open fun fits(spawnablePosition: T): Boolean {
|
||||
if (isPokeSnack != null && (spawnablePosition.spawner is PokeSnackBlockEntity.Spawner != isPokeSnack!!)) {
|
||||
return false
|
||||
} else if (spawnablePosition.position.x < minX.orMin() || spawnablePosition.position.x > maxX.orMax()) {
|
||||
if (spawnablePosition.position.x < minX.orMin() || spawnablePosition.position.x > maxX.orMax()) {
|
||||
return false
|
||||
} else if (spawnablePosition.position.y < minY.orMin() || spawnablePosition.position.y > maxY.orMax()) {
|
||||
return false
|
||||
|
||||
-1
@@ -32,7 +32,6 @@ import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
|
||||
class EmptySpawnDetail : SpawnDetail() {
|
||||
companion object {
|
||||
val TYPE = "empty"
|
||||
val EMPTY = EmptySpawnDetail()
|
||||
}
|
||||
|
||||
override val type: String = TYPE
|
||||
|
||||
+24
-42
@@ -11,19 +11,14 @@ package com.cobblemon.mod.common.api.spawning.detail
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.spawning.position.AreaSpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth.clamp
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import kotlin.math.ceil
|
||||
|
||||
/**
|
||||
* A spawn detail that spawns a herd of Pokémon. This relies on the same [PokemonSpawnAction]s as
|
||||
@@ -41,6 +36,7 @@ import kotlin.math.ceil
|
||||
class PokemonHerdSpawnDetail : SpawnDetail() {
|
||||
companion object {
|
||||
val TYPE = "pokemon-herd"
|
||||
const val HERD_LEADER = "leader"
|
||||
}
|
||||
|
||||
override val type: String = TYPE
|
||||
@@ -54,14 +50,14 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
|
||||
get() = "${id}__LEVEL"
|
||||
|
||||
class Herdable {
|
||||
var pokemon = PokemonProperties()
|
||||
var pokemon: PokemonProperties = PokemonProperties()
|
||||
val levelRange: IntRange? = null
|
||||
var levelRangeOffset: IntRange? = null
|
||||
val herdLevelRange: IntRange? = null
|
||||
var dropTable: DropTable? = null
|
||||
var isLeader = false
|
||||
var isFollower = true
|
||||
var weight = 1F
|
||||
var isLeader: Boolean = false
|
||||
var isFollower: Boolean = true
|
||||
var weight: Float = 1F
|
||||
var maxTimes = 10
|
||||
var heldItem: ResourceLocation? = null
|
||||
}
|
||||
@@ -73,23 +69,22 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
|
||||
selectionData: SpawnSelectionData
|
||||
) {
|
||||
selectionData.removeSpawnDetails { it != this }
|
||||
selectionData.guaranteedBucket = bucket
|
||||
selectionData.removeSpawnablePositions { _, pos -> pos.distanceTo(spawnablePosition) < minDistanceBetweenSpawns }
|
||||
|
||||
val herdSpawnCount = selectionData.spawnActions.count { it.detail == this } + 1
|
||||
val lacksPossibleLeader = lacksPossibleLeader(selectionData)
|
||||
|
||||
if (herdSpawnCount >= maxHerdSize || getValidHerdMembersForRole(selectionData, useLeaderRole = lacksPossibleLeader).isEmpty()) {
|
||||
if (herdSpawnCount >= maxHerdSize || getValidHerdMembers(selectionData).isEmpty()) {
|
||||
// We can't spawn any more in the herd so remove this spawn from the selection pool.
|
||||
selectionData.removeSpawnDetails { it == this }
|
||||
}
|
||||
}
|
||||
|
||||
fun lacksPossibleLeader(selectionData: SpawnSelectionData): Boolean {
|
||||
val level = getOrSetHerdLevel(selectionData)
|
||||
val herdable = getValidHerdMembers(selectionData)
|
||||
val herdCounts = getHerdMemberCounts(selectionData)
|
||||
|
||||
val leaderIsPossible = herdable.any { it.isLeader }
|
||||
val leaderIsPossible = herdable.any { it.herdLevelRange?.contains(level) != false && it.isLeader }
|
||||
val leaderIsSelected = herdCounts.any { it.key?.isLeader == true && it.value > 0 }
|
||||
return leaderIsPossible && !leaderIsSelected
|
||||
}
|
||||
@@ -98,42 +93,25 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
|
||||
return (selectionData.context[herdLevelKey] as? Int) ?: run {
|
||||
val level = levelRange.random()
|
||||
selectionData.context[herdLevelKey] = level
|
||||
level
|
||||
return level
|
||||
}
|
||||
}
|
||||
|
||||
private fun getValidHerdMembersForRole(selectionData: SpawnSelectionData, useLeaderRole: Boolean): List<Herdable> {
|
||||
return getValidHerdMembers(selectionData).filter { (useLeaderRole && it.isLeader) || (!useLeaderRole && it.isFollower) }
|
||||
}
|
||||
|
||||
override fun createSpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: String,
|
||||
selectionData: SpawnSelectionData
|
||||
): SpawnAction<*> {
|
||||
val herdable = getValidHerdMembersForRole(
|
||||
selectionData = selectionData,
|
||||
useLeaderRole = lacksPossibleLeader(selectionData)
|
||||
).weightedSelection { it.weight } ?: herdablePokemon[0]
|
||||
|
||||
var level = selectionData.context[herdLevelKey] as? Int ?: 1
|
||||
|
||||
// Attempt to do more detailed anti-wall-spawn logic. Herd members could spawn in walls because
|
||||
// we've just been assuming they're 1 block wide (or at least, all in the herd have the same size).
|
||||
// Some pokemon are not that though.
|
||||
if (spawnablePosition is AreaSpawnablePosition) {
|
||||
val species = herdable.pokemon.species?.let { PokemonSpecies.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
|
||||
if (species != null) {
|
||||
val height = ceil(species.hitbox.height * species.baseScale).toInt()
|
||||
val width = ceil(species.hitbox.width * species.baseScale).toInt()
|
||||
if (!spawnablePosition.hasSpace(width, height)) {
|
||||
return EmptySpawnAction(spawnablePosition, bucket, EmptySpawnDetail.EMPTY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
level = herdable.levelRange?.let { clamp(level, it.first, it.last) } ?: level
|
||||
): PokemonSpawnAction {
|
||||
val validHerdMembers = getValidHerdMembers(selectionData)
|
||||
var searchedForLeader = false
|
||||
val herdable = if (lacksPossibleLeader(selectionData)) {
|
||||
searchedForLeader = true
|
||||
validHerdMembers.filter { it.isLeader }
|
||||
} else {
|
||||
validHerdMembers.filter { it.isFollower }
|
||||
}.weightedSelection { it.weight } ?: herdablePokemon[0]
|
||||
|
||||
val level = selectionData.context[herdLevelKey] as? Int ?: 1
|
||||
val levelRange = herdable.levelRangeOffset?.let { offset ->
|
||||
val min = level + offset.first
|
||||
val max = level + offset.last
|
||||
@@ -158,7 +136,11 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
|
||||
drops = herdable.dropTable,
|
||||
heldItem = heldItem,
|
||||
levelRange = levelRange
|
||||
)
|
||||
).also {
|
||||
if (herdable.isLeader && searchedForLeader) {
|
||||
it.labels += HERD_LEADER
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getHerdMemberCounts(selectionData: SpawnSelectionData): Map<Herdable?, Int> {
|
||||
|
||||
+22
-27
@@ -53,39 +53,34 @@ open class AreaSpawnablePosition(
|
||||
*/
|
||||
open fun isSafeSpace(world: ServerLevel, pos: BlockPos, state: BlockState): Boolean = !state.isSolid
|
||||
|
||||
fun hasSpace(width: Int, height: Int): Boolean {
|
||||
val sizeX = width.takeIf { it > 0 } ?: 1
|
||||
val sizeY = height.takeIf { it > 0 } ?: 1
|
||||
|
||||
val minX = floor(position.x + 0.5 - (sizeX - 1) / 2F).toInt() - 1
|
||||
val maxX = ceil(position.x + 0.5 + (sizeX + 1) / 2F).toInt() + 1
|
||||
|
||||
val maxY = ceil(position.y + (sizeY + 1) / 2F).toInt() + 1
|
||||
|
||||
val minZ = floor(position.z + 0.5 - (sizeX - 1) / 2F).toInt() - 1
|
||||
val maxZ = ceil(position.z + 0.5 + (sizeX + 1) / 2F).toInt() + 1
|
||||
|
||||
val mutable = BlockPos.MutableBlockPos()
|
||||
for (x in minX until maxX) {
|
||||
for (y in (position.y + 1)..maxY) {
|
||||
for (z in minZ until maxZ) {
|
||||
val state = zone.getBlockState(mutable.set(x, y, z))
|
||||
if (!isSafeSpace(world, mutable, state)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun postFilter(detail: SpawnDetail): Boolean {
|
||||
if (!super.postFilter(detail)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (detail.width > 1 || detail.height > 1) {
|
||||
return hasSpace(detail.width, detail.height)
|
||||
val sizeX = detail.width.takeIf { it > 0 } ?: 1
|
||||
val sizeY = detail.height.takeIf { it > 0 } ?: 1
|
||||
|
||||
val minX = floor(position.x + 0.5 - (sizeX - 1) / 2F).toInt() - 1
|
||||
val maxX = ceil(position.x + 0.5 + (sizeX + 1) / 2F).toInt() + 1
|
||||
|
||||
val maxY = ceil(position.y + (sizeY + 1) / 2F).toInt() + 1
|
||||
|
||||
val minZ = floor(position.z + 0.5 - (sizeX - 1) / 2F).toInt() - 1
|
||||
val maxZ = ceil(position.z + 0.5 + (sizeX + 1) / 2F).toInt() + 1
|
||||
|
||||
val mutable = BlockPos.MutableBlockPos()
|
||||
for (x in minX until maxX) {
|
||||
for (y in (position.y + 1)..maxY) {
|
||||
for (z in minZ until maxZ) {
|
||||
val state = zone.getBlockState(mutable.set(x, y, z))
|
||||
if (!isSafeSpace(world, mutable, state)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
+3
-3
@@ -28,7 +28,7 @@ import java.io.InputStreamReader
|
||||
* @since July 8th, 2022
|
||||
*/
|
||||
class BestSpawnerConfig {
|
||||
val version = 2
|
||||
val version = 0
|
||||
/** Whether an external config will be replaced by an internal one once [version] is higher on the internal. */
|
||||
val replaceWithNewVersion = true
|
||||
@SerializedName("spawnablePositionTypeWeights", alternate = ["contextWeights"])
|
||||
@@ -40,11 +40,11 @@ class BestSpawnerConfig {
|
||||
)
|
||||
|
||||
val worldBuckets = mutableMapOf(
|
||||
"common" to 94.05F,
|
||||
"common" to 94F,
|
||||
"uncommon" to 5F,
|
||||
"rare" to 0.5F,
|
||||
"ultra-rare" to 0.2F,
|
||||
"boss" to 0.25F,
|
||||
"boss" to 0.3F,
|
||||
)
|
||||
|
||||
val fishingBuckets = mutableMapOf(
|
||||
|
||||
@@ -89,7 +89,6 @@ open class FlatSelector : SpawningSelector<FlatSelector.SpawnablePositionSelecti
|
||||
val spawnablePositions: List<SpawnablePosition>,
|
||||
val selectingSpawnInformation: MutableMap<String, SpawnablePositionBucketSelectionData>,
|
||||
): SpawnSelectionData {
|
||||
override var guaranteedBucket: String? = null
|
||||
override val spawnActions = mutableListOf<SpawnAction<*>>()
|
||||
override val context = mutableMapOf<String, Any>()
|
||||
|
||||
|
||||
-1
@@ -101,7 +101,6 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
|
||||
val spawner: Spawner,
|
||||
val spawnablePositions: List<SpawnablePosition>
|
||||
) : SpawnSelectionData {
|
||||
override var guaranteedBucket: String? = null
|
||||
override val spawnActions = mutableListOf<SpawnAction<*>>()
|
||||
override val context = mutableMapOf<String, Any>()
|
||||
|
||||
|
||||
-2
@@ -21,7 +21,6 @@ import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
* @since April 21st, 2025
|
||||
*/
|
||||
interface SpawnSelectionData {
|
||||
var guaranteedBucket: String?
|
||||
val context: MutableMap<String, Any>
|
||||
val spawnActions: MutableList<SpawnAction<*>>
|
||||
fun removeSpawnDetails(shouldRemove: (SpawnDetail) -> Boolean)
|
||||
@@ -29,7 +28,6 @@ interface SpawnSelectionData {
|
||||
|
||||
companion object {
|
||||
val EMPTY = object : SpawnSelectionData {
|
||||
override var guaranteedBucket: String? = null
|
||||
override val context: MutableMap<String, Any> = mutableMapOf()
|
||||
override val spawnActions: MutableList<SpawnAction<*>> = mutableListOf()
|
||||
override fun removeSpawnDetails(shouldRemove: (SpawnDetail) -> Boolean) {}
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ interface SpawningSelector<T : SpawnSelectionData> {
|
||||
val spawnActions = selectionData.spawnActions
|
||||
|
||||
while (spawnActions.size < maxSpawns) {
|
||||
val bucket = selectionData.guaranteedBucket ?: bucketSelector()
|
||||
val bucket = bucketSelector()
|
||||
val spawnAction = selectSpawnAction(
|
||||
spawner = spawner,
|
||||
bucket = bucket,
|
||||
|
||||
+2
-3
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.spawning.influence.BucketNormalizingInfluenc
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawnBaitInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.rules.SpawnRule
|
||||
import com.cobblemon.mod.common.block.entity.PokeSnackBlockEntity
|
||||
import com.cobblemon.mod.common.util.mutableLazy
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
@@ -67,10 +66,10 @@ object PokeSnackSpawnerFactory {
|
||||
},
|
||||
)
|
||||
|
||||
fun create(ctx: Context): PokeSnackBlockEntity.Spawner {
|
||||
fun create(ctx: Context): FixedAreaSpawner {
|
||||
val influences = influenceBuilders.mapNotNull { it(ctx) }
|
||||
|
||||
return PokeSnackBlockEntity.Spawner(
|
||||
return FixedAreaSpawner(
|
||||
name = ctx.name,
|
||||
spawnPool = spawnPool,
|
||||
buckets = Cobblemon.bestSpawner.config.pokeSnackBuckets,
|
||||
|
||||
@@ -46,7 +46,6 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
|
||||
pokemon.forEachIndexed { index, pokemon ->
|
||||
pokemon.storeCoordinates.set(StoreCoordinates(this, BottomlessPosition(index)))
|
||||
}
|
||||
rebuildPokemonByUuid()
|
||||
}
|
||||
|
||||
override fun saveToNBT(nbt: CompoundTag, registryAccess: RegistryAccess): CompoundTag {
|
||||
|
||||
@@ -53,7 +53,7 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
|
||||
* Runs initialization logic for this store, knowing that it has just been constructed in a [PokemonStoreFactory].
|
||||
*
|
||||
* The minimum of what this function should do is iterate over all the Pokémon in this store and set their store
|
||||
* coordinates. Built-in stores then rebuild the UUID index via [rebuildPokemonByUuid].
|
||||
* coordinates.
|
||||
*
|
||||
* If this does not get called, or it does not do its job properly, serious de-sync issues may follow.
|
||||
*/
|
||||
@@ -99,7 +99,6 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
|
||||
|
||||
setAtPosition(position, pokemon)
|
||||
pokemon.storeCoordinates.set(StoreCoordinates(this, position))
|
||||
pokemonByUuid[pokemon.uuid] = pokemon
|
||||
}
|
||||
|
||||
/** Swaps the Pokémon at the specified positions. If one of the spaces is empty, it will simply move the not-null one to that space. */
|
||||
@@ -148,26 +147,10 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
|
||||
pokemon.recall()
|
||||
pokemon.storeCoordinates.set(null)
|
||||
setAtPosition(currentPosition.position, null)
|
||||
pokemonByUuid.remove(pokemon.uuid)
|
||||
return true
|
||||
}
|
||||
|
||||
/** In-memory UUID index. Not serialized; rebuilt by [rebuildPokemonByUuid]. */
|
||||
private val pokemonByUuid = hashMapOf<UUID, Pokemon>()
|
||||
|
||||
/** Gets the [Pokemon] with this UUID, or null if it is not in this store. */
|
||||
operator fun get(uuid: UUID): Pokemon? = pokemonByUuid[uuid]
|
||||
|
||||
/**
|
||||
* Rebuilds the UUID index from the store's current contents.
|
||||
*
|
||||
* Call after load or any structural mutation that does not go through [set] / [remove], such as
|
||||
* filling slots directly from NBT/JSON or shrinking a PC.
|
||||
*/
|
||||
protected fun rebuildPokemonByUuid() {
|
||||
pokemonByUuid.clear()
|
||||
forEach { pokemonByUuid[it.uuid] = it }
|
||||
}
|
||||
operator fun get(uuid: UUID) = find { it.uuid.equals(uuid) }
|
||||
|
||||
open fun handleInvalidSpeciesNBT(nbt: CompoundTag) {
|
||||
Cobblemon.LOGGER.error("Failed to read unknown species: ${nbt.getString(DataKeys.POKEMON_SPECIES_IDENTIFIER)}")
|
||||
|
||||
@@ -138,7 +138,6 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
|
||||
val pokemon = get(slot) ?: continue
|
||||
pokemon.storeCoordinates.set(StoreCoordinates(this, PartyPosition(slot)))
|
||||
}
|
||||
rebuildPokemonByUuid()
|
||||
}
|
||||
|
||||
override fun onPokemonChanged(pokemon: Pokemon) {
|
||||
@@ -280,7 +279,7 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
|
||||
}
|
||||
}
|
||||
|
||||
return result.sortedBy { it.health <= 0 }
|
||||
return result
|
||||
}
|
||||
fun clearParty() {
|
||||
forEach {
|
||||
|
||||
@@ -105,7 +105,6 @@ open class PCStore(
|
||||
override fun initialize() {
|
||||
boxes.forEach { it.initialize() }
|
||||
backupStore.initialize()
|
||||
rebuildPokemonByUuid()
|
||||
}
|
||||
|
||||
fun relocateEvictedBoxPokemon(pokemon: Pokemon) {
|
||||
@@ -136,7 +135,6 @@ open class PCStore(
|
||||
|
||||
tryRestoreBackedUpPokemon()
|
||||
}
|
||||
rebuildPokemonByUuid()
|
||||
pcChangeObservable.emit(Unit)
|
||||
}
|
||||
|
||||
@@ -144,7 +142,6 @@ open class PCStore(
|
||||
this.lockedSize = lockNewSize
|
||||
boxes.removeAll(boxList)
|
||||
boxList.flatMap { it.asIterable() }.forEach(overflowHandler)
|
||||
rebuildPokemonByUuid()
|
||||
pcChangeObservable.emit(Unit)
|
||||
}
|
||||
|
||||
|
||||
@@ -66,8 +66,6 @@ object CobblemonBiomeTags {
|
||||
val IS_WARM_OCEAN = create("is_warm_ocean")
|
||||
@JvmField
|
||||
val IS_DEEP_DARK = create("is_deep_dark")
|
||||
@JvmField
|
||||
val IS_MOUNTAIN = create("is_mountain")
|
||||
|
||||
// Has Block tags
|
||||
@JvmField
|
||||
|
||||
+1
-1
@@ -166,7 +166,7 @@ object AIUtility {
|
||||
"geargrind" to Pair(2, 2),
|
||||
"twinbeam" to Pair(2, 2),
|
||||
"twineedle" to Pair(2, 2),
|
||||
"surgingstrikes" to Pair(3, 3),
|
||||
"suringstrikes" to Pair(3, 3),
|
||||
"tripledive" to Pair(3, 3),
|
||||
"watershuriken" to Pair(3, 3),
|
||||
|
||||
|
||||
+1
-2
@@ -39,6 +39,7 @@ class FaintInstruction(battle: PokemonBattle, val message: BattleMessage) : Inte
|
||||
battle.dispatchFuture {
|
||||
val (pnx, _) = message.pnxAndUuid(0) ?: return@dispatchFuture CompletableFuture.completedFuture(Unit)
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchFuture CompletableFuture.completedFuture(Unit)
|
||||
battle.sendUpdate(BattleFaintPacket(pnx))
|
||||
val actor = pokemon.actor
|
||||
pokemon.effectedPokemon.currentHealth = 0
|
||||
val preamble = if (actor is EntityBackedBattleActor<*>) {
|
||||
@@ -49,8 +50,6 @@ class FaintInstruction(battle: PokemonBattle, val message: BattleMessage) : Inte
|
||||
val context = ShowdownInterpreter.getContextFromFaint(faintingPokemon, battle)
|
||||
|
||||
preamble.thenAccept {
|
||||
// Sent after the faint/recall animation so clients don't learn about the KO early
|
||||
battle.sendUpdate(BattleFaintPacket(pnx))
|
||||
faintingPokemon.effectedPokemon.currentHealth = 0
|
||||
faintingPokemon.faintedAt = battle.faintCounter++
|
||||
faintingPokemon.sendUpdate()
|
||||
|
||||
+10
-23
@@ -17,8 +17,15 @@ import com.cobblemon.mod.common.api.events.cooking.PokeSnackSpawnPokemonEvent
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBait
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBait.Effect
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBaitEffects
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.detail.*
|
||||
import com.cobblemon.mod.common.api.spawning.detail.EntitySpawnResult
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonHerdSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketMultiplyingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketNormalizingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawnBaitInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.FixedAreaSpawner
|
||||
@@ -30,6 +37,7 @@ import com.cobblemon.mod.common.item.components.FoodColourComponent
|
||||
import com.cobblemon.mod.common.item.components.IngredientComponent
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.PokeSnackBlockParticlesPacket
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import java.util.UUID
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
@@ -43,7 +51,6 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import java.util.*
|
||||
|
||||
open class PokeSnackBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
BlockEntity(CobblemonBlockEntities.POKE_SNACK, pos, state), TintBlockEntity,
|
||||
@@ -56,29 +63,9 @@ open class PokeSnackBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
const val POKE_SNACK_CRUMBED_ASPECT = "poke_snack_crumbed"
|
||||
}
|
||||
|
||||
class Spawner(
|
||||
name: String,
|
||||
spawnPool: SpawnPool,
|
||||
buckets: Map<String, Float>,
|
||||
world: ServerLevel,
|
||||
position: BlockPos,
|
||||
horizontalRadius: Int,
|
||||
verticalRadius: Int,
|
||||
maxPokemonPerChunk: Float
|
||||
) : FixedAreaSpawner(
|
||||
name = name,
|
||||
spawnPool = spawnPool,
|
||||
buckets = buckets,
|
||||
world = world,
|
||||
position = position,
|
||||
horizontalRadius = horizontalRadius,
|
||||
verticalRadius = verticalRadius,
|
||||
maxPokemonPerChunk = maxPokemonPerChunk
|
||||
)
|
||||
|
||||
override var tint: Int? = null
|
||||
|
||||
val spawner: Spawner by lazy {
|
||||
val spawner: FixedAreaSpawner by lazy {
|
||||
val server = level as ServerLevel
|
||||
|
||||
val baitEffects = getBaitEffects()
|
||||
|
||||
@@ -278,7 +278,7 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
override fun injectSpawns(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail>? {
|
||||
val spawningStyle = spawningStyle
|
||||
return if (spawningStyle is NaturalHabitatSpawning) {
|
||||
spawnDetails.filter { it.bucket == bucket && it.spawnablePositionType.clazz == spawnablePosition::class.java && it.isSatisfiedBy(spawnablePosition) }
|
||||
spawnDetails.filter { it.spawnablePositionType.clazz == spawnablePosition::class.java && it.isSatisfiedBy(spawnablePosition) }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.isLookingAt
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.color.block.BlockColor
|
||||
@@ -79,16 +80,19 @@ import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.client.model.BoatModel
|
||||
import net.minecraft.client.model.ChestBoatModel
|
||||
import net.minecraft.client.model.PlayerModel
|
||||
import net.minecraft.client.player.LocalPlayer
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.blockentity.HangingSignRenderer
|
||||
import net.minecraft.client.renderer.blockentity.SignRenderer
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer
|
||||
import net.minecraft.client.renderer.entity.LivingEntityRenderer
|
||||
import net.minecraft.client.resources.PlayerSkin
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.AABB
|
||||
import org.joml.Vector2d
|
||||
|
||||
object CobblemonClient {
|
||||
|
||||
@@ -109,6 +113,8 @@ object CobblemonClient {
|
||||
val battleOverlay: BattleOverlay by lazy { BattleOverlay() }
|
||||
val pokedexUsageContext: PokedexUsageContext by lazy { PokedexUsageContext() }
|
||||
val rideControlsOverlay: RideControlsOverlay by lazy { RideControlsOverlay() }
|
||||
val ridingTurnInputHandler: MutableMap<ResourceLocation, (LocalPlayer, Double, Double, Double) -> Vector2d> = mutableMapOf()
|
||||
val ridingSprintHandler: MutableMap<ResourceLocation, () -> Boolean> = mutableMapOf()
|
||||
|
||||
fun onLogin() {
|
||||
clientPlayerData = ClientGeneralPlayerData()
|
||||
@@ -194,6 +200,8 @@ object CobblemonClient {
|
||||
ClientPlayerIcon.onTick()
|
||||
PartyOverlayDataControl.tick(event.client.isPaused)
|
||||
}
|
||||
|
||||
ridingSprintHandler.put(cobblemonResource("normal")) { Minecraft.getInstance().options.keySprint.isDown }
|
||||
}
|
||||
|
||||
private fun registerTooltipManagers() {
|
||||
|
||||
+6
-5
@@ -470,7 +470,7 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
|
||||
val random = currentEntity.level().random
|
||||
when (particleData) {
|
||||
is ParticleData.SnowstormParticle -> {
|
||||
val locators = particleData.locatorResolver(locatorStates).filter(::isLocatorVisible)
|
||||
val locators = particleData.locatorResolver(locatorStates).ifEmpty { listOf("root") }.filter(::isLocatorVisible)
|
||||
if (particleData.chance > random.nextDouble()) {
|
||||
locators.forEach { locator ->
|
||||
repeat(particleData.amount) {
|
||||
@@ -518,14 +518,15 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
fun getSeatLocator(passenger: Entity): String? {
|
||||
return this.getEntity().occupiedSeats.entries
|
||||
fun getSeatLocator(passenger: Entity): String {
|
||||
val seat = this.getEntity().occupiedSeats.entries
|
||||
.firstOrNull { it.value == passenger }?.key
|
||||
?.locator
|
||||
?: throw IllegalArgumentException("Entity is not currently riding a seat")
|
||||
return seat.locator
|
||||
}
|
||||
|
||||
override fun positionRider(passenger: Entity, positionUpdater: MoveFunction) {
|
||||
val locatorName = getSeatLocator(passenger) ?: return
|
||||
val locatorName = getSeatLocator(passenger)
|
||||
val locator = this.locatorStates[locatorName] ?: return
|
||||
|
||||
val locatorOffset = locator.matrix.getTranslation(Vector3f())
|
||||
|
||||
@@ -129,6 +129,10 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
|
||||
val panelX = 0
|
||||
val party = CobblemonClient.storage.party
|
||||
val matrices = context.pose()
|
||||
|
||||
matrices.pushPose()
|
||||
matrices.translate(0.0, 0.0, -100.0)
|
||||
|
||||
if (party.slots.none { it != null }) {
|
||||
if (!this.attachedToast &&
|
||||
CobblemonClient.clientPlayerData.promptStarter &&
|
||||
@@ -182,10 +186,11 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
|
||||
)
|
||||
|
||||
matrices.pushPose()
|
||||
matrices.scale(1F, 1F, 0.2F)
|
||||
matrices.translate(
|
||||
panelX + portraitFrameOffsetX + selectedOffsetX + PORTRAIT_DIAMETER / 2.0 - 1.0,
|
||||
y.toDouble() - 12,
|
||||
0.0
|
||||
-50.0
|
||||
)
|
||||
|
||||
val shouldAnimate = when (Cobblemon.config.partyPortraitAnimations) {
|
||||
@@ -557,5 +562,7 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matrices.popPose()
|
||||
}
|
||||
}
|
||||
@@ -195,7 +195,7 @@ abstract class ScrollingWidget<T : ObjectSelectionList.Entry<T>>(
|
||||
return this.width
|
||||
}
|
||||
|
||||
override fun getRowTop(index: Int): Int {
|
||||
public override fun getRowTop(index: Int): Int {
|
||||
return this.y - scrollAmount.toInt() + (index * this.itemHeight)
|
||||
}
|
||||
|
||||
@@ -205,6 +205,19 @@ abstract class ScrollingWidget<T : ObjectSelectionList.Entry<T>>(
|
||||
|
||||
override fun getScrollbarPosition(): Int = this.left + this.width - this.scrollBarWidth
|
||||
|
||||
public override fun ensureVisible(entry: T) {
|
||||
val i = getRowTop(children().indexOf(entry))
|
||||
val topDifference = i - y
|
||||
if (topDifference < 0) {
|
||||
scroll(topDifference)
|
||||
}
|
||||
|
||||
val bottomDifference = bottom - i - itemHeight - 4
|
||||
if (bottomDifference < 0) {
|
||||
scroll(-bottomDifference)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Slot<T : Slot<T>>(): Entry<T>() {
|
||||
// Override render to show each individual element
|
||||
}
|
||||
|
||||
+29
-18
@@ -68,25 +68,23 @@ open class VariableObjectSelectionList(x: Int, y: Int, width: Int, height: Int):
|
||||
}
|
||||
|
||||
override fun renderWidget(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
|
||||
var stackHeight = -scrollAmount.toInt()
|
||||
guiGraphics.enableScissor(x, y, x + width, y + height)
|
||||
children.forEachIndexed { index, entry ->
|
||||
if (stackHeight in 1..<height || stackHeight + entry.height in 1..<height) {
|
||||
entry.render(guiGraphics, index, x, y + stackHeight, mouseX, mouseY, partialTick)
|
||||
val yTop = getRowTop(entry)
|
||||
if (yTop + entry.height > y || yTop < y + height) {
|
||||
entry.render(guiGraphics, index, x, yTop, mouseX, mouseY, partialTick)
|
||||
}
|
||||
stackHeight += entry.height
|
||||
}
|
||||
guiGraphics.disableScissor()
|
||||
}
|
||||
|
||||
fun getEntryAtPosition(mouseX: Int, mouseY: Int): Entry? {
|
||||
if (mouseX in x..(x + width) && mouseY in y..(y + height)) {
|
||||
var stackHeight = -scrollAmount
|
||||
children.forEach {
|
||||
if (mouseY.toDouble() in (y + stackHeight)..(y + stackHeight + it.height)) {
|
||||
val yTop = getRowTop(it)
|
||||
if (mouseY in (yTop)..(yTop + it.height)) {
|
||||
return it
|
||||
}
|
||||
stackHeight += it.height
|
||||
}
|
||||
}
|
||||
return null
|
||||
@@ -94,10 +92,16 @@ open class VariableObjectSelectionList(x: Int, y: Int, width: Int, height: Int):
|
||||
|
||||
fun ensureVisible(index: Int) {
|
||||
val entry = children[index]
|
||||
if (entry.scrolledY - scrollAmount < 0) {
|
||||
ensureVisible(entry)
|
||||
}
|
||||
|
||||
open fun ensureVisible(entry: Entry) {
|
||||
if (entry !in children) return
|
||||
val yTop = entry.scrolledY - scrollAmount.toInt()
|
||||
if (yTop < 0) {
|
||||
setScroll(entry.scrolledY.toDouble())
|
||||
} else if (entry.scrolledY + entry.height > height) {
|
||||
setScroll(entry.scrolledY.toDouble() + entry.height)
|
||||
} else if (yTop + entry.height > height) {
|
||||
setScroll((entry.scrolledY + entry.height - height).toDouble())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,14 +125,12 @@ open class VariableObjectSelectionList(x: Int, y: Int, width: Int, height: Int):
|
||||
}
|
||||
|
||||
fun getRowTop(entry: Entry): Int {
|
||||
var stackHeight = -scrollAmount.toInt()
|
||||
children.forEach {
|
||||
if (it == entry) {
|
||||
return y + stackHeight
|
||||
}
|
||||
stackHeight += it.height
|
||||
}
|
||||
return y
|
||||
if (entry !in children) return -1
|
||||
return y + entry.scrolledY - scrollAmount.toInt()
|
||||
}
|
||||
|
||||
fun getLeft(): Int {
|
||||
return x
|
||||
}
|
||||
|
||||
override fun defaultButtonNarrationText(pNarrationElementOutput: NarrationElementOutput) {}
|
||||
@@ -136,6 +138,7 @@ open class VariableObjectSelectionList(x: Int, y: Int, width: Int, height: Int):
|
||||
|
||||
abstract inner class Entry(var height: Int): GuiEventListener {
|
||||
var scrolledY: Int = 0
|
||||
|
||||
private var widgetFocused = false
|
||||
|
||||
override fun setFocused(focused: Boolean) {
|
||||
@@ -145,6 +148,14 @@ open class VariableObjectSelectionList(x: Int, y: Int, width: Int, height: Int):
|
||||
override fun isFocused(): Boolean = widgetFocused
|
||||
|
||||
abstract fun render(guiGraphics: GuiGraphics, index: Int, x: Int, y: Int, mouseX: Int, mouseY: Int, partialTick: Float)
|
||||
|
||||
fun getRowTop(): Int {
|
||||
return getRowTop(this)
|
||||
}
|
||||
|
||||
fun getRowLeft(): Int {
|
||||
return getLeft()
|
||||
}
|
||||
}
|
||||
|
||||
inner class EmptySeparator(height: Int): Entry(height) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.battle
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.battles.PassActionResponse
|
||||
import com.cobblemon.mod.common.battles.ShowdownActionResponse
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
@@ -17,9 +18,18 @@ import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleActionSelection
|
||||
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleBackButton
|
||||
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleGeneralActionSelection
|
||||
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleMoveSelection
|
||||
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleSwitchPokemonSelection
|
||||
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleTargetSelection
|
||||
import com.cobblemon.mod.common.client.gui.battle.subscreen.ForfeitConfirmationSelection
|
||||
import com.cobblemon.mod.common.client.gui.battle.widgets.BattleMessagePane
|
||||
import com.cobblemon.mod.common.client.gui.battle.widgets.BattleOptionTile
|
||||
import com.cobblemon.mod.common.client.gui.summary.Summary.Companion.PARTY
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.PartyWidget
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerScreen
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.SubManagerComponent
|
||||
import com.cobblemon.mod.common.client.keybind.boundKey
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
@@ -29,8 +39,11 @@ import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import org.joml.Vector4i
|
||||
|
||||
class BattleGUI : Screen(battleLang("gui.title")), CobblemonRenderable {
|
||||
class BattleGUI : Screen(battleLang("gui.title")), CobblemonRenderable, ControllerScreen {
|
||||
companion object {
|
||||
const val OPTION_VERTICAL_SPACING = 3
|
||||
const val OPTION_HORIZONTAL_SPACING = 3
|
||||
@@ -51,11 +64,49 @@ class BattleGUI : Screen(battleLang("gui.title")), CobblemonRenderable {
|
||||
|
||||
var queuedActions = mutableListOf<() -> Unit>()
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(this)
|
||||
|
||||
override fun getComponentManager(): ControllerComponentManager {
|
||||
return controllerComponentManager
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
messagePane = BattleMessagePane(CobblemonClient.battle!!.messages)
|
||||
messagePane.opacity = CobblemonClient.battleOverlay.opacityRatio.toFloat().coerceAtLeast(0.3F)
|
||||
addRenderableWidget(messagePane)
|
||||
|
||||
controllerComponentManager.componentList["spectating_quit"] = ControllerComponent().also {
|
||||
it.onPress = {
|
||||
quitSpectating()
|
||||
}
|
||||
}
|
||||
controllerComponentManager.componentList["battle_action"] = SubManagerComponent().also {
|
||||
it.subManager = { getCurrentActionSelection()?.controllerComponentManager }
|
||||
}
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["battle_action"]
|
||||
it.onActiveChange = { state ->
|
||||
if (!state) {
|
||||
getCurrentActionSelection()?.controllerComponentManager?.active = false
|
||||
} else {
|
||||
if (it.currentSelectedComponent == "battle_action") getCurrentActionSelection()?.controllerComponentManager?.active = true
|
||||
}
|
||||
}
|
||||
it.onBack = {
|
||||
if (it.currentSelectedComponent == "battle_action") {
|
||||
getCurrentActionSelection()?.controllerComponentManager?.onBack?.invoke()
|
||||
} else {
|
||||
minimizeBattle()
|
||||
}
|
||||
}
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SPECIAL_1,
|
||||
{ (getCurrentActionSelection() as? BattleMoveSelection)?.shiftButtonActive() ?: false },
|
||||
Component.translatable("cobblemon.controller.battle.swap_position"),
|
||||
true
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fun changeActionSelection(newSelection: BattleActionSelection?) {
|
||||
@@ -65,6 +116,11 @@ class BattleGUI : Screen(battleLang("gui.title")), CobblemonRenderable {
|
||||
if (newSelection != null) {
|
||||
addRenderableWidget(newSelection)
|
||||
}
|
||||
if (controllerComponentManager.currentSelectedComponent == "battle_action" && controllerComponentManager.active) {
|
||||
focused = null
|
||||
getCurrentActionSelection()?.controllerComponentManager?.active = true
|
||||
controllerComponentManager.focused?.onFocus?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +141,38 @@ class BattleGUI : Screen(battleLang("gui.title")), CobblemonRenderable {
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSelectionPosAndSize(): Vector4i? {
|
||||
val data = super.getSelectionPosAndSize()
|
||||
if (data != null) {
|
||||
return data
|
||||
}
|
||||
|
||||
val actionSelection = getCurrentActionSelection()
|
||||
var newData: Vector4i? = null
|
||||
when (actionSelection) {
|
||||
is BattleGeneralActionSelection -> {
|
||||
val tile = actionSelection.controllerComponentManager.focused?.associatedGUIElement as? BattleOptionTile
|
||||
if (tile != null) {
|
||||
newData = Vector4i(tile.x - 2, tile.y - 2, BattleOptionTile.OPTION_WIDTH + 4, BattleOptionTile.OPTION_HEIGHT + 4)
|
||||
}
|
||||
}
|
||||
is BattleMoveSelection -> {
|
||||
val move = actionSelection.moveTiles[actionSelection.selectedMoveIndex]
|
||||
newData = Vector4i(move.x.toInt() - 10, move.y.toInt() - 2, BattleMoveSelection.MOVE_WIDTH + 12, BattleMoveSelection.MOVE_HEIGHT + 4)
|
||||
}
|
||||
is BattleSwitchPokemonSelection -> {
|
||||
val tile = actionSelection.tiles[actionSelection.selectedTileIndex]
|
||||
newData = Vector4i(tile.x.toInt() - 2, tile.y.toInt() - 2, BattleSwitchPokemonSelection.SwitchTile.SELECT_WIDTH + 2, BattleSwitchPokemonSelection.SwitchTile.SELECT_HEIGHT - 1)
|
||||
}
|
||||
is BattleTargetSelection -> {
|
||||
val tile = actionSelection.baseTiles[actionSelection.selectedTileIndex]
|
||||
newData = Vector4i(tile.x.toInt() - 2, tile.y.toInt() - 2, BattleTargetSelection.TARGET_WIDTH + 4, BattleTargetSelection.TARGET_HEIGHT + 2)
|
||||
}
|
||||
}
|
||||
|
||||
return newData
|
||||
}
|
||||
|
||||
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
super.render(context, mouseX, mouseY, delta)
|
||||
|
||||
@@ -122,9 +210,10 @@ class BattleGUI : Screen(battleLang("gui.title")), CobblemonRenderable {
|
||||
|
||||
val currentSelection = getCurrentActionSelection()
|
||||
if (currentSelection == null || currentSelection is BattleGeneralActionSelection ) {
|
||||
val hideLabelButton = if (controllerComponentManager.active) controllerComponentManager.getVisual(ResourceLocation.fromNamespaceAndPath("controlify", "gui_back")) else PartySendBinding.boundKey().displayName
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = battleLang("ui.hide_label", PartySendBinding.boundKey().displayName),
|
||||
text = battleLang("ui.hide_label", hideLabelButton),
|
||||
x = Minecraft.getInstance().window.guiScaledWidth / 2,
|
||||
y = (Minecraft.getInstance().window.guiScaledHeight / 5),
|
||||
opacity = 0.75F * opacity,
|
||||
@@ -141,6 +230,17 @@ class BattleGUI : Screen(battleLang("gui.title")), CobblemonRenderable {
|
||||
)
|
||||
}
|
||||
|
||||
renderSelection(context.pose())
|
||||
|
||||
val activeComponent: ControllerComponent?
|
||||
if (battle.spectating) {
|
||||
activeComponent = controllerComponentManager.componentList["spectator_quit"]
|
||||
} else {
|
||||
activeComponent = controllerComponentManager.componentList["battle_action"]
|
||||
}
|
||||
if (controllerComponentManager.focused != activeComponent) {
|
||||
controllerComponentManager.focused = activeComponent
|
||||
}
|
||||
|
||||
queuedActions.forEach { it() }
|
||||
queuedActions.clear()
|
||||
@@ -151,8 +251,6 @@ class BattleGUI : Screen(battleLang("gui.title")), CobblemonRenderable {
|
||||
}
|
||||
|
||||
fun deriveRootActionSelection(actor: ClientBattleActor, request: SingleActionRequest): BattleActionSelection? {
|
||||
|
||||
|
||||
return if (request.forceSwitch) {
|
||||
BattleSwitchPokemonSelection(this, request)
|
||||
} else {
|
||||
@@ -192,15 +290,21 @@ class BattleGUI : Screen(battleLang("gui.title")), CobblemonRenderable {
|
||||
return minimizeBattle()
|
||||
}
|
||||
|
||||
val battle = CobblemonClient.battle
|
||||
if (battle?.spectating == true && specBackButton.isHovered(mouseX, mouseY)) {
|
||||
RemoveSpectatorPacket(battle.battleId).sendToServer()
|
||||
CobblemonClient.endBattle()
|
||||
if (specBackButton.isHovered(mouseX, mouseY)) {
|
||||
quitSpectating()
|
||||
}
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
private fun minimizeBattle(): Boolean {
|
||||
fun quitSpectating() {
|
||||
val battle = CobblemonClient.battle
|
||||
if (battle?.spectating == true) {
|
||||
RemoveSpectatorPacket(battle.battleId).sendToServer()
|
||||
CobblemonClient.endBattle()
|
||||
}
|
||||
}
|
||||
|
||||
fun minimizeBattle(): Boolean {
|
||||
val battle = CobblemonClient.battle ?: return false
|
||||
battle.minimised = !battle.minimised
|
||||
PartySendBinding.actioned()
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.battle.ActiveClientBattlePokemon
|
||||
import com.cobblemon.mod.common.client.battle.ClientBallDisplay
|
||||
import com.cobblemon.mod.common.client.gui.battle.widgets.BattleMessagePane
|
||||
import com.cobblemon.mod.common.client.integration.controlify.CobblemonControlifyEntrypoint
|
||||
import com.cobblemon.mod.common.client.keybind.boundKey
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
@@ -114,6 +115,10 @@ class BattleOverlay : Gui(Minecraft.getInstance()), Schedulable {
|
||||
override val schedulingTracker = SchedulingTracker()
|
||||
|
||||
override fun render(context: GuiGraphics, tickCounter: DeltaTracker) {
|
||||
val matrixStack = context.pose()
|
||||
matrixStack.pushPose()
|
||||
matrixStack.translate(0.0, 0.0, -50.0) // Push back so controlify gui renders infront
|
||||
|
||||
val tickDelta = tickCounter.realtimeDeltaTicks.takeIf { !Minecraft.getInstance()!!.isPaused } ?: 0F
|
||||
schedulingTracker.update(tickDelta / 20F)
|
||||
passedSeconds += tickDelta / 20
|
||||
@@ -142,9 +147,14 @@ class BattleOverlay : Gui(Minecraft.getInstance()), Schedulable {
|
||||
|
||||
if (Minecraft.getInstance().screen !is BattleGUI && battle.mustChoose) {
|
||||
val textOpacity = PROMPT_TEXT_OPACITY_CURVE(passedSeconds)
|
||||
|
||||
val bindingComponent = if (Cobblemon.implementation.isModInstalled("controlify") && CobblemonControlifyEntrypoint.controllerActive())
|
||||
CobblemonControlifyEntrypoint.SEND_OUT_POKEMON!!.inputGlyph()
|
||||
else PartySendBinding.boundKey().displayName
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = battleLang("ui.actions_label", PartySendBinding.boundKey().displayName),
|
||||
text = battleLang("ui.actions_label", bindingComponent),
|
||||
x = Minecraft.getInstance().window.guiScaledWidth / 2,
|
||||
y = Minecraft.getInstance().window.guiScaledHeight / 5,
|
||||
opacity = textOpacity,
|
||||
@@ -160,6 +170,8 @@ class BattleOverlay : Gui(Minecraft.getInstance()), Schedulable {
|
||||
messagePane.opacity = 0.3F
|
||||
messagePane.render(context, 0, 0, 0F)
|
||||
}
|
||||
|
||||
matrixStack.popPose()
|
||||
}
|
||||
|
||||
fun drawTile(context: GuiGraphics, tickDelta: Float, activeBattlePokemon: ActiveClientBattlePokemon, left: Boolean, rank: Int, dexState: PokedexEntryProgress, hasCommand: Boolean = false, isHovered: Boolean = false, isCompact: Boolean = false) {
|
||||
@@ -297,10 +309,11 @@ class BattleOverlay : Gui(Minecraft.getInstance()), Schedulable {
|
||||
(y + portraitDiameter + portraitOffsetY).toInt(),
|
||||
)
|
||||
matrixStack.pushPose()
|
||||
matrixStack.scale(1F, 1F, 0.2F)
|
||||
matrixStack.translate(
|
||||
portraitStartX + portraitDiameter / 2.0,
|
||||
y.toDouble() + portraitOffsetY - if (isCompact) 15.0 else 5.0,
|
||||
0.0
|
||||
-50.0
|
||||
)
|
||||
|
||||
if (ballState != null && ballState.currentPose != "shut") {
|
||||
|
||||
+3
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.gui.battle.subscreen
|
||||
import com.cobblemon.mod.common.api.gui.ParentWidget
|
||||
import com.cobblemon.mod.common.client.battle.SingleActionRequest
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.mojang.blaze3d.platform.InputConstants
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
|
||||
@@ -26,6 +27,8 @@ abstract class BattleActionSelection(
|
||||
val opacity: Float
|
||||
get() = battleGUI.opacity
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(battleGUI)
|
||||
|
||||
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
|
||||
if(pButton == InputConstants.MOUSE_BUTTON_LEFT){
|
||||
return mousePrimaryClicked(pMouseX, pMouseY)
|
||||
|
||||
+30
-9
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.battle.SingleActionRequest
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
|
||||
import com.cobblemon.mod.common.client.gui.battle.widgets.BattleOptionTile
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
@@ -41,6 +42,7 @@ class BattleGeneralActionSelection(
|
||||
val lastAnwseredRequest = CobblemonClient.battle?.getLastAnsweredRequest()
|
||||
|
||||
val tiles = mutableListOf<BattleOptionTile>()
|
||||
|
||||
init {
|
||||
var rank = 0
|
||||
|
||||
@@ -76,22 +78,41 @@ class BattleGeneralActionSelection(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["tile_0_0"]
|
||||
it.onBack = {
|
||||
battleGUI.minimizeBattle()
|
||||
}
|
||||
it.onActiveChange = { state ->
|
||||
if (state) it.focused?.onSelect?.invoke() else it.focused?.onUnselect?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addOption(rank: Int, text: MutableComponent, texture: ResourceLocation, onClick: () -> Unit) {
|
||||
val startY = Minecraft.getInstance().window.guiScaledHeight - BattleGUI.OPTION_VERTICAL_OFFSET
|
||||
val x = if (rank % 2 == 0) BattleGUI.OPTION_ROOT_X else BattleGUI.OPTION_ROOT_X + BattleGUI.OPTION_HORIZONTAL_SPACING + BattleOptionTile.OPTION_WIDTH
|
||||
val y = if (rank > 1) startY + BattleOptionTile.OPTION_HEIGHT + BattleGUI.OPTION_HORIZONTAL_SPACING else startY
|
||||
tiles.add(
|
||||
BattleOptionTile(
|
||||
battleGUI = battleGUI,
|
||||
x = x,
|
||||
y = y,
|
||||
resource = texture,
|
||||
text = text,
|
||||
onClick = onClick
|
||||
)
|
||||
val tile = BattleOptionTile(
|
||||
battleGUI = battleGUI,
|
||||
x = x,
|
||||
y = y,
|
||||
resource = texture,
|
||||
text = text,
|
||||
onClick = onClick
|
||||
)
|
||||
tiles.add(tile)
|
||||
val row = rank / 2
|
||||
val column = rank % 2
|
||||
controllerComponentManager.componentList["tile_${row}_${column}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = tile
|
||||
if (row == 1) it.up = { controllerComponentManager.componentList["tile_0_${column}"] }
|
||||
else it.down = { controllerComponentManager.componentList["tile_1_${column}"] }
|
||||
if (column == 1) it.left = { controllerComponentManager.componentList["tile_${row}_0"] }
|
||||
else it.right = { controllerComponentManager.componentList["tile_${row}_1"] }
|
||||
it.onPress = { tile.onClick.invoke() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
|
||||
+63
-4
@@ -21,10 +21,12 @@ import com.cobblemon.mod.common.client.battle.SingleActionRequest
|
||||
import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.math.toRGB
|
||||
import com.cobblemon.mod.common.util.startWith
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
@@ -66,6 +68,10 @@ class BattleMoveSelection(
|
||||
MoveTile(this, inBattleMove, x, y)
|
||||
}
|
||||
var moveTiles = baseTiles
|
||||
set(value) {
|
||||
field = value
|
||||
recalculateMoveGrid()
|
||||
}
|
||||
|
||||
val backButton = BattleBackButton(x - 11F, Minecraft.getInstance().window.guiScaledHeight - 22F)
|
||||
val gimmickButtons = moveSet.getGimmicks().filter { it !in moveSet.pendingGimmickUsedThisTurn }.mapIndexed { index, gimmick ->
|
||||
@@ -76,6 +82,53 @@ class BattleMoveSelection(
|
||||
|
||||
val shiftButton = BattleShiftButton(x + 22.5F, Minecraft.getInstance().window.guiScaledHeight - 22F )
|
||||
|
||||
private fun recalculateMoveGrid() {
|
||||
controllerComponentManager.componentList.filter { it.key.startsWith("move_") }.forEach { controllerComponentManager.componentList.remove(it.key) }
|
||||
moveTiles.forEachIndexed { index, tile ->
|
||||
val column = index % 2
|
||||
val row = index / 2
|
||||
controllerComponentManager.componentList["move_${row}_${column}"] = ControllerComponent().also {
|
||||
if (row == 1) it.up = { controllerComponentManager.componentList["move_${0}_${column}"] }
|
||||
else it.down = { controllerComponentManager.componentList["move_${1}_${column}"] }
|
||||
if (column == 1) it.left = { controllerComponentManager.componentList["move_${row}_0"] }
|
||||
else it.right = { controllerComponentManager.componentList["move_${row}_1"] }
|
||||
it.onPress = {
|
||||
if(request.activePokemon.getFormat().battleType.pokemonPerSide == 1) {
|
||||
tile.onClick()
|
||||
} else {
|
||||
battleGUI.changeActionSelection(BattleTargetSelection(battleGUI, request, tile.move, tile.response.gimmickID, tile.move.gimmickMove))
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
}
|
||||
}
|
||||
it.onSpecial1 = {
|
||||
if (shiftButtonActive()) {
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
battleGUI.selectAction(request, ShiftActionResponse())
|
||||
}
|
||||
}
|
||||
it.onFocus = { tile.controllerSelected = true }
|
||||
it.onUnfocus = { tile.controllerSelected = false }
|
||||
it.onSelect = { selectedMoveIndex = index }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var selectedMoveIndex = 0
|
||||
|
||||
init {
|
||||
recalculateMoveGrid()
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["move_0_0"]
|
||||
it.onBack = {
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
battleGUI.changeActionSelection(null)
|
||||
}
|
||||
it.onActiveChange = { state ->
|
||||
if (state) it.focused?.onSelect?.invoke() else it.focused?.onUnselect?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class MoveTile(
|
||||
val moveSelection: BattleMoveSelection,
|
||||
val move: InBattleMove,
|
||||
@@ -99,6 +152,8 @@ class BattleMoveSelection(
|
||||
}
|
||||
}
|
||||
|
||||
var controllerSelected = false
|
||||
|
||||
fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
|
||||
val selectConditionOpacity = moveSelection.opacity * if (!selectable) 0.5F else 1F
|
||||
@@ -110,7 +165,7 @@ class BattleMoveSelection(
|
||||
y = y,
|
||||
width = MOVE_WIDTH,
|
||||
height = MOVE_HEIGHT,
|
||||
vOffset = if (selectable && isHovered(mouseX.toDouble(), mouseY.toDouble())) MOVE_HEIGHT else 0,
|
||||
vOffset = if (selectable && (isHovered(mouseX.toDouble(), mouseY.toDouble()) || controllerSelected)) MOVE_HEIGHT else 0,
|
||||
textureHeight = MOVE_HEIGHT * 2,
|
||||
red = rgb.first,
|
||||
green = rgb.second,
|
||||
@@ -175,7 +230,7 @@ class BattleMoveSelection(
|
||||
)
|
||||
}
|
||||
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = mouseX >= x && mouseX <= x + MOVE_WIDTH && mouseY >= y && mouseY <= y + MOVE_HEIGHT
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = (mouseX >= x && mouseX <= x + MOVE_WIDTH && mouseY >= y && mouseY <= y + MOVE_HEIGHT)
|
||||
|
||||
fun onClick() {
|
||||
if (!selectable) return
|
||||
@@ -184,6 +239,10 @@ class BattleMoveSelection(
|
||||
}
|
||||
}
|
||||
|
||||
fun shiftButtonActive(): Boolean {
|
||||
return this.request.activePokemon.getFormat().battleType.slotsPerActor == 3 && (request.activePokemon.getPNX()[2] == 'a' || request.activePokemon.getPNX()[2] == 'c')
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
moveTiles.forEach {
|
||||
it.render(context, mouseX, mouseY, delta)
|
||||
@@ -192,7 +251,7 @@ class BattleMoveSelection(
|
||||
gimmickButtons.forEach {
|
||||
it.render(context.pose(), mouseX, mouseY, delta)
|
||||
}
|
||||
if(this.request.activePokemon.getFormat().battleType.slotsPerActor == 3 && (request.activePokemon.getPNX()[2] == 'a' || request.activePokemon.getPNX()[2] == 'c')) {
|
||||
if(shiftButtonActive()) {
|
||||
shiftButton.render(context, mouseX, mouseY, delta)
|
||||
}
|
||||
}
|
||||
@@ -214,7 +273,7 @@ class BattleMoveSelection(
|
||||
} else if (gimmick != null) {
|
||||
gimmickButtons.filter { it != gimmick }.forEach { it.toggled = false }
|
||||
moveTiles = if (gimmick.toggle()) gimmick.tiles else baseTiles
|
||||
} else if(shiftButton.isHovered(mouseX,mouseY)) {
|
||||
} else if(shiftButton.isHovered(mouseX,mouseY) && shiftButtonActive()) {
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
battleGUI.selectAction(request, ShiftActionResponse())
|
||||
}
|
||||
|
||||
+41
-5
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.battle.SingleActionRequest
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.getDepletableRedGreen
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
@@ -68,6 +69,8 @@ class BattleSwitchPokemonSelection(
|
||||
|
||||
var isReviving = false
|
||||
|
||||
var selectedTileIndex = 0
|
||||
|
||||
init {
|
||||
val pendingActionRequests = CobblemonClient.battle!!.pendingActionRequests
|
||||
val switchingInPokemon = pendingActionRequests.mapNotNull { it.response }.filterIsInstance<SwitchActionResponse>().map { it.newPokemonId }
|
||||
@@ -92,6 +95,33 @@ class BattleSwitchPokemonSelection(
|
||||
|
||||
tiles.add(SwitchTile(this, slotX, slotY, pokemon, showdownPokemon, isFainted, isCurrentlyInBattle))
|
||||
}
|
||||
|
||||
tiles.forEachIndexed { index, tile ->
|
||||
val row = index / 2
|
||||
val column = index % 2
|
||||
val maxRow = (tiles.size + 1) / 2
|
||||
controllerComponentManager.componentList["tile_${row}_${column}"] = ControllerComponent().also {
|
||||
if (row - 1 >= 0) it.up = { controllerComponentManager.componentList["tile_${row-1}_${column}"] }
|
||||
if (row + 1 < maxRow) it.down = { controllerComponentManager.componentList["tile_${row+1}_${column}"] }
|
||||
if (column == 1) it.left = { controllerComponentManager.componentList["tile_${row}_0"] }
|
||||
else if (row != maxRow || tiles.size % 2 == 0) it.right = { controllerComponentManager.componentList["tile_${row}_1"] }
|
||||
it.onPress = { clickTile(tile) }
|
||||
it.onFocus = { tile.controllerSelected = true }
|
||||
it.onUnfocus = { tile.controllerSelected = false }
|
||||
it.onSelect = { selectedTileIndex = index }
|
||||
}
|
||||
}
|
||||
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["tile_0_0"]
|
||||
it.onBack = {
|
||||
battleGUI.changeActionSelection(null)
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
}
|
||||
it.onActiveChange = { state ->
|
||||
if (state) it.focused?.onSelect?.invoke() else it.focused?.onUnselect?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getSlotPosition(index: Int): Pair<Float, Float> {
|
||||
@@ -154,11 +184,15 @@ class BattleSwitchPokemonSelection(
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
return true
|
||||
}
|
||||
val clicked = tiles.find { it.isHovered(mouseX, mouseY) && if (isReviving) it.isFainted else (!it.isFainted && !it.isCurrentlyInBattle) } ?: return false
|
||||
val pokemon = clicked.pokemon
|
||||
val clicked = tiles.find { it.isHovered(mouseX, mouseY) } ?: return false
|
||||
return clickTile(clicked)
|
||||
}
|
||||
|
||||
fun clickTile(tile: SwitchTile): Boolean {
|
||||
if (!(if (isReviving) tile.isFainted else (!tile.isFainted && !tile.isCurrentlyInBattle))) return false
|
||||
val pokemon = tile.pokemon
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
battleGUI.selectAction(request, SwitchActionResponse(pokemon.uuid))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -190,7 +224,9 @@ class BattleSwitchPokemonSelection(
|
||||
|
||||
val state = FloatingState()
|
||||
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = mouseX in x..(x + SELECT_WIDTH) && mouseY in (y..(y + SELECT_HEIGHT))
|
||||
var controllerSelected = false
|
||||
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = (mouseX in x..(x + SELECT_WIDTH) && mouseY in (y..(y + SELECT_HEIGHT)))
|
||||
|
||||
fun render(context: GuiGraphics, mouseX: Double, mouseY: Double, deltaTicks: Float) {
|
||||
state.currentAspects = pokemon.aspects
|
||||
@@ -229,7 +265,7 @@ class BattleSwitchPokemonSelection(
|
||||
y = y,
|
||||
width = SELECT_WIDTH,
|
||||
height = SELECT_HEIGHT,
|
||||
vOffset = if (!isFainted && (isHovered(mouseX, mouseY) || isCurrentlyInBattle)) SELECT_HEIGHT else 0,
|
||||
vOffset = if (!isFainted && (isHovered(mouseX, mouseY) || isCurrentlyInBattle || controllerSelected)) SELECT_HEIGHT else 0,
|
||||
textureHeight = SELECT_HEIGHT * 2,
|
||||
)
|
||||
|
||||
|
||||
+31
-3
@@ -26,6 +26,7 @@ import com.cobblemon.mod.common.client.gui.battle.BattleGUI
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
|
||||
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleSwitchPokemonSelection.Companion
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
@@ -111,10 +112,35 @@ class BattleTargetSelection(
|
||||
else -> if (isAlly) ArrowDirection.UP else ArrowDirection.DOWN
|
||||
}
|
||||
}
|
||||
TargetTile(this, target, x, y, arrowDirection)
|
||||
var out = TargetTile(this, target, x, y, arrowDirection)
|
||||
controllerComponentManager.componentList["tile_${if (isAlly) 0 else 1}_${fieldPos}"] = ControllerComponent().also {
|
||||
if (isAlly) it.up = { controllerComponentManager.componentList["tile_1_${fieldPos}"] }
|
||||
else it.down = { controllerComponentManager.componentList["tile_0_${fieldPos}"] }
|
||||
if (fieldPos - 1 >= 0) it.left = { controllerComponentManager.componentList["tile_${if (isAlly) 0 else 1}_${fieldPos-1}"] }
|
||||
if (fieldPos + 1 < teamSize) it.right = { controllerComponentManager.componentList["tile_${if (isAlly) 0 else 1}_${fieldPos+1}"] }
|
||||
it.onPress = { out.onClick() }
|
||||
it.onFocus = { out.controllerSelected = true }
|
||||
it.onUnfocus = { out.controllerSelected = false }
|
||||
it.onSelect = { selectedTileIndex = index }
|
||||
}
|
||||
out
|
||||
}
|
||||
var targetTiles = baseTiles
|
||||
|
||||
var selectedTileIndex = 0
|
||||
|
||||
init {
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["tile_0_0"]
|
||||
it.onBack = {
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
battleGUI.changeActionSelection(BattleMoveSelection(battleGUI, request))
|
||||
}
|
||||
it.onActiveChange = { state ->
|
||||
if (state) it.focused?.onSelect?.invoke() else it.focused?.onUnselect?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open inner class TargetTile(
|
||||
val targetSelection: BattleTargetSelection,
|
||||
@@ -151,6 +177,8 @@ class BattleTargetSelection(
|
||||
ArrowDirection.DOWN -> Pair(this.x + TARGET_WIDTH / 2 - 4F, this.y - 7)
|
||||
}
|
||||
|
||||
var controllerSelected = false
|
||||
|
||||
fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
val passedSeconds = CobblemonClient.battleOverlay.passedSeconds // This syncs the role throbbers
|
||||
val selectConditionOpacity = targetSelection.opacity * if (!selectable) 0.5F else 1F
|
||||
@@ -304,9 +332,9 @@ class BattleTargetSelection(
|
||||
|
||||
fun isHovered(mouseX: Double, mouseY: Double): Boolean {
|
||||
if(isMultiTarget) {
|
||||
return targetTiles.firstOrNull { it.selectable && mouseX >= it.x && mouseX <= it.x + TARGET_WIDTH && mouseY >= it.y && mouseY <= it.y + TARGET_HEIGHT } != null
|
||||
return targetTiles.firstOrNull { it.selectable && ((mouseX >= it.x && mouseX <= it.x + TARGET_WIDTH && mouseY >= it.y && mouseY <= it.y + TARGET_HEIGHT) || it.controllerSelected) } != null
|
||||
}
|
||||
return mouseX >= x && mouseX <= x + TARGET_WIDTH && mouseY >= y && mouseY <= y + TARGET_HEIGHT
|
||||
return mouseX >= x && mouseX <= x + TARGET_WIDTH && mouseY >= y && mouseY <= y + TARGET_HEIGHT || controllerSelected
|
||||
}
|
||||
|
||||
fun onClick() {
|
||||
|
||||
+41
-9
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.battle.SingleActionRequest
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
|
||||
import com.cobblemon.mod.common.client.gui.interact.battleRequest.BattleResponseButton
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -51,21 +52,52 @@ class ForfeitConfirmationSelection(
|
||||
|
||||
acceptButton = BattleResponseButton(xPos + 22, yPos + 18, true) {}
|
||||
declineButton = BattleResponseButton(xPos + 57, yPos + 18, false) {}
|
||||
|
||||
controllerComponentManager.componentList["accept_button"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = acceptButton
|
||||
it.onPress = {
|
||||
onAccept()
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
}
|
||||
it.right = { controllerComponentManager.componentList["decline_button"] }
|
||||
}
|
||||
controllerComponentManager.componentList["decline_button"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = declineButton
|
||||
it.onPress = {
|
||||
onDecline()
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
}
|
||||
it.left = { controllerComponentManager.componentList["accept_button"] }
|
||||
}
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["decline_button"]
|
||||
it.onBack = {
|
||||
it.componentList["decline_button"]?.onPress?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onAccept() {
|
||||
battleGUI.selectAction(request, ForfeitActionResponse())
|
||||
|
||||
// Need to fill out any other pending requests
|
||||
var pendingRequest = CobblemonClient.battle?.getFirstUnansweredRequest()
|
||||
while (pendingRequest != null) {
|
||||
battleGUI.selectAction(pendingRequest, PassActionResponse)
|
||||
pendingRequest = CobblemonClient.battle?.getFirstUnansweredRequest()
|
||||
}
|
||||
}
|
||||
|
||||
fun onDecline() {
|
||||
battleGUI.changeActionSelection(null)
|
||||
}
|
||||
|
||||
override fun mousePrimaryClicked(mouseX: Double, mouseY: Double): Boolean {
|
||||
if (declineButton.isHovered(mouseX, mouseY) || acceptButton.isHovered(mouseX, mouseY)) {
|
||||
if (declineButton.isHovered(mouseX, mouseY)) {
|
||||
battleGUI.changeActionSelection(null)
|
||||
onDecline()
|
||||
} else {
|
||||
battleGUI.selectAction(request, ForfeitActionResponse())
|
||||
|
||||
// Need to fill out any other pending requests
|
||||
var pendingRequest = CobblemonClient.battle?.getFirstUnansweredRequest()
|
||||
while (pendingRequest != null) {
|
||||
battleGUI.selectAction(pendingRequest, PassActionResponse)
|
||||
pendingRequest = CobblemonClient.battle?.getFirstUnansweredRequest()
|
||||
}
|
||||
onAccept()
|
||||
}
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
return true
|
||||
|
||||
+2
-2
@@ -50,7 +50,7 @@ class BattleOptionTile(
|
||||
alpha = opacity,
|
||||
width = OPTION_WIDTH,
|
||||
height = OPTION_HEIGHT,
|
||||
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble())) OPTION_HEIGHT else 0,
|
||||
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble()) || focused) OPTION_HEIGHT else 0,
|
||||
textureHeight = OPTION_HEIGHT * 2
|
||||
)
|
||||
|
||||
@@ -80,7 +80,7 @@ class BattleOptionTile(
|
||||
|
||||
override fun isFocused() = focused
|
||||
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = mouseX > x && mouseY > y && mouseX < x + OPTION_WIDTH && mouseY < y + OPTION_HEIGHT
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = (mouseX > x && mouseY > y && mouseX < x + OPTION_WIDTH && mouseY < y + OPTION_HEIGHT)
|
||||
|
||||
override fun updateNarration(builder: NarrationElementOutput) {
|
||||
builder.add(NarratedElementType.TITLE, text)
|
||||
|
||||
+55
-1
@@ -10,6 +10,10 @@ package com.cobblemon.mod.common.client.gui.config
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.config.variable.CobblemonConfigVariable
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerScreen
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.ifServer
|
||||
@@ -22,11 +26,14 @@ import net.minecraft.client.gui.components.StringWidget
|
||||
import net.minecraft.client.gui.layouts.HeaderAndFooterLayout
|
||||
import net.minecraft.client.gui.layouts.LinearLayout
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.network.chat.CommonComponents
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
|
||||
class CobblemonConfigScreen(
|
||||
private val previousScreen: Screen?,
|
||||
) : Screen("gui.cobblemon_config.title".asTranslated()), CobblemonRenderable {
|
||||
) : Screen("gui.cobblemon_config.title".asTranslated()), CobblemonRenderable, ControllerScreen {
|
||||
|
||||
companion object {
|
||||
const val HEIGHT = 240
|
||||
@@ -55,6 +62,13 @@ class CobblemonConfigScreen(
|
||||
|
||||
private var searchString: String = ""
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(this)
|
||||
var lastSelectedEntry = 0
|
||||
|
||||
override fun getComponentManager(): ControllerComponentManager {
|
||||
return controllerComponentManager
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
@@ -98,6 +112,46 @@ class CobblemonConfigScreen(
|
||||
addRenderableWidget(widget)
|
||||
}
|
||||
arrangeLayoutElements()
|
||||
|
||||
controllerComponentManager.componentList["search"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = searchEdit
|
||||
it.down = {
|
||||
val variableEntries = variableList.children().filter { it is CobblemonConfigVariable }
|
||||
if (variableEntries.isNotEmpty()) {
|
||||
controllerComponentManager.componentList["entry_${lastSelectedEntry}_input"]
|
||||
} else {
|
||||
controllerComponentManager.componentList["done"]
|
||||
}
|
||||
}
|
||||
}
|
||||
controllerComponentManager.componentList["done"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = doneButton
|
||||
it.up = {
|
||||
val variableEntries = variableList.children().filter { it is CobblemonConfigVariable }
|
||||
if (variableEntries.isNotEmpty()) {
|
||||
controllerComponentManager.componentList["entry_${lastSelectedEntry}_input"]
|
||||
} else {
|
||||
controllerComponentManager.componentList["search"]
|
||||
}
|
||||
}
|
||||
}
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["search"]
|
||||
it.onBack = {
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK.value(), 1.0F))
|
||||
doneButtonClick()
|
||||
}
|
||||
it.onStart = {
|
||||
it.focused = it.componentList["search"]
|
||||
it.openKeyboard.invoke(it.focused!!.associatedGUIElement as EditBox)
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK.value(), 1.0F))
|
||||
}
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.START,
|
||||
{ true },
|
||||
Component.translatable("cobblemon.controller.quick_search")
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
private fun arrangeLayoutElements() {
|
||||
|
||||
+58
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companio
|
||||
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.SLOT_WIDTH
|
||||
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigVariableList.CobblemonConfigVariableListEntry
|
||||
import com.cobblemon.mod.common.client.gui.config.variable.*
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.config.Category
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
@@ -134,6 +135,63 @@ class CobblemonConfigVariableList(
|
||||
}
|
||||
|
||||
scrollAmount = min(maxScroll.toDouble(), scrollAmount)
|
||||
|
||||
val variableEntries = children().filter { it is CobblemonConfigVariable }
|
||||
variableEntries.forEachIndexed { index, entry ->
|
||||
parent.controllerComponentManager.componentList["entry_${index}_input"] = ControllerComponent().also {
|
||||
when (entry) {
|
||||
is CobblemonConfigButtonVariable -> it.associatedGUIElement = entry.button
|
||||
is CobblemonConfigEditBoxVariable -> it.associatedGUIElement = entry.editBox
|
||||
}
|
||||
it.right = {
|
||||
if ((entry as CobblemonConfigVariable).resetButton.active) {
|
||||
parent.controllerComponentManager.componentList["entry_${index}_reset"]
|
||||
}
|
||||
else null
|
||||
}
|
||||
if (index + 1 <= variableEntries.size - 1) it.down = { parent.controllerComponentManager.componentList["entry_${index+1}_input"] }
|
||||
else it.down = { parent.controllerComponentManager.componentList["done"] }
|
||||
if (index - 1 >= 0) it.up = { parent.controllerComponentManager.componentList["entry_${index-1}_input"] }
|
||||
else it.up = { parent.controllerComponentManager.componentList["search"] }
|
||||
it.onSelect = { parent.lastSelectedEntry = index }
|
||||
it.onFocus = { ensureVisible(entry) }
|
||||
}
|
||||
parent.controllerComponentManager.componentList["entry_${index}_reset"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = (entry as CobblemonConfigVariable).resetButton
|
||||
it.left = { parent.controllerComponentManager.componentList["entry_${index}_input"] }
|
||||
if (index + 1 <= variableEntries.size - 1) it.down = {
|
||||
if ((variableEntries[index+1] as CobblemonConfigVariable).resetButton.active) {
|
||||
parent.controllerComponentManager.componentList["entry_${index+1}_reset"]
|
||||
}
|
||||
else null
|
||||
}
|
||||
else it.down = { parent.controllerComponentManager.componentList["done"] }
|
||||
if (index - 1 >= 0) it.up = {
|
||||
if ((variableEntries[index-1] as CobblemonConfigVariable).resetButton.active) {
|
||||
parent.controllerComponentManager.componentList["entry_${index-1}_reset"]
|
||||
}
|
||||
else null
|
||||
}
|
||||
else it.up = { parent.controllerComponentManager.componentList["search"] }
|
||||
it.onPress = {
|
||||
entry.resetButton.also { button ->
|
||||
button.onPress()
|
||||
button.playDownSound(Minecraft.getInstance().soundManager)
|
||||
parent.controllerComponentManager.also { manager ->
|
||||
manager.focused = manager.componentList["entry_${index}_input"]
|
||||
}
|
||||
}
|
||||
}
|
||||
it.onSelect = { parent.lastSelectedEntry = index }
|
||||
it.onFocus = { ensureVisible(entry) }
|
||||
}
|
||||
}
|
||||
parent.controllerComponentManager.let {
|
||||
if (it.currentSelectedComponent.startsWith("entry_")) {
|
||||
it.focused = it.componentList["search"]
|
||||
}
|
||||
}
|
||||
parent.lastSelectedEntry = 0
|
||||
}
|
||||
|
||||
abstract class CobblemonConfigVariableListEntry(
|
||||
|
||||
+41
-1
@@ -26,6 +26,9 @@ import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialogueOptionWidget
|
||||
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialoguePortraitWidget
|
||||
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialogueTextInputWidget
|
||||
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialogueTimerWidget
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerScreen
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.dto.DialogueDTO
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.dto.DialogueGibberDTO
|
||||
@@ -45,9 +48,10 @@ import net.minecraft.client.gui.components.EditBox
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
|
||||
class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTranslated()), CobblemonRenderable {
|
||||
class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTranslated()), CobblemonRenderable, ControllerScreen {
|
||||
val speakers = dialogueDTO.speakers?.mapNotNull { (key, value) ->
|
||||
val name = value.name
|
||||
when (val face = value.face) {
|
||||
@@ -141,6 +145,12 @@ class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTra
|
||||
)
|
||||
}
|
||||
|
||||
var controllerComponentManager = ControllerComponentManager(this)
|
||||
|
||||
override fun getComponentManager(): ControllerComponentManager {
|
||||
return controllerComponentManager
|
||||
}
|
||||
|
||||
override fun renderBackground(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {}
|
||||
|
||||
override fun renderBlurredBackground(delta: Float) {}
|
||||
@@ -242,6 +252,34 @@ class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTra
|
||||
}
|
||||
|
||||
this.dialogueStartTick = minecraft!!.player!!.tickCount
|
||||
|
||||
controllerComponentManager = ControllerComponentManager(this)
|
||||
|
||||
controllerComponentManager.componentList["none"] = ControllerComponent().also {
|
||||
it.onPress = { dialogueBox.onClick() }
|
||||
}
|
||||
dialogueOptionWidgets.forEachIndexed { index, option ->
|
||||
controllerComponentManager.componentList["option_${index}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = option
|
||||
if (index + 1 <= dialogueOptionWidgets.size - 1) it.right = { controllerComponentManager.componentList["option_${index+1}"] }
|
||||
if (index - 1 >= 0) it.left = { controllerComponentManager.componentList["option_${index-1}"] }
|
||||
it.onPress = { if (!option.onClick()) dialogueBox.onClick() }
|
||||
}
|
||||
}
|
||||
controllerComponentManager.let {
|
||||
it.focused = if (dialogueOptionWidgets.isNotEmpty()) {
|
||||
it.componentList["option_0"]
|
||||
} else {
|
||||
it.componentList["none"]
|
||||
}
|
||||
it.onSpecial1 = { dialogueBox.onClick() }
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SPECIAL_1,
|
||||
{ dialogueBox.dialogue.dialogueInput.allowSkip && dialogueBox.dialogue.dialogueInput.inputType in listOf(DialogueInputDTO.InputType.NONE, DialogueInputDTO.InputType.AUTO_CONTINUE)},
|
||||
Component.translatable("cobblemon.controller.dialogue.skip"),
|
||||
true
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
var gibberIndex = 0
|
||||
@@ -265,6 +303,8 @@ class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTra
|
||||
super.render(guiGraphics, mouseX, mouseY, delta)
|
||||
expressionsToRun.forEach { runtime.resolve(it) }
|
||||
expressionsToRun = emptyList()
|
||||
|
||||
renderSelection(guiGraphics.pose())
|
||||
}
|
||||
|
||||
override fun isPauseScreen() = false
|
||||
|
||||
+10
-4
@@ -160,10 +160,17 @@ class DialogueBox(
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
// TODO change this coordinate check to just be "anywhere the scroll bar isn't"
|
||||
if (!dialogueScreen.waitingForServerUpdate &&
|
||||
mouseX > this.x && mouseX < this.scrollbarPosition &&
|
||||
if (mouseX > this.x && mouseX < this.scrollbarPosition &&
|
||||
mouseY > this.y && mouseY < this.bottom
|
||||
) {
|
||||
if (onClick()) return true
|
||||
}
|
||||
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
fun onClick(): Boolean {
|
||||
if (!dialogueScreen.waitingForServerUpdate) {
|
||||
if (dialogue.dialogueInput.allowSkip && dialogue.dialogueInput.inputType in listOf(DialogueInputDTO.InputType.NONE, DialogueInputDTO.InputType.AUTO_CONTINUE)) {
|
||||
val gibber = dialogueScreen.gibber
|
||||
if (gibber != null && gibber.graduallyShowText && !dialogueScreen.gibberDone) {
|
||||
@@ -177,8 +184,7 @@ class DialogueBox(
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
return false
|
||||
}
|
||||
|
||||
class DialogueLine(val line: FormattedCharSequence, val lineColor: Int) : Entry<DialogueLine>() {
|
||||
|
||||
+6
-2
@@ -42,7 +42,7 @@ class DialogueOptionWidget(
|
||||
y = y,
|
||||
width = width,
|
||||
height = height,
|
||||
vOffset = if (selectable) (if (isHovered) height else 0) else (height * 2),
|
||||
vOffset = if (selectable) (if (isHovered || isFocused) height else 0) else (height * 2),
|
||||
textureHeight = height * 3,
|
||||
)
|
||||
|
||||
@@ -58,10 +58,14 @@ class DialogueOptionWidget(
|
||||
}
|
||||
|
||||
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
|
||||
if (!isHovered) return false
|
||||
return onClick()
|
||||
}
|
||||
|
||||
fun onClick(): Boolean {
|
||||
if (!dialogueScreen.renderInput()) {
|
||||
return false
|
||||
}
|
||||
if (!isHovered) return false
|
||||
|
||||
if (!selectable || dialogueScreen.waitingForServerUpdate) return true
|
||||
|
||||
|
||||
+68
-21
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.interact.battleRequest
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.ColourLibrary
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
@@ -16,6 +17,9 @@ import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.battle.ClientBattleChallenge
|
||||
import com.cobblemon.mod.common.client.battle.ClientTeamRequest
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerScreen
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.*
|
||||
@@ -27,16 +31,18 @@ import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import java.util.*
|
||||
|
||||
class BattleConfigureGUI(
|
||||
private val packet: PlayerInteractOptionsPacket,
|
||||
private val activeRequest: ClientBattleChallenge? = null,
|
||||
private val activeTeamRequest: ClientTeamRequest? = null
|
||||
) : Screen(lang("ui.challenge.challenge_title")), CobblemonRenderable {
|
||||
) : Screen(lang("ui.challenge.challenge_title")), CobblemonRenderable, ControllerScreen {
|
||||
companion object {
|
||||
const val SIZE = 113
|
||||
private val backgroundResource = cobblemonResource("textures/gui/interact/request/battle_request.png")
|
||||
@@ -200,6 +206,12 @@ class BattleConfigureGUI(
|
||||
private var hasRequest = false
|
||||
private var ticksPassed = 0F
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(this)
|
||||
|
||||
override fun getComponentManager(): ControllerComponentManager {
|
||||
return controllerComponentManager
|
||||
}
|
||||
|
||||
override fun renderBlurredBackground(delta: Float) { }
|
||||
override fun renderMenuBackground(context: GuiGraphics) { }
|
||||
|
||||
@@ -218,29 +230,29 @@ class BattleConfigureGUI(
|
||||
}
|
||||
val (x, y) = getDimensions()
|
||||
|
||||
var battleAccept: BattleResponseButton? = null
|
||||
var battleDecline: BattleResponseButton? = null
|
||||
if(pendingRequest != null) {
|
||||
// Draw Accept/Decline buttons
|
||||
this.addRenderableWidget(
|
||||
BattleResponseButton(
|
||||
x + 22,
|
||||
y + 99,
|
||||
true
|
||||
) {
|
||||
battleRequestMap[options[currentPage]]?.onResponse?.let { it1 -> it1(packet, pendingRequest.requestID, true) }
|
||||
closeGUI()
|
||||
}
|
||||
)
|
||||
battleAccept = BattleResponseButton(
|
||||
x + 22,
|
||||
y + 99,
|
||||
true
|
||||
) {
|
||||
battleRequestMap[options[currentPage]]?.onResponse?.let { it1 -> it1(packet, pendingRequest.requestID, true) }
|
||||
closeGUI()
|
||||
}
|
||||
this.addRenderableWidget(battleAccept)
|
||||
|
||||
this.addRenderableWidget(
|
||||
BattleResponseButton(
|
||||
x + 56,
|
||||
y + 99,
|
||||
false
|
||||
) {
|
||||
battleRequestMap[options[currentPage]]?.onResponse?.let { it1 -> it1(packet, pendingRequest.requestID, false) }
|
||||
closeGUI()
|
||||
}
|
||||
)
|
||||
battleDecline = BattleResponseButton(
|
||||
x + 56,
|
||||
y + 99,
|
||||
false
|
||||
) {
|
||||
battleRequestMap[options[currentPage]]?.onResponse?.let { it1 -> it1(packet, pendingRequest.requestID, false) }
|
||||
closeGUI()
|
||||
}
|
||||
this.addRenderableWidget(battleDecline)
|
||||
|
||||
}
|
||||
else {
|
||||
@@ -306,6 +318,36 @@ class BattleConfigureGUI(
|
||||
this.addRenderableWidget(autoLevelNavButtonRight)
|
||||
}
|
||||
}
|
||||
|
||||
controllerComponentManager.componentList["battle_accept"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = battleAccept
|
||||
it.right = { controllerComponentManager.componentList["battle_decline"] }
|
||||
}
|
||||
controllerComponentManager.componentList["battle_decline"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = battleDecline
|
||||
it.left = { controllerComponentManager.componentList["battle_accept"] }
|
||||
}
|
||||
controllerComponentManager.componentList["request"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = requestButton
|
||||
}
|
||||
controllerComponentManager.let {
|
||||
if (pendingRequest != null) {
|
||||
it.focused = it.componentList["battle_decline"]
|
||||
} else {
|
||||
it.focused = it.componentList["request"]
|
||||
it.onTabNav = { nextTab ->
|
||||
currentPage = (currentPage + (if (nextTab) 1 else -1)) % options.count()
|
||||
updateRequestButtonText()
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
}
|
||||
if (battleRequestMap[options[currentPage]]?.option != PlayerInteractOptionsPacket.Options.TEAM_REQUEST) {
|
||||
it.onSecondaryTabNav = { nextTab ->
|
||||
levelRulesetOptionIndex += if (nextTab) 1 else -1
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateRequestButtonText() {
|
||||
@@ -438,6 +480,8 @@ class BattleConfigureGUI(
|
||||
)
|
||||
|
||||
super.render(context, mouseX, mouseY, delta)
|
||||
|
||||
renderSelection(context.pose())
|
||||
}
|
||||
|
||||
private fun getDimensions(): Pair<Int, Int> {
|
||||
@@ -454,4 +498,7 @@ class BattleConfigureGUI(
|
||||
Minecraft.getInstance().setScreen(null)
|
||||
}
|
||||
|
||||
fun playSound(soundEvent: SoundEvent) {
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -35,7 +35,7 @@ class BattleResponseButton(
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
val hovered = (isHovered(pMouseX.toDouble(), pMouseY.toDouble()))
|
||||
val hovered = (isHovered(pMouseX.toDouble(), pMouseY.toDouble()) || isFocused)
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
x = x / SCALE,
|
||||
|
||||
+31
-11
@@ -14,6 +14,9 @@ import com.cobblemon.mod.common.api.callback.MoveSelectDTO
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.ExitButton
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerScreen
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -36,7 +39,7 @@ class MoveSelectConfiguration(
|
||||
|
||||
class MoveSelectGUI(
|
||||
val config: MoveSelectConfiguration
|
||||
) : Screen(Component.translatable("cobblemon.ui.interact.moveselect")), CobblemonRenderable {
|
||||
) : Screen(Component.translatable("cobblemon.ui.interact.moveselect")), CobblemonRenderable, ControllerScreen {
|
||||
companion object {
|
||||
const val WIDTH = 122
|
||||
const val HEIGHT = 133
|
||||
@@ -46,6 +49,8 @@ class MoveSelectGUI(
|
||||
|
||||
var closed = false
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(this)
|
||||
|
||||
constructor(
|
||||
title: MutableComponent,
|
||||
moves: List<MoveSelectDTO>,
|
||||
@@ -63,6 +68,10 @@ class MoveSelectGUI(
|
||||
)
|
||||
)
|
||||
|
||||
override fun getComponentManager(): ControllerComponentManager {
|
||||
return controllerComponentManager
|
||||
}
|
||||
|
||||
fun closeProperly() {
|
||||
closed = true
|
||||
onClose()
|
||||
@@ -73,16 +82,21 @@ class MoveSelectGUI(
|
||||
val y = (height - HEIGHT) / 2
|
||||
|
||||
config.moves.forEachIndexed { index, move ->
|
||||
addRenderableWidget(
|
||||
MoveSlotButton(
|
||||
x = x + 7,
|
||||
y = y + 7 + ((MoveSlotButton.HEIGHT + 3) * index),
|
||||
move = move.moveTemplate,
|
||||
pp = move.pp,
|
||||
ppMax = move.ppMax,
|
||||
enabled = move.enabled
|
||||
) { onPress(move) }
|
||||
)
|
||||
val moveSlot = MoveSlotButton(
|
||||
x = x + 7,
|
||||
y = y + 7 + ((MoveSlotButton.HEIGHT + 3) * index),
|
||||
move = move.moveTemplate,
|
||||
pp = move.pp,
|
||||
ppMax = move.ppMax,
|
||||
enabled = move.enabled
|
||||
) { onPress(move) }
|
||||
addRenderableWidget(moveSlot)
|
||||
|
||||
controllerComponentManager.componentList["move_${index}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = moveSlot
|
||||
if (index + 1 <= config.moves.size - 1) it.down = { controllerComponentManager.componentList["move_${index+1}"] }
|
||||
if (index - 1 >= 0) it.up = { controllerComponentManager.componentList["move_${index-1}"] }
|
||||
}
|
||||
}
|
||||
|
||||
// Add Exit Button
|
||||
@@ -97,6 +111,10 @@ class MoveSelectGUI(
|
||||
)
|
||||
|
||||
super.init()
|
||||
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["move_0"]
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTicks: Float) {
|
||||
@@ -114,6 +132,8 @@ class MoveSelectGUI(
|
||||
|
||||
// Render all added Widgets
|
||||
super.render(context, mouseX, mouseY, partialTicks)
|
||||
|
||||
renderSelection(context.pose())
|
||||
}
|
||||
|
||||
private fun onPress(move: MoveSelectDTO) {
|
||||
|
||||
+39
-15
@@ -16,6 +16,9 @@ import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.ExitButton
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerScreen
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonSelectedPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.party.PartySelectCancelledPacket
|
||||
@@ -38,7 +41,7 @@ class PartySelectConfiguration(
|
||||
)
|
||||
class PartySelectGUI(
|
||||
val config: PartySelectConfiguration
|
||||
) : Screen(Component.translatable("cobblemon.ui.interact.moveselect")), CobblemonRenderable {
|
||||
) : Screen(Component.translatable("cobblemon.ui.interact.moveselect")), CobblemonRenderable, ControllerScreen {
|
||||
companion object {
|
||||
const val WIDTH = 163
|
||||
const val HEIGHT = 132
|
||||
@@ -50,6 +53,8 @@ class PartySelectGUI(
|
||||
|
||||
var closed = false
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(this)
|
||||
|
||||
constructor(
|
||||
title: MutableComponent,
|
||||
pokemon: List<PartySelectPokemonDTO>,
|
||||
@@ -67,6 +72,10 @@ class PartySelectGUI(
|
||||
)
|
||||
)
|
||||
|
||||
override fun getComponentManager(): ControllerComponentManager {
|
||||
return controllerComponentManager
|
||||
}
|
||||
|
||||
fun closeProperly() {
|
||||
closed = true
|
||||
onClose()
|
||||
@@ -90,20 +99,29 @@ class PartySelectGUI(
|
||||
slotY += (31 * offsetIndex) + offsetY
|
||||
}
|
||||
|
||||
addRenderableWidget(
|
||||
PartySlotButton(
|
||||
parent = this,
|
||||
x = slotX,
|
||||
y = slotY,
|
||||
pokemon = pokemon.pokemonProperties,
|
||||
aspects = pokemon.aspects,
|
||||
heldItem = pokemon.heldItem,
|
||||
currentHealth = pokemon.currentHealth,
|
||||
maxHealth = pokemon.maxHealth,
|
||||
enabled = pokemon.enabled,
|
||||
hoverText = pokemon.hoverText,
|
||||
) { onPress(pokemon) }
|
||||
)
|
||||
val slot = PartySlotButton(
|
||||
parent = this,
|
||||
x = slotX,
|
||||
y = slotY,
|
||||
pokemon = pokemon.pokemonProperties,
|
||||
aspects = pokemon.aspects,
|
||||
heldItem = pokemon.heldItem,
|
||||
currentHealth = pokemon.currentHealth,
|
||||
maxHealth = pokemon.maxHealth,
|
||||
enabled = pokemon.enabled,
|
||||
hoverText = pokemon.hoverText,
|
||||
) { onPress(pokemon) }
|
||||
addRenderableWidget(slot)
|
||||
|
||||
val row = index / 2
|
||||
val column = index % 2
|
||||
controllerComponentManager.componentList["slot_${row}_${column}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = slot
|
||||
if (column == 0) it.right = { controllerComponentManager.componentList["slot_${row}_${1}"] }
|
||||
else it.left = { controllerComponentManager.componentList["slot_${row}_${0}"] }
|
||||
if (row - 1 >= 0) it.up = { controllerComponentManager.componentList["slot_${row-1}_${column}"] }
|
||||
if (row + 1 <= 2) it.down = { controllerComponentManager.componentList["slot_${row+1}_${column}"] }
|
||||
}
|
||||
}
|
||||
|
||||
// Add Exit Button
|
||||
@@ -118,6 +136,10 @@ class PartySelectGUI(
|
||||
)
|
||||
|
||||
super.init()
|
||||
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["slot_0_0"]
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTicks: Float) {
|
||||
@@ -155,6 +177,8 @@ class PartySelectGUI(
|
||||
|
||||
// Render all added Widgets
|
||||
super.render(context, mouseX, mouseY, partialTicks)
|
||||
|
||||
renderSelection(context.pose())
|
||||
}
|
||||
|
||||
private fun onPress(pokemon: PartySelectPokemonDTO) {
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ class PartySlotButton(
|
||||
private val renderablePokemon = pokemon.asRenderablePokemon().also { it.aspects = aspects }
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
isHovered = pMouseX >= x && pMouseY >= y && pMouseX < x + width && pMouseY < y + height && enabled
|
||||
isHovered = ((pMouseX >= x && pMouseY >= y && pMouseX < x + width && pMouseY < y + height) || isFocused) && enabled
|
||||
val alpha = if (enabled) 1.0 else 0.7
|
||||
val matrices = context.pose()
|
||||
|
||||
|
||||
+4
-4
@@ -28,7 +28,7 @@ class InteractWheelButton(
|
||||
private val tooltipText: String?,
|
||||
x: Int,
|
||||
y: Int,
|
||||
private val isEnabled: Boolean,
|
||||
val isEnabled: Boolean,
|
||||
private val colour: () -> Vector3f?,
|
||||
onPress: OnPress,
|
||||
private val canHover: (Double, Double) -> Boolean
|
||||
@@ -74,12 +74,12 @@ class InteractWheelButton(
|
||||
y = y,
|
||||
width = width,
|
||||
height = height,
|
||||
vOffset = if (isHovered(mouseX.toFloat(), mouseY.toFloat()) && isEnabled) height else 0,
|
||||
vOffset = if ((isHovered(mouseX.toFloat(), mouseY.toFloat()) || isFocused) && isEnabled) height else 0,
|
||||
textureHeight = height * 2,
|
||||
alpha = if (isEnabled) 1f else 0.4F
|
||||
)
|
||||
|
||||
if(isEnabled && isHovered(mouseX.toFloat(), mouseY.toFloat())){
|
||||
if(isEnabled && (isHovered(mouseX.toFloat(), mouseY.toFloat()) || isFocused)){
|
||||
tooltipText?.let {
|
||||
context.renderTooltip(Minecraft.getInstance().font, Component.translatable(it), mouseX, mouseY)
|
||||
}
|
||||
@@ -162,7 +162,7 @@ class InteractWheelButton(
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
return if (isHovered(mouseX.toFloat(), mouseY.toFloat())) super.mouseClicked(mouseX, mouseY, button) else false
|
||||
return if (isHovered(mouseX.toFloat(), mouseY.toFloat()) || isFocused) super.mouseClicked(mouseX, mouseY, button) else false
|
||||
}
|
||||
|
||||
override fun getTooltip(): Tooltip? {
|
||||
|
||||
+3
-2
@@ -32,9 +32,10 @@ class InteractWheelGUI(private val options: Multimap<Orientation, InteractWheelO
|
||||
return super.keyPressed(keyCode, scanCode, modifiers)
|
||||
}
|
||||
|
||||
private val buttons = mutableListOf<InteractWheelButton>()
|
||||
val buttons = mutableListOf<InteractWheelButton>()
|
||||
private var maxPage = 1
|
||||
private var currentPage = 0
|
||||
|
||||
override fun renderBlurredBackground(delta: Float) { }
|
||||
override fun renderMenuBackground(context: GuiGraphics) {}
|
||||
|
||||
@@ -86,7 +87,7 @@ class InteractWheelGUI(private val options: Multimap<Orientation, InteractWheelO
|
||||
currentPage = page
|
||||
buttons.forEach { removeWidget(it) }
|
||||
buttons.clear()
|
||||
val orientations = Orientation.values()
|
||||
val orientations = Orientation.entries.toTypedArray()
|
||||
orientations.forEach { orientation ->
|
||||
val option = options[orientation].toList().getOrNull(page)
|
||||
addButton(orientation, option)
|
||||
|
||||
+54
-3
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.gui.pc.StorageSlot
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.PartySlotWidget
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
@@ -76,11 +77,58 @@ class PasturePokemonScrollList(
|
||||
|
||||
removedEntries.forEach(this::removeEntry)
|
||||
newEntries.forEach { addEntry(PastureSlot(it, parent)) }
|
||||
|
||||
rebuildControllerSelection()
|
||||
}
|
||||
}
|
||||
|
||||
fun rebuildControllerSelection() {
|
||||
if (parent.lastSelectedIndex > 0 && parent.lastSelectedIndex >= children().size) parent.lastSelectedIndex = children().size - 1
|
||||
children().forEachIndexed { index, slot ->
|
||||
parent.controllerComponentManager.componentList["slot_${index}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = slot
|
||||
if (index - 1 >= 0) it.up = { parent.controllerComponentManager.componentList["slot_${index - 1}"] }
|
||||
if (index + 1 <= children().size - 1) it.down =
|
||||
{ parent.controllerComponentManager.componentList["slot_${index + 1}"] }
|
||||
else it.down = { parent.controllerComponentManager.componentList["recall_all"] }
|
||||
it.onPress = { slot.moveButton.onPress() }
|
||||
it.onSpecial1 = { slot.conflictButton.onPress() }
|
||||
it.onFocus = {
|
||||
slot.controllerFocused = true
|
||||
ensureVisible(slot)
|
||||
}
|
||||
it.onUnfocus = {
|
||||
slot.controllerFocused = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parent.controllerComponentManager.currentSelectedComponent.let { it.startsWith("slot_") || it == "empty" }) {
|
||||
parent.controllerComponentManager.focused =
|
||||
if (children().isEmpty()) parent.controllerComponentManager.componentList["empty"]
|
||||
else parent.controllerComponentManager.componentList["slot_${parent.lastSelectedIndex}"]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getScrollbarPosition() = x + width - 3
|
||||
|
||||
public override fun ensureVisible(entry: PastureSlot) {
|
||||
val i = getRowTop(children().indexOf(entry))
|
||||
val topDifference = i - y
|
||||
if (topDifference < 0) {
|
||||
scroll(topDifference)
|
||||
}
|
||||
|
||||
val bottomDifference = bottom - i - itemHeight - 4
|
||||
if (bottomDifference < 0) {
|
||||
scroll(-bottomDifference)
|
||||
}
|
||||
}
|
||||
|
||||
public override fun getRowTop(index: Int): Int {
|
||||
return super.getRowTop(index)
|
||||
}
|
||||
|
||||
public override fun addEntry(entry: PastureSlot) = super.addEntry(entry)
|
||||
public override fun removeEntry(entry: PastureSlot) = super.removeEntry(entry)
|
||||
override fun renderSelection(context: GuiGraphics, y: Int, entryWidth: Int, entryHeight: Int, borderColor: Int, fillColor: Int) {}
|
||||
@@ -166,6 +214,8 @@ class PasturePokemonScrollList(
|
||||
val client: Minecraft = Minecraft.getInstance()
|
||||
val state = FloatingState()
|
||||
|
||||
var controllerFocused = false
|
||||
|
||||
fun isOwned() = client.player?.uuid == pokemon.playerId
|
||||
fun canUnpasture() = isOwned() || parent.pasturePCGUIConfiguration.permissions.canUnpastureOthers
|
||||
fun canDefend() = PokemonSpecies.getByIdentifier(pokemon.species)?.behaviour?.combat?.willDefendOwner
|
||||
@@ -182,7 +232,7 @@ class PasturePokemonScrollList(
|
||||
setEnabled(PokemonBehaviourFlag.PASTURE_CONFLICT in pokemon.behaviourFlags)
|
||||
}
|
||||
|
||||
private val moveButton: PastureSlotIconButton = PastureSlotIconButton(
|
||||
val moveButton: PastureSlotIconButton = PastureSlotIconButton(
|
||||
xPos = 0,
|
||||
yPos = 0,
|
||||
onPress = {
|
||||
@@ -191,7 +241,8 @@ class PasturePokemonScrollList(
|
||||
pastureId = parent.pasturePCGUIConfiguration.pastureId,
|
||||
pokemonId = pokemon.pokemonId
|
||||
).sendToServer()
|
||||
}
|
||||
},
|
||||
parent = this@PastureSlot
|
||||
)
|
||||
|
||||
override fun getNarration() = pokemon.displayName
|
||||
@@ -219,7 +270,7 @@ class PasturePokemonScrollList(
|
||||
y = y,
|
||||
height = SLOT_HEIGHT,
|
||||
width = rowWidth,
|
||||
vOffset = if (isHovered) SLOT_HEIGHT else 0,
|
||||
vOffset = if (isHovered || controllerFocused) SLOT_HEIGHT else 0,
|
||||
textureHeight = SLOT_HEIGHT * 2
|
||||
)
|
||||
|
||||
|
||||
+3
-2
@@ -18,7 +18,8 @@ import net.minecraft.network.chat.Component
|
||||
|
||||
class PastureSlotIconButton(
|
||||
var xPos: Int, var yPos: Int,
|
||||
onPress: OnPress
|
||||
onPress: OnPress,
|
||||
var parent: PasturePokemonScrollList.PastureSlot
|
||||
) : Button(xPos, yPos, (SIZE * SCALE).toInt(), (SIZE * SCALE).toInt(), Component.literal("Pasture Move"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
@@ -35,7 +36,7 @@ class PastureSlotIconButton(
|
||||
y = yPos / SCALE,
|
||||
width = SIZE,
|
||||
height = SIZE,
|
||||
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble())) SIZE else 0,
|
||||
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble()) || parent.controllerFocused) SIZE else 0,
|
||||
textureHeight = SIZE * 2,
|
||||
texture = baseResource,
|
||||
scale = SCALE
|
||||
|
||||
@@ -15,6 +15,8 @@ import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.pc.PCGUI
|
||||
import com.cobblemon.mod.common.client.gui.pc.StorageWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.net.messages.server.pasture.UnpastureAllPokemonPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -34,6 +36,9 @@ class PastureWidget(
|
||||
private val baseResource = cobblemonResource("textures/gui/pasture/pasture_panel.png")
|
||||
}
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(storageWidget.pcGui)
|
||||
var lastSelectedIndex = 0
|
||||
|
||||
private val recallButton = RecallButton(
|
||||
x = x + 6,
|
||||
y = y + 153
|
||||
@@ -48,6 +53,23 @@ class PastureWidget(
|
||||
parent = this
|
||||
)
|
||||
|
||||
init {
|
||||
controllerComponentManager.componentList["recall_all"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = recallButton
|
||||
it.up = {
|
||||
if (pastureScrollList.children().isEmpty()) controllerComponentManager.componentList["empty"]
|
||||
else controllerComponentManager.componentList["slot_${pastureScrollList.children().size-1}"]
|
||||
}
|
||||
}
|
||||
controllerComponentManager.componentList["empty"] = ControllerComponent().also {
|
||||
it.down = { controllerComponentManager.componentList["recall_all"] }
|
||||
}
|
||||
controllerComponentManager.let {
|
||||
it.focused = if (pastureScrollList.children().isEmpty()) it.componentList["empty"]
|
||||
else it.componentList["slot_0"]
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
|
||||
@@ -40,7 +40,7 @@ class RecallButton(
|
||||
y = y,
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble())) HEIGHT else 0,
|
||||
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble()) || isFocused) HEIGHT else 0,
|
||||
textureHeight = HEIGHT * 2
|
||||
)
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.ExitButton
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.gui.pasture.PasturePokemonScrollList
|
||||
import com.cobblemon.mod.common.client.gui.summary.Summary
|
||||
import com.cobblemon.mod.common.client.gui.summary.Summary.Companion.iconCosmeticItemResource
|
||||
import com.cobblemon.mod.common.client.gui.summary.Summary.Companion.iconHeldItemResource
|
||||
@@ -28,6 +29,10 @@ import com.cobblemon.mod.common.client.gui.summary.SummaryButton
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.MarkingsWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.ModelWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.common.reformatNatureTextIfMinted
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerScreen
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.SubManagerComponent
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.storage.ClientPC
|
||||
import com.cobblemon.mod.common.client.storage.ClientParty
|
||||
@@ -44,12 +49,15 @@ import com.cobblemon.mod.common.util.toAssetPath
|
||||
import com.mojang.blaze3d.platform.InputConstants
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.EditBox
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import kotlin.isInitialized
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import org.joml.Vector4i
|
||||
|
||||
class PCGUI(
|
||||
val pc: ClientPC,
|
||||
@@ -57,7 +65,7 @@ class PCGUI(
|
||||
val configuration: PCGUIConfiguration,
|
||||
val openOnBox: Int = CobblemonClient.lastPcBoxViewed,
|
||||
val unseenWallpapers: MutableSet<ResourceLocation> = mutableSetOf()
|
||||
) : Screen(Component.translatable("cobblemon.ui.pc.title")), CobblemonRenderable {
|
||||
) : Screen(Component.translatable("cobblemon.ui.pc.title")), CobblemonRenderable, ControllerScreen {
|
||||
|
||||
companion object {
|
||||
const val BASE_WIDTH = 349
|
||||
@@ -128,6 +136,14 @@ class PCGUI(
|
||||
var selectPointerOffsetIncrement = false
|
||||
var displayOptions = false
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(this)
|
||||
var lastSelectedBeforeOptions = ""
|
||||
var controllerShiftToggle = false
|
||||
|
||||
override fun getComponentManager(): ControllerComponentManager {
|
||||
return controllerComponentManager
|
||||
}
|
||||
|
||||
override fun renderBlurredBackground(delta: Float) {}
|
||||
|
||||
override fun renderMenuBackground(context: GuiGraphics) {}
|
||||
@@ -198,30 +214,39 @@ class PCGUI(
|
||||
this.addRenderableWidget(boxNameWidget)
|
||||
|
||||
// Initialise box options if not pasture
|
||||
var optionsButton: IconButton? = null
|
||||
var wallpaperButton: IconButton? = null
|
||||
if (storageWidget.pastureWidget == null) {
|
||||
optionButtons.clear()
|
||||
addRenderableWidget(
|
||||
IconButton(
|
||||
pX = x + 218,
|
||||
pY = y + 186,
|
||||
buttonWidth = 16,
|
||||
buttonHeight = 16,
|
||||
resource = buttonOptionsResource,
|
||||
label = "options"
|
||||
) {
|
||||
displayOptions = !displayOptions
|
||||
if (!displayOptions && wallpaperWidget.visible) {
|
||||
configuration.showParty = true
|
||||
wallpaperWidget.visible = false
|
||||
}
|
||||
(it as IconButton).highlighted = displayOptions
|
||||
storageWidget.setupStorageSlots()
|
||||
for (button in optionButtons) {
|
||||
button.visible = displayOptions
|
||||
button.highlighted = false
|
||||
}
|
||||
}.also { it.highlighted = displayOptions }
|
||||
)
|
||||
optionsButton = IconButton(
|
||||
pX = x + 218,
|
||||
pY = y + 186,
|
||||
buttonWidth = 16,
|
||||
buttonHeight = 16,
|
||||
resource = buttonOptionsResource,
|
||||
label = "options"
|
||||
) {
|
||||
if (!displayOptions) {
|
||||
lastSelectedBeforeOptions = controllerComponentManager.currentSelectedComponent
|
||||
controllerComponentManager.focused = controllerComponentManager.componentList["setting_button_0"]
|
||||
controllerShiftToggle = false
|
||||
} else {
|
||||
controllerComponentManager.focused = controllerComponentManager.componentList[lastSelectedBeforeOptions]
|
||||
}
|
||||
|
||||
displayOptions = !displayOptions
|
||||
if (!displayOptions && wallpaperWidget.visible) {
|
||||
configuration.showParty = true
|
||||
wallpaperWidget.visible = false
|
||||
}
|
||||
(it as IconButton).highlighted = displayOptions
|
||||
storageWidget.setupStorageSlots()
|
||||
for (button in optionButtons) {
|
||||
button.visible = displayOptions
|
||||
button.highlighted = false
|
||||
}
|
||||
}.also { it.highlighted = displayOptions }
|
||||
addRenderableWidget(optionsButton)
|
||||
|
||||
// Add Wallpaper Widget
|
||||
this.wallpaperWidget = WallpapersScrollingWidget(
|
||||
@@ -237,7 +262,7 @@ class PCGUI(
|
||||
this.addRenderableWidget(wallpaperWidget)
|
||||
|
||||
// Add Wallpaper Settings Button
|
||||
this.addRenderableWidget(IconButton(
|
||||
wallpaperButton = IconButton(
|
||||
pX = x + 242,
|
||||
pY = y + 31,
|
||||
buttonWidth = 20,
|
||||
@@ -250,14 +275,16 @@ class PCGUI(
|
||||
wallpaperWidget.visible = !isVisible
|
||||
(it as IconButton).highlighted = !isVisible
|
||||
if (!unseenWallpapers.isEmpty()) MarkPCBoxWallpapersSeenPacket(unseenWallpapers).sendToServer()
|
||||
controllerComponentManager.focused = if (wallpaperWidget.visible) controllerComponentManager.componentList["wallpaper_widget"] else controllerComponentManager.componentList["wallpaper_button"]
|
||||
}.also {
|
||||
it.visible = displayOptions
|
||||
optionButtons.add(it)
|
||||
})
|
||||
}
|
||||
this.addRenderableWidget(wallpaperButton)
|
||||
|
||||
PokemonSortMode.entries.forEachIndexed { index, sortType ->
|
||||
val typeName = sortType.name.lowercase()
|
||||
this.addRenderableWidget(IconButton(
|
||||
val button = IconButton(
|
||||
pX = x + 92 + (12 * index),
|
||||
pY = y + 31,
|
||||
buttonWidth = 20,
|
||||
@@ -268,33 +295,58 @@ class PCGUI(
|
||||
label = "sort_${typeName}"
|
||||
) {
|
||||
storageWidget.resetSelected()
|
||||
SortPCBoxPacket(pc.uuid, storageWidget.box, sortType, hasShiftDown()).sendToServer()
|
||||
SortPCBoxPacket(pc.uuid, storageWidget.box, sortType, hasShiftDown() || (controllerComponentManager.active && controllerShiftToggle)).sendToServer()
|
||||
}.also {
|
||||
it.visible = displayOptions
|
||||
optionButtons.add(it)
|
||||
})
|
||||
}
|
||||
this.addRenderableWidget(button)
|
||||
|
||||
controllerComponentManager.componentList["setting_button_${index}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = button
|
||||
if (index - 1 >= 0) it.left = { controllerComponentManager.componentList["setting_button_${index-1}"] }
|
||||
if (index + 1 <= PokemonSortMode.entries.size - 1) it.right = { controllerComponentManager.componentList["setting_button_${index+1}"] }
|
||||
else it.right = { controllerComponentManager.componentList["wallpaper_button"] }
|
||||
it.onSpecial1 = {
|
||||
controllerShiftToggle = !controllerShiftToggle
|
||||
playSound(CobblemonSounds.PC_CLICK)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
controllerComponentManager.componentList["wallpaper_button"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = wallpaperButton
|
||||
it.left = { controllerComponentManager.componentList["setting_button_${PokemonSortMode.entries.size - 1}"] }
|
||||
}
|
||||
controllerComponentManager.componentList["wallpaper_widget"] = SubManagerComponent().also {
|
||||
it.subManager = { wallpaperWidget.controllerComponentManager }
|
||||
it.onSpecial1 = {
|
||||
controllerShiftToggle = !controllerShiftToggle
|
||||
playSound(CobblemonSounds.PC_CLICK)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.markingsWidget = MarkingsWidget(x + 29, y + 96.5, null, false)
|
||||
this.addRenderableWidget(markingsWidget)
|
||||
|
||||
// Held/Cosmetic Item Button
|
||||
addRenderableWidget(
|
||||
SummaryButton(
|
||||
buttonX = x + 67F,
|
||||
buttonY = y + 107F,
|
||||
buttonWidth = 12,
|
||||
buttonHeight = 12,
|
||||
scale = 0.5F,
|
||||
resource = iconCosmeticItemResource,
|
||||
activeResource = iconHeldItemResource,
|
||||
clickAction = {
|
||||
showCosmeticItem = !showCosmeticItem
|
||||
(it as SummaryButton).buttonActive = showCosmeticItem
|
||||
}
|
||||
)
|
||||
|
||||
val switchCosmetic = SummaryButton(
|
||||
buttonX = x + 67F,
|
||||
buttonY = y + 107F,
|
||||
buttonWidth = 12,
|
||||
buttonHeight = 12,
|
||||
scale = 0.5F,
|
||||
resource = iconCosmeticItemResource,
|
||||
activeResource = iconHeldItemResource,
|
||||
clickAction = {
|
||||
showCosmeticItem = !showCosmeticItem
|
||||
(it as SummaryButton).buttonActive = showCosmeticItem
|
||||
}
|
||||
)
|
||||
// Held/Cosmetic Item Button
|
||||
addRenderableWidget(switchCosmetic)
|
||||
|
||||
|
||||
addRenderableWidget(
|
||||
IconButton(
|
||||
@@ -312,6 +364,149 @@ class PCGUI(
|
||||
this.setPreviewPokemon(null)
|
||||
|
||||
super.init()
|
||||
|
||||
controllerComponentManager.componentList["box_name"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = boxNameWidget
|
||||
it.down = { controllerComponentManager.componentList["storage"] }
|
||||
}
|
||||
controllerComponentManager.componentList["storage"] = SubManagerComponent().also {
|
||||
it.subManager = { storageWidget.controllerComponentManager }
|
||||
it.up = {
|
||||
var returnVal: ControllerComponent? = null
|
||||
val manager = storageWidget.controllerComponentManager
|
||||
if (manager.up() && manager.currentSelectedComponent.startsWith("slot") && storageWidget.grabbedSlot == null) {
|
||||
returnVal = controllerComponentManager.componentList["box_name"]
|
||||
}
|
||||
returnVal
|
||||
}
|
||||
it.down = {
|
||||
var returnVal: ControllerComponent? = null
|
||||
val manager = storageWidget.controllerComponentManager
|
||||
if (manager.down() && manager.currentSelectedComponent.startsWith("slot") && storageWidget.grabbedSlot == null) {
|
||||
returnVal = controllerComponentManager.componentList["filter"]
|
||||
}
|
||||
returnVal
|
||||
}
|
||||
it.onSpecial1 = {
|
||||
if (storageWidget.controllerComponentManager.currentSelectedComponent == "pasture") {
|
||||
storageWidget.controllerComponentManager.special1()
|
||||
} else {
|
||||
switchCosmetic.onPress()
|
||||
playSound(CobblemonSounds.PC_CLICK)
|
||||
}
|
||||
|
||||
}
|
||||
it.onSpecial2 = { if (storageWidget.pastureWidget == null) displayOptions = !displayOptions }
|
||||
}
|
||||
controllerComponentManager.componentList["filter"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = filterWidget
|
||||
it.up = { controllerComponentManager.componentList["storage"] }
|
||||
}
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["storage"]
|
||||
it.onTabNav = { nextTab ->
|
||||
storageWidget.box += if (nextTab) 1 else -1
|
||||
playSound(CobblemonSounds.PC_CLICK)
|
||||
}
|
||||
it.onSecondaryTabNav = { nextTab ->
|
||||
var nextValue = currentStatIndex + if (nextTab) 1 else -1
|
||||
if (nextValue > 2) nextValue = 0
|
||||
if (nextValue < 0) nextValue = 2
|
||||
currentStatIndex = nextValue
|
||||
playSound(CobblemonSounds.PC_CLICK)
|
||||
}
|
||||
it.onStart = {
|
||||
if (it.currentSelectedComponent != "storage" || storageWidget.grabbedSlot == null) {
|
||||
it.focused = controllerComponentManager.componentList["filter"]
|
||||
storageWidget.controllerComponentManager.focused =
|
||||
storageWidget.controllerComponentManager.componentList["slot_4_0"]
|
||||
it.openKeyboard.invoke(it.focused!!.associatedGUIElement as EditBox)
|
||||
playSound(SoundEvents.UI_BUTTON_CLICK.value())
|
||||
}
|
||||
}
|
||||
it.onSpecial2 = {
|
||||
if (storageWidget.pastureWidget == null && storageWidget.grabbedSlot == null) {
|
||||
optionsButton!!.onPress()
|
||||
playSound(CobblemonSounds.PC_CLICK)
|
||||
}
|
||||
}
|
||||
it.onBack = {
|
||||
if (storageWidget.pastureWidget == null && wallpaperWidget.visible) {
|
||||
wallpaperButton?.onPress()
|
||||
playSound(CobblemonSounds.PC_CLICK)
|
||||
} else if (displayOptions) {
|
||||
optionsButton!!.onPress()
|
||||
playSound(CobblemonSounds.PC_CLICK)
|
||||
} else if (storageWidget.grabbedSlot != null) {
|
||||
storageWidget.resetSelected()
|
||||
} else {
|
||||
closeNormally(true)
|
||||
}
|
||||
}
|
||||
it.onActiveChange = { state ->
|
||||
if (!state) {
|
||||
storageWidget.controllerComponentManager.active = false
|
||||
} else {
|
||||
if (it.currentSelectedComponent == "storage") storageWidget.controllerComponentManager.active = true
|
||||
}
|
||||
}
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.START,
|
||||
{ true },
|
||||
Component.translatable("cobblemon.controller.quick_search")
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.TAB,
|
||||
{ true },
|
||||
Component.translatable("cobblemon.controller.pc.switch_box")
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SECONDARY_TAB,
|
||||
{ true },
|
||||
Component.translatable("cobblemon.controller.pc.switch_stats")
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SPECIAL_1,
|
||||
{ it.currentSelectedComponent == "storage" && storageWidget.controllerComponentManager.currentSelectedComponent != "pasture" },
|
||||
Component.translatable("cobblemon.controller.toggle_held_item_type"),
|
||||
true
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SPECIAL_1,
|
||||
{ it.currentSelectedComponent == "storage" && storageWidget.controllerComponentManager.currentSelectedComponent == "pasture" &&
|
||||
storageWidget.pastureWidget!!.controllerComponentManager.currentSelectedComponent.startsWith("slot_") },
|
||||
Component.translatable("cobblemon.controller.pc.toggle_defence"),
|
||||
true
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SPECIAL_2,
|
||||
{ it.currentSelectedComponent == "storage" && storageWidget.controllerComponentManager.currentSelectedComponent != "pasture" },
|
||||
Component.translatable("cobblemon.controller.pc.box_settings"),
|
||||
true
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSelectionPosAndSize(): Vector4i? {
|
||||
val data = super.getSelectionPosAndSize()
|
||||
if (data == null) {
|
||||
when (controllerComponentManager.currentSelectedComponent) {
|
||||
"wallpaper_widget" -> return Vector4i(wallpaperWidget.rowLeft - 2, wallpaperWidget.getRowTop(wallpaperWidget.children().indexOf(focused)) + 2,
|
||||
WallpapersScrollingWidget.SLOT_WIDTH + 4, WallpapersScrollingWidget.SLOT_HEIGHT + 4)
|
||||
"storage" -> {
|
||||
if (storageWidget.pastureWidget != null && storageWidget.controllerComponentManager.currentSelectedComponent == "pasture") {
|
||||
val scrollWidget = storageWidget.pastureWidget!!.pastureScrollList
|
||||
if (storageWidget.pastureWidget!!.controllerComponentManager.currentSelectedComponent.startsWith("slot_")) {
|
||||
return Vector4i(scrollWidget.rowLeft - 6, scrollWidget.getRowTop(scrollWidget.children().indexOf(focused)),
|
||||
PasturePokemonScrollList.SLOT_WIDTH + 4, PasturePokemonScrollList.SLOT_HEIGHT + 4)
|
||||
} else if (storageWidget.pastureWidget!!.controllerComponentManager.currentSelectedComponent == "empty") {
|
||||
return Vector4i(scrollWidget.x - 2, scrollWidget.y - 11, scrollWidget.width + 4, scrollWidget.height + 13)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
@@ -685,7 +880,7 @@ class PCGUI(
|
||||
)
|
||||
|
||||
if (!optionButtons.isEmpty() && displayOptions) {
|
||||
for (button in optionButtons) button.showAlt = hasShiftDown()
|
||||
for (button in optionButtons) button.showAlt = hasShiftDown() || (controllerComponentManager.active && controllerShiftToggle)
|
||||
}
|
||||
|
||||
super.render(context, mouseX, mouseY, delta)
|
||||
@@ -700,6 +895,8 @@ class PCGUI(
|
||||
if (itemHovered) context.renderTooltip(Minecraft.getInstance().font, displayedItem, mouseX, mouseY)
|
||||
}
|
||||
}
|
||||
|
||||
renderSelection(matrices)
|
||||
}
|
||||
|
||||
fun closeNormally(unlink: Boolean = true) {
|
||||
|
||||
@@ -27,8 +27,8 @@ class ReleaseButton(
|
||||
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Release"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
private const val WIDTH = 58
|
||||
private const val HEIGHT = 16
|
||||
const val WIDTH = 58
|
||||
const val HEIGHT = 16
|
||||
|
||||
private val buttonResource = cobblemonResource("textures/gui/pc/pc_release_button.png")
|
||||
}
|
||||
@@ -42,7 +42,7 @@ class ReleaseButton(
|
||||
y = y,
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble())) HEIGHT else 0,
|
||||
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble()) || isFocused) HEIGHT else 0,
|
||||
textureHeight = HEIGHT * 2
|
||||
)
|
||||
|
||||
|
||||
+3
-3
@@ -28,8 +28,8 @@ class ReleaseConfirmButton(
|
||||
) : Button(x, y, WIDTH, HEIGHT, Component.literal("ReleaseConfirm"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
private const val WIDTH = 30
|
||||
private const val HEIGHT = 13
|
||||
const val WIDTH = 30
|
||||
const val HEIGHT = 13
|
||||
|
||||
private val buttonResource = cobblemonResource("textures/gui/pc/pc_release_button_confirm.png")
|
||||
}
|
||||
@@ -43,7 +43,7 @@ class ReleaseConfirmButton(
|
||||
y = y,
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble())) HEIGHT else 0,
|
||||
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble()) || isFocused) HEIGHT else 0,
|
||||
textureHeight = HEIGHT * 2
|
||||
)
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.pasture.PasturePCGUIConfiguration
|
||||
import com.cobblemon.mod.common.client.gui.pasture.PastureWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.SubManagerComponent
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.gui.PCBoxWallpaperRepository
|
||||
import com.cobblemon.mod.common.client.settings.ServerSettings
|
||||
@@ -41,6 +44,7 @@ import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import org.joml.Vector2i
|
||||
|
||||
class StorageWidget(
|
||||
pX: Int, pY: Int,
|
||||
@@ -91,6 +95,9 @@ class StorageWidget(
|
||||
this.setupStorageSlots()
|
||||
}
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(pcGui)
|
||||
var grabbedSlotControllerPos = Vector2i()
|
||||
|
||||
init {
|
||||
this.setupStorageSlots()
|
||||
|
||||
@@ -102,6 +109,7 @@ class StorageWidget(
|
||||
if (!displayConfirmRelease && canDeleteSelected()) {
|
||||
displayConfirmRelease = true
|
||||
playSound(CobblemonSounds.PC_CLICK)
|
||||
controllerComponentManager.focused = controllerComponentManager.componentList["release_no"]
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -126,6 +134,7 @@ class StorageWidget(
|
||||
playSound(CobblemonSounds.PC_RELEASE)
|
||||
resetSelected()
|
||||
displayConfirmRelease = false
|
||||
controllerComponentManager.focused = controllerComponentManager.componentList["slot_0_0"]
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -139,6 +148,7 @@ class StorageWidget(
|
||||
if (displayConfirmRelease) {
|
||||
displayConfirmRelease = false
|
||||
playSound(CobblemonSounds.PC_CLICK)
|
||||
controllerComponentManager.focused = controllerComponentManager.componentList["release"]
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -146,6 +156,47 @@ class StorageWidget(
|
||||
if (pcGui.configuration is PasturePCGUIConfiguration) {
|
||||
this.pastureWidget = PastureWidget(this, pcGui.configuration, x + 182, y - 19)
|
||||
}
|
||||
|
||||
controllerComponentManager.componentList["release"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = releaseButton
|
||||
it.up = { controllerComponentManager.componentList["party_slot_2_0"] }
|
||||
it.left = { controllerComponentManager.componentList["slot_4_5"] }
|
||||
it.onSelect = {
|
||||
grabbedSlotControllerPos = Vector2i(releaseButton.x + ReleaseButton.WIDTH / 2 + 4, releaseButton.y + ReleaseButton.HEIGHT / 2 + 4)
|
||||
}
|
||||
}
|
||||
controllerComponentManager.componentList["release_no"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = releaseNoButton
|
||||
it.up = { controllerComponentManager.componentList["party_slot_2_1"] }
|
||||
it.left = { controllerComponentManager.componentList["release_yes"] }
|
||||
it.onSelect = {
|
||||
grabbedSlotControllerPos = Vector2i(releaseNoButton.x + ReleaseConfirmButton.WIDTH / 2 + 4, releaseNoButton.y + ReleaseConfirmButton.HEIGHT / 2 + 4)
|
||||
}
|
||||
it.onUnselect = {
|
||||
if (!controllerComponentManager.currentSelectedComponent.startsWith("release")) displayConfirmRelease = false
|
||||
}
|
||||
}
|
||||
controllerComponentManager.componentList["release_yes"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = releaseYesButton
|
||||
it.up = { controllerComponentManager.componentList["party_slot_2_0"] }
|
||||
it.left = { controllerComponentManager.componentList["slot_4_5"] }
|
||||
it.right = { controllerComponentManager.componentList["release_no"] }
|
||||
it.onSelect = {
|
||||
grabbedSlotControllerPos = Vector2i(releaseYesButton.x + ReleaseConfirmButton.WIDTH / 2 + 4, releaseYesButton.y + ReleaseConfirmButton.HEIGHT / 2 + 4)
|
||||
}
|
||||
it.onUnselect = {
|
||||
if (!controllerComponentManager.currentSelectedComponent.startsWith("release")) displayConfirmRelease = false
|
||||
}
|
||||
}
|
||||
if (pastureWidget != null) {
|
||||
controllerComponentManager.componentList["pasture"] = SubManagerComponent().also {
|
||||
it.subManager = { pastureWidget!!.controllerComponentManager }
|
||||
it.left = { controllerComponentManager.componentList["slot_0_5"] }
|
||||
}
|
||||
}
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["slot_0_0"]
|
||||
}
|
||||
}
|
||||
|
||||
fun canDeleteSelected(): Boolean {
|
||||
@@ -164,7 +215,7 @@ class StorageWidget(
|
||||
|
||||
for (row in 1..5) {
|
||||
for (col in 1..6) {
|
||||
BoxStorageSlot(
|
||||
val slot = BoxStorageSlot(
|
||||
x = boxStartX + ((col - 1) * (StorageSlot.SIZE + BOX_SLOT_PADDING)),
|
||||
y = boxStartY + ((row - 1) * (StorageSlot.SIZE + BOX_SLOT_PADDING)),
|
||||
parent = this,
|
||||
@@ -176,6 +227,27 @@ class StorageWidget(
|
||||
this.boxSlots.add(widget)
|
||||
}
|
||||
index++
|
||||
|
||||
val adjCol = col - 1
|
||||
val adjRow = row - 1
|
||||
controllerComponentManager.componentList["slot_${adjRow}_${adjCol}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = slot
|
||||
if (adjCol - 1 >= 0) it.left = { controllerComponentManager.componentList["slot_${adjRow}_${adjCol-1}"] }
|
||||
if (adjCol + 1 <= 5) it.right = { controllerComponentManager.componentList["slot_${adjRow}_${adjCol+1}"] }
|
||||
else if (pastureWidget == null) {
|
||||
it.right = when (adjRow) {
|
||||
4 -> {{ if (canDeleteSelected()) controllerComponentManager.componentList["release"] else null }}
|
||||
3 -> {{ controllerComponentManager.componentList["party_slot_2_0"] }}
|
||||
else -> {{ controllerComponentManager.componentList["party_slot_${adjRow}_0"] }}
|
||||
}
|
||||
} else {
|
||||
it.right = { controllerComponentManager.componentList["pasture"] }
|
||||
}
|
||||
if (adjRow - 1 >= 0) it.up = { controllerComponentManager.componentList["slot_${adjRow-1}_${adjCol}"] }
|
||||
if (adjRow + 1 <= 4) it.down = { controllerComponentManager.componentList["slot_${adjRow+1}_${adjCol}"] }
|
||||
it.onSelect = { grabbedSlotControllerPos = Vector2i(slot.x + slot.width / 2 + 4, slot.y + slot.height / 2 + 4) }
|
||||
it.onPress = { if (slot.clickable()) slot.onPress() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +266,7 @@ class StorageWidget(
|
||||
partyY += ((StorageSlot.SIZE + PARTY_SLOT_PADDING) * offsetIndex) + offsetY
|
||||
}
|
||||
|
||||
PartyStorageSlot(
|
||||
val slot = PartyStorageSlot(
|
||||
x = partyX,
|
||||
y = partyY,
|
||||
parent = this,
|
||||
@@ -205,6 +277,22 @@ class StorageWidget(
|
||||
this.addWidget(widget)
|
||||
this.partySlots.add(widget)
|
||||
}
|
||||
|
||||
val row = partyIndex / 2
|
||||
val col = partyIndex % 2
|
||||
controllerComponentManager.componentList["party_slot_${row}_${col}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = slot
|
||||
if (col == 1) it.left = { controllerComponentManager.componentList["party_slot_${row}_0"] }
|
||||
else {
|
||||
it.right = { controllerComponentManager.componentList["party_slot_${row}_1"] }
|
||||
it.left = { controllerComponentManager.componentList["slot_${row}_5"] }
|
||||
}
|
||||
if (row - 1 >= 0) it.up = { controllerComponentManager.componentList["party_slot_${row-1}_${col}"] }
|
||||
if (row + 1 <= 2) it.down = { controllerComponentManager.componentList["party_slot_${row+1}_${col}"] }
|
||||
else it.down = { if (canDeleteSelected()) controllerComponentManager.componentList["release"] else null }
|
||||
it.onSelect = { grabbedSlotControllerPos = Vector2i(slot.x + slot.width / 2 + 4, slot.y + slot.height / 2 + 4) }
|
||||
it.onPress = { if (slot.clickable()) slot.onPress() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +395,7 @@ class StorageWidget(
|
||||
this.boxSlots.forEach { slot ->
|
||||
slot.render(context, mouseX, mouseY, delta)
|
||||
val pokemon = slot.getPokemon()
|
||||
if (grabbedSlot == null && slot.isHovered(mouseX, mouseY)
|
||||
if (grabbedSlot == null && (slot.isHovered(mouseX, mouseY) || slot.isFocused)
|
||||
&& pokemon != null && pokemon != pcGui.previewPokemon
|
||||
) {
|
||||
pcGui.setPreviewPokemon(pokemon, pcGui.isPreviewInParty ?: false)
|
||||
@@ -323,7 +411,7 @@ class StorageWidget(
|
||||
this.partySlots.forEach { slot ->
|
||||
slot.render(context, mouseX, mouseY, delta)
|
||||
val pokemon = slot.getPokemon()
|
||||
if (grabbedSlot == null && slot.isHovered(mouseX, mouseY)
|
||||
if (grabbedSlot == null && (slot.isHovered(mouseX, mouseY) || slot.isFocused)
|
||||
&& pokemon != null && pokemon != pcGui.previewPokemon
|
||||
) {
|
||||
pcGui.setPreviewPokemon(pokemon, pcGui.isPreviewInParty ?: true)
|
||||
@@ -335,7 +423,11 @@ class StorageWidget(
|
||||
pastureWidget?.render(context, mouseX, mouseY, delta)
|
||||
matrices.pushPose()
|
||||
matrices.translate(0.0, 0.0, 1000.0)
|
||||
grabbedSlot?.render(context, mouseX, mouseY, delta)
|
||||
if (controllerComponentManager.active) {
|
||||
grabbedSlot?.render(context, grabbedSlotControllerPos.x, grabbedSlotControllerPos.y, delta)
|
||||
} else {
|
||||
grabbedSlot?.render(context, mouseX, mouseY, delta)
|
||||
}
|
||||
matrices.popPose()
|
||||
}
|
||||
|
||||
@@ -372,6 +464,9 @@ class StorageWidget(
|
||||
}
|
||||
|
||||
// Reset release confirmation
|
||||
if (controllerComponentManager.currentSelectedComponent.startsWith("release")) {
|
||||
controllerComponentManager.focused = controllerComponentManager.componentList["slot_0_0"]
|
||||
}
|
||||
displayConfirmRelease = false
|
||||
|
||||
// Clicking on itself, so unselect position
|
||||
|
||||
+36
-8
@@ -15,6 +15,8 @@ import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.ScrollingWidget
|
||||
import com.cobblemon.mod.common.client.gui.pc.WallpapersScrollingWidget.WallpaperEntry
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.gui.PCBoxWallpaperRepository
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.RequestChangePCBoxWallpaperPacket
|
||||
@@ -49,10 +51,14 @@ class WallpapersScrollingWidget(
|
||||
val backgroundResource = cobblemonResource("textures/gui/pc/wallpaper_scroll_background.png")
|
||||
}
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(pcGui)
|
||||
|
||||
init {
|
||||
this.visible = false
|
||||
this.y = pY
|
||||
createEntries()
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun getScrollbarPosition(): Int = x + width - 3
|
||||
@@ -90,6 +96,24 @@ class WallpapersScrollingWidget(
|
||||
}
|
||||
}
|
||||
|
||||
override fun addEntry(entry: WallpaperEntry): Int {
|
||||
val i = super.addEntry(entry)
|
||||
controllerComponentManager.componentList["wallpaper_${i}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = entry
|
||||
it.onPress = { entry.selectWallpaper() }
|
||||
it.onFocus = { ensureVisible(entry) }
|
||||
}
|
||||
if (children().size > 1) {
|
||||
controllerComponentManager.componentList["wallpaper_${i}"]?.let {
|
||||
it.pairUp(controllerComponentManager.componentList["wallpaper_${i-1}"])
|
||||
it.pairDown(controllerComponentManager.componentList["wallpaper_${0}"])
|
||||
}
|
||||
} else {
|
||||
controllerComponentManager.focused = controllerComponentManager.componentList["wallpaper_${i}"]
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
override fun getRowLeft(): Int {
|
||||
return this.left + SLOT_PADDING
|
||||
}
|
||||
@@ -116,7 +140,7 @@ class WallpapersScrollingWidget(
|
||||
partialTick: Float
|
||||
) {
|
||||
val matrices = guiGraphics.pose()
|
||||
val resource = if (Screen.hasShiftDown() && altWallpaper !== null) altWallpaper else wallpaper
|
||||
val resource = if ((Screen.hasShiftDown() || (pcGui.controllerComponentManager.active && pcGui.controllerShiftToggle)) && altWallpaper !== null) altWallpaper else wallpaper
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = resource,
|
||||
@@ -133,24 +157,28 @@ class WallpapersScrollingWidget(
|
||||
y = top + SLOT_PADDING,
|
||||
width = width,
|
||||
height = SLOT_HEIGHT,
|
||||
vOffset = if (hovering) SLOT_HEIGHT else 0,
|
||||
vOffset = if (hovering || isFocused) SLOT_HEIGHT else 0,
|
||||
textureHeight = SLOT_HEIGHT * 2
|
||||
)
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (this@WallpapersScrollingWidget.visible && isMouseOver(mouseX, mouseY)) {
|
||||
val appliedWallpaper = if (Screen.hasShiftDown()) altWallpaper ?: wallpaper else wallpaper
|
||||
RequestChangePCBoxWallpaperPacket(pcGui.pc.uuid, storageWidget.box, wallpaper, if (Screen.hasShiftDown()) altWallpaper else null).sendToServer()
|
||||
pcGui.pc.boxes[storageWidget.box].wallpaper = appliedWallpaper
|
||||
pcGui.unseenWallpapers.remove(wallpaper)
|
||||
isNew = false
|
||||
minecraft.soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.PC_CLICK, 1.0F))
|
||||
selectWallpaper()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun selectWallpaper() {
|
||||
val appliedWallpaper = if (Screen.hasShiftDown() || (pcGui.controllerComponentManager.active && pcGui.controllerShiftToggle)) altWallpaper ?: wallpaper else wallpaper
|
||||
RequestChangePCBoxWallpaperPacket(pcGui.pc.uuid, storageWidget.box, wallpaper, if (Screen.hasShiftDown() || (pcGui.controllerComponentManager.active && pcGui.controllerShiftToggle)) altWallpaper else null).sendToServer()
|
||||
pcGui.pc.boxes[storageWidget.box].wallpaper = appliedWallpaper
|
||||
pcGui.unseenWallpapers.remove(wallpaper)
|
||||
isNew = false
|
||||
minecraft.soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.PC_CLICK, 1.0F))
|
||||
}
|
||||
|
||||
override fun getNarration(): Component? {
|
||||
return wallpaper.toString().text()
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.pokedex.CaughtCount
|
||||
import com.cobblemon.mod.common.api.pokedex.Dexes
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.api.pokedex.SeenCount
|
||||
import com.cobblemon.mod.common.api.pokedex.def.PokedexDef
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
|
||||
@@ -46,6 +47,10 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_ICON_
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_MOVES
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_SIZE
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_STATS
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.*
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerScreen
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.AbilitiesWidget
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.DescriptionWidget
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.DropsScrollingWidget
|
||||
@@ -74,6 +79,8 @@ import net.minecraft.core.BlockPos
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import org.joml.Vector4i
|
||||
|
||||
/**
|
||||
* Pokedex GUI
|
||||
@@ -85,7 +92,7 @@ class PokedexGUI private constructor(
|
||||
val type: PokedexType,
|
||||
val initSpecies: ResourceLocation?,
|
||||
val blockPos: BlockPos?
|
||||
): Screen(Component.translatable("cobblemon.ui.pokedex.title")), CobblemonRenderable {
|
||||
): Screen(Component.translatable("cobblemon.ui.pokedex.title")), CobblemonRenderable, ControllerScreen {
|
||||
companion object {
|
||||
private val screenBackground = cobblemonResource("textures/gui/pokedex/pokedex_screen.png")
|
||||
|
||||
@@ -163,6 +170,14 @@ class PokedexGUI private constructor(
|
||||
lateinit var tabInfoElement: GuiEventListener
|
||||
var tabInfoIndex = TAB_DESCRIPTION
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(this)
|
||||
var lastTileComponent: ControllerComponent? = null
|
||||
var lastSelectedColumn = 0
|
||||
|
||||
override fun getComponentManager(): ControllerComponentManager {
|
||||
return controllerComponentManager
|
||||
}
|
||||
|
||||
override fun renderBlurredBackground(delta: Float) {}
|
||||
override fun renderMenuBackground(context: GuiGraphics) {}
|
||||
|
||||
@@ -268,6 +283,222 @@ class PokedexGUI private constructor(
|
||||
)
|
||||
addRenderableWidget(categoryFilterButtonDown)
|
||||
updateFilters(true)
|
||||
|
||||
fun hasKnowledge(): Boolean {
|
||||
val currentEntry = pokemonInfoWidget.currentEntry
|
||||
return currentEntry != null && CobblemonClient.clientPokedexData.getKnowledgeForSpecies(currentEntry.speciesId) != PokedexEntryProgress.UNREGISTERED
|
||||
}
|
||||
|
||||
controllerComponentManager.componentList["search"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = searchWidget
|
||||
it.down = {
|
||||
val returnVal = if (scrollScreen.children().isNotEmpty()) {
|
||||
controllerComponentManager.componentList["tile_0_0"]
|
||||
} else {
|
||||
controllerComponentManager.componentList["empty_entries"]
|
||||
}
|
||||
returnVal
|
||||
}
|
||||
it.onSpecial1 = {
|
||||
searchByTypeButton.onPress()
|
||||
playSound(CobblemonSounds.POKEDEX_CLICK_SHORT)
|
||||
}
|
||||
}
|
||||
controllerComponentManager.componentList["empty_entries"] = ControllerComponent().also {
|
||||
it.up = { controllerComponentManager.componentList["search"] }
|
||||
it.right = { controllerComponentManager.componentList["pokemon_render"] }
|
||||
}
|
||||
controllerComponentManager.componentList["pokemon_render"] = ControllerComponent().also {
|
||||
it.left = {
|
||||
val returnVal = if (scrollScreen.children().isNotEmpty()) {
|
||||
lastTileComponent
|
||||
} else {
|
||||
controllerComponentManager.componentList["empty_entries"]
|
||||
}
|
||||
returnVal
|
||||
}
|
||||
it.down = { controllerComponentManager.componentList["info_widget"] }
|
||||
it.onPress = {
|
||||
if (hasKnowledge()) {
|
||||
pokemonInfoWidget.playCry()
|
||||
}
|
||||
}
|
||||
it.onSpecial1 = {
|
||||
if (hasKnowledge() && pokemonInfoWidget.genderButton.visible) {
|
||||
pokemonInfoWidget.genderButton.onPress()
|
||||
playSound(CobblemonSounds.POKEDEX_CLICK_SHORT)
|
||||
}
|
||||
}
|
||||
it.onSpecial2 = {
|
||||
if (hasKnowledge() && pokemonInfoWidget.shinyButton.visible) {
|
||||
pokemonInfoWidget.shinyButton.onPress()
|
||||
playSound(CobblemonSounds.POKEDEX_CLICK_SHORT)
|
||||
}
|
||||
}
|
||||
}
|
||||
controllerComponentManager.componentList["info_widget"] = ControllerComponent().also {
|
||||
it.left = {
|
||||
val returnVal = if (scrollScreen.children().isNotEmpty()) {
|
||||
lastTileComponent
|
||||
} else {
|
||||
controllerComponentManager.componentList["empty_entries"]
|
||||
}
|
||||
returnVal
|
||||
}
|
||||
it.up = { controllerComponentManager.componentList["pokemon_render"] }
|
||||
}
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["tile_0_0"] ?: it.componentList["empty_entries"]
|
||||
it.onTabNav = { nextTab ->
|
||||
if (it.currentSelectedComponent == "empty_entries" || it.currentSelectedComponent.startsWith("tile_")) {
|
||||
updatePokedexRegion(nextTab)
|
||||
playSound(CobblemonSounds.POKEDEX_CLICK_SHORT)
|
||||
} else if (it.currentSelectedComponent == "pokemon_render") {
|
||||
val currentEntry = pokemonInfoWidget.currentEntry
|
||||
if (currentEntry != null) {
|
||||
val hasKnowledge = CobblemonClient.clientPokedexData.getKnowledgeForSpecies(currentEntry.speciesId) != PokedexEntryProgress.UNREGISTERED
|
||||
val showableForms = CobblemonClient.clientPokedexData.getEncounteredForms(currentEntry)
|
||||
if (hasKnowledge && showableForms.size > 1 && showableForms.size > pokemonInfoWidget.selectedFormIndex) {
|
||||
pokemonInfoWidget.switchForm(nextTab)
|
||||
playSound(CobblemonSounds.POKEDEX_CLICK_SHORT)
|
||||
}
|
||||
}
|
||||
} else if (it.currentSelectedComponent == "info_widget") {
|
||||
val trimmedTabs = tabButtons.filterIndexed { index, _ -> tabInfoIndex == index || canSelectTab(index) }.map { tabButtons.indexOf(it) }.toMutableList()
|
||||
var currentTab = trimmedTabs.indexOf(tabInfoIndex) + if (nextTab) 1 else -1
|
||||
if (currentTab > trimmedTabs.size - 1) currentTab = 0
|
||||
else if (currentTab < 0) currentTab = trimmedTabs.size - 1
|
||||
displaytabInfoElement(trimmedTabs[currentTab])
|
||||
if (trimmedTabs.size > 1) playSound(CobblemonSounds.POKEDEX_CLICK_SHORT)
|
||||
}
|
||||
}
|
||||
it.onSecondaryTabNav = { nextTab ->
|
||||
if (it.currentSelectedComponent == "pokemon_render") {
|
||||
pokemonInfoWidget.switchPose(nextTab)
|
||||
playSound(CobblemonSounds.POKEDEX_CLICK_SHORT)
|
||||
} else if (it.currentSelectedComponent == "info_widget") {
|
||||
(tabInfoElement as? AbilitiesWidget)?.let {
|
||||
it.switchAbility(nextTab)
|
||||
playSound(CobblemonSounds.POKEDEX_CLICK_SHORT)
|
||||
}
|
||||
(tabInfoElement as? StatsWidget)?.let { widget ->
|
||||
if (nextTab) {
|
||||
if (children().contains(widget.rightButton)) {
|
||||
widget.rightButton.onPress()
|
||||
playSound(CobblemonSounds.POKEDEX_CLICK_SHORT)
|
||||
}
|
||||
} else {
|
||||
if (children().contains(widget.leftButton)) {
|
||||
widget.leftButton.onPress()
|
||||
playSound(CobblemonSounds.POKEDEX_CLICK_SHORT)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
it.onRHorizontal = { right ->
|
||||
if (it.currentSelectedComponent == "info_widget") {
|
||||
(tabInfoElement as? StatsWidget)?.let { widget ->
|
||||
if (widget.selectedStatTypeIndex == StatsWidget.STAT_TYPE_RIDE) {
|
||||
if (right) {
|
||||
if (children().contains(widget.rightSubButton)) {
|
||||
widget.rightSubButton.onPress()
|
||||
playSound(CobblemonSounds.POKEDEX_CLICK_SHORT)
|
||||
}
|
||||
} else {
|
||||
if (children().contains(widget.leftSubButton)) {
|
||||
widget.leftSubButton.onPress()
|
||||
playSound(CobblemonSounds.POKEDEX_CLICK_SHORT)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
it.onStart = {
|
||||
it.focused = it.componentList["search"]
|
||||
it.openKeyboard.invoke(it.focused!!.associatedGUIElement as EditBox)
|
||||
playSound(SoundEvents.UI_BUTTON_CLICK.value())
|
||||
}
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.START,
|
||||
{ true },
|
||||
Component.translatable("cobblemon.controller.quick_search")
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.TAB,
|
||||
{ it.currentSelectedComponent == "empty_entries" || it.currentSelectedComponent.startsWith("tile_") },
|
||||
Component.translatable("cobblemon.controller.dex.cycle_region")
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.TAB,
|
||||
{ it.currentSelectedComponent == "pokemon_render" && hasKnowledge() && pokemonInfoWidget.visibleForms.size > 1},
|
||||
Component.translatable("cobblemon.controller.dex.cycle_form"),
|
||||
true
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SECONDARY_TAB,
|
||||
{ it.currentSelectedComponent == "pokemon_render" && hasKnowledge() },
|
||||
Component.translatable("cobblemon.controller.dex.cycle_animation")
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SECONDARY_TAB,
|
||||
{ it.currentSelectedComponent == "info_widget" && tabInfoElement is AbilitiesWidget },
|
||||
Component.translatable("cobblemon.controller.dex.cycle_ability")
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SECONDARY_TAB,
|
||||
{ it.currentSelectedComponent == "info_widget" && (tabInfoElement as? StatsWidget)?.let { children().contains(it.rightButton) } ?: false },
|
||||
Component.translatable("cobblemon.controller.dex.cycle_stats")
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.JOYSTICK_HORIZONTAL,
|
||||
{ it.currentSelectedComponent == "info_widget" && (tabInfoElement as? StatsWidget)?.let { it.selectedStatTypeIndex == StatsWidget.STAT_TYPE_RIDE && children().contains(it.rightSubButton) } ?: false },
|
||||
Component.translatable("cobblemon.controller.dex.cycle_ride_style")
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SPECIAL_1,
|
||||
{ it.currentSelectedComponent == "search" },
|
||||
Component.translatable("cobblemon.controller.dex.cycle_search_by"),
|
||||
true
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SPECIAL_1,
|
||||
{ it.currentSelectedComponent == "pokemon_render" && hasKnowledge() && pokemonInfoWidget.genderButton.visible },
|
||||
Component.translatable("cobblemon.controller.dex.toggle_gender"),
|
||||
true
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SPECIAL_2,
|
||||
{ it.currentSelectedComponent == "pokemon_render" && hasKnowledge() && pokemonInfoWidget.shinyButton.visible },
|
||||
Component.translatable("cobblemon.controller.dex.toggle_shiny"),
|
||||
true
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.PRESS,
|
||||
{ it.currentSelectedComponent == "pokemon_render" && hasKnowledge() },
|
||||
Component.translatable("cobblemon.controller.dex.play_cry"),
|
||||
true
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSelectionPosAndSize(): Vector4i? {
|
||||
return when (controllerComponentManager.currentSelectedComponent) {
|
||||
"search" -> Vector4i(searchWidget.x - 2, searchWidget.y - 2, searchWidget.width + 15, searchWidget.height + 4)
|
||||
"pokemon_render" -> Vector4i(pokemonInfoWidget.x - 2, pokemonInfoWidget.y + 12, pokemonInfoWidget.width + 6, pokemonInfoWidget.height + 16)
|
||||
"info_widget" -> Vector4i(pokemonInfoWidget.x - 2, pokemonInfoWidget.y + 94, pokemonInfoWidget.width + 6, pokemonInfoWidget.height + 3)
|
||||
"empty_entries" -> Vector4i(scrollScreen.x - 2, scrollScreen.y - 2, scrollScreen.width + 4, scrollScreen.height + 4)
|
||||
else -> {
|
||||
if (controllerComponentManager.currentSelectedComponent.startsWith("tile_")) {
|
||||
val scrollWidget = controllerComponentManager.focused?.associatedGUIElement as? EntriesScrollingWidget.PokemonScrollSlotRow
|
||||
val startX = scrollScreen.x + lastSelectedColumn * (PokedexGUIConstants.SCROLL_SLOT_SIZE + PokedexGUIConstants.SCROLL_SLOT_SPACING)
|
||||
if (scrollWidget != null) {
|
||||
Vector4i(startX - 2, scrollScreen.getRowTop(scrollScreen.children().indexOf(scrollWidget)) + 1, PokedexGUIConstants.SCROLL_SLOT_SIZE + 4, PokedexGUIConstants.SCROLL_SLOT_SIZE + 4)
|
||||
} else null
|
||||
} else null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getSufficientlyKnownForms(selectedEntry: PokedexEntry, tabIndex: Int) = if (tabIndex in looseUnlockTabs) {
|
||||
@@ -412,6 +643,8 @@ class PokedexGUI private constructor(
|
||||
|
||||
super.render(context, mouseX, mouseY, delta)
|
||||
|
||||
renderSelection(context.pose())
|
||||
|
||||
// Search type tooltip
|
||||
if (searchByTypeButton.isButtonHovered(mouseX, mouseY)) {
|
||||
val searchTypeText = lang("ui.pokedex.search.search_by", lang("ui.pokedex.search.type.${selectedSearchByType.name.lowercase()}")).bold()
|
||||
@@ -467,6 +700,7 @@ class PokedexGUI private constructor(
|
||||
if (isDragging && canDragRender) {
|
||||
val dragOffsetY = (oldDragPosX - mouseX).toFloat()
|
||||
pokemonInfoWidget.rotationY = (((pokemonInfoWidget.rotationY + dragOffsetY) % 360 + 360) % 360)
|
||||
pokemonInfoWidget.lastRotationY = pokemonInfoWidget.rotationY
|
||||
}
|
||||
oldDragPosX = mouseX
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
@@ -482,6 +716,18 @@ class PokedexGUI private constructor(
|
||||
|
||||
override fun tick() {
|
||||
if (::pokemonInfoWidget.isInitialized && !pokemonInfoWidget.suppressViewport) pokemonInfoWidget.tick()
|
||||
|
||||
//Controller
|
||||
if (controllerComponentManager.rHorizontalState != 0) {
|
||||
if (controllerComponentManager.currentSelectedComponent == "pokemon_render") {
|
||||
pokemonInfoWidget.rotationY = ((pokemonInfoWidget.rotationY + 5F * controllerComponentManager.rHorizontalState) % 360 + 360) % 360
|
||||
}
|
||||
}
|
||||
if (controllerComponentManager.rVerticalState != 0) {
|
||||
if (controllerComponentManager.currentSelectedComponent == "info_widget") {
|
||||
(tabInfoElement as? InfoTextScrollWidget)?.scroll(-3 * controllerComponentManager.rVerticalState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updatePokedexRegion(nextIndex: Boolean) {
|
||||
@@ -541,6 +787,45 @@ class PokedexGUI private constructor(
|
||||
} else {
|
||||
setSelectedEntry(entries.first())
|
||||
}
|
||||
|
||||
val totalRows = scrollScreen.children().size
|
||||
val lastRowCount = scrollScreen.children()[totalRows-1].dexDataList.size
|
||||
scrollScreen.children().forEachIndexed { row, scrollRow ->
|
||||
scrollRow.dexDataList.forEachIndexed { column, slot ->
|
||||
controllerComponentManager.componentList["tile_${row}_${column}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = scrollRow
|
||||
if (row - 1 >= 0) it.up = { controllerComponentManager.componentList["tile_${row-1}_${column}"] }
|
||||
else it.up = { controllerComponentManager.componentList["search"] }
|
||||
if (row + 1 <= scrollScreen.children().size - 1 &&
|
||||
(row + 1 != totalRows || column <= lastRowCount - 1)) it.down = { controllerComponentManager.componentList["tile_${row+1}_${column}"] }
|
||||
if (column - 1 >= 0) it.left = { controllerComponentManager.componentList["tile_${row}_${column-1}"] }
|
||||
if (column + 1 <= scrollRow.dexDataList.size - 1) it.right = { controllerComponentManager.componentList["tile_${row}_${column+1}"] }
|
||||
else it.right = { controllerComponentManager.componentList["pokemon_render"] }
|
||||
it.onPress = {
|
||||
scrollRow.setPokedexEntry.invoke(slot)
|
||||
playSound(CobblemonSounds.POKEDEX_CLICK)
|
||||
}
|
||||
it.onSelect = {
|
||||
lastTileComponent = it
|
||||
lastSelectedColumn = column
|
||||
scrollScreen.ensureVisible(scrollRow)
|
||||
}
|
||||
it.onFocus = {
|
||||
scrollRow.slotControllerSelected = column
|
||||
}
|
||||
it.onUnfocus = {
|
||||
scrollRow.slotControllerSelected = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (controllerComponentManager.currentSelectedComponent.let { it == "empty_entries" || it.startsWith("tile_") }) {
|
||||
if (entries.isNotEmpty()) {
|
||||
controllerComponentManager.focused = controllerComponentManager.componentList["tile_0_0"]
|
||||
} else {
|
||||
controllerComponentManager.focused = controllerComponentManager.componentList["empty_entries"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ class AbilitiesWidget(x: Int, y: Int): InfoTextScrollWidget(pX = x, pY = y) {
|
||||
|
||||
init { setAbility() }
|
||||
|
||||
private fun switchAbility(nextIndex: Boolean) {
|
||||
fun switchAbility(nextIndex: Boolean) {
|
||||
val size = abilitiesList.size
|
||||
selectedAbilitiesIndex = if (nextIndex) {
|
||||
(selectedAbilitiesIndex + 1) % size
|
||||
|
||||
+3
-1
@@ -149,6 +149,8 @@ class EntriesScrollingWidget(val pX: Int, val pY: Int, val setPokedexEntry: (Pok
|
||||
var x: Int = 0
|
||||
var y: Int = 0
|
||||
|
||||
var slotControllerSelected = -1
|
||||
|
||||
override fun render(
|
||||
context: GuiGraphics,
|
||||
index: Int,
|
||||
@@ -190,7 +192,7 @@ class EntriesScrollingWidget(val pX: Int, val pY: Int, val setPokedexEntry: (Pok
|
||||
height = SCROLL_SLOT_SIZE
|
||||
)
|
||||
|
||||
if (getHoveredSlotIndex(mouseX, mouseY) == index) {
|
||||
if (getHoveredSlotIndex(mouseX, mouseY) == index || slotControllerSelected == index) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = slotHighlight,
|
||||
|
||||
+15
-9
@@ -56,6 +56,7 @@ import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.util.Mth
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import java.io.FileNotFoundException
|
||||
@@ -123,6 +124,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
|
||||
var state = FloatingState()
|
||||
var rotationY = 30F
|
||||
var lastRotationY = rotationY // Used to smooth out rotation for controllers
|
||||
|
||||
var ticksElapsed = 0
|
||||
var pokeBallBackgroundFrame = 0
|
||||
@@ -188,7 +190,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
|
||||
val variationButtons = mutableListOf<VariationButtonWrapper>()
|
||||
|
||||
private val genderButton: ScaledButton = ScaledButton(
|
||||
val genderButton: ScaledButton = ScaledButton(
|
||||
pX + 114F,
|
||||
pY + 27F,
|
||||
20,
|
||||
@@ -200,7 +202,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
}
|
||||
).apply { addWidget(this) }
|
||||
|
||||
private val shinyButton: ScaledButton = ScaledButton(
|
||||
val shinyButton: ScaledButton = ScaledButton(
|
||||
(pX + 126F),
|
||||
(pY + 27F),
|
||||
20,
|
||||
@@ -261,6 +263,8 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
).apply { addWidget(this) }
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
val realDetla = Minecraft.getInstance().timer.getGameTimeDeltaPartialTick(true)
|
||||
|
||||
val currentEntry = this.currentEntry ?: return
|
||||
|
||||
val hasKnowledge =
|
||||
@@ -375,10 +379,10 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
matrices.translate(
|
||||
pX.toDouble() + (POKEMON_PORTRAIT_WIDTH.toDouble() + 2) / 2,
|
||||
pY.toDouble() + portraitStartY - 12,
|
||||
1000.0 // Prevent model from clipping into background
|
||||
10.0 // Dont use too big numbers otherwise side mod overlays can break (controllify keyboard)
|
||||
)
|
||||
matrices.scale(scaleAmount, scaleAmount, scaleAmount)
|
||||
val rotationVector = Vector3f(13F, rotationY, 0F)
|
||||
matrices.scale(scaleAmount, scaleAmount, 0.2F)
|
||||
val rotationVector = Vector3f(13F, Mth.rotLerp(realDetla, lastRotationY, rotationY), 0F)
|
||||
|
||||
drawProfilePokemon(
|
||||
renderablePokemon = renderablePokemon!!,
|
||||
@@ -407,7 +411,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
|
||||
// Ensure elements are not hidden behind Pokémon render
|
||||
matrices.pushPose()
|
||||
matrices.translate(0.0, 0.0, 2000.0)
|
||||
matrices.translate(0.0, 0.0, 20.0)
|
||||
|
||||
if (isSelectedPokemonOwned()) {
|
||||
val primaryType = type[0]
|
||||
@@ -638,12 +642,12 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
type = arrayOf(form.primaryType, form.secondaryType)
|
||||
}
|
||||
|
||||
private fun playCry() {
|
||||
fun playCry() {
|
||||
state.activeAnimations.clear()
|
||||
state.addFirstAnimation(setOf("cry"))
|
||||
}
|
||||
|
||||
private fun switchForm(nextIndex: Boolean) {
|
||||
fun switchForm(nextIndex: Boolean) {
|
||||
if (visibleForms.isEmpty() || currentEntry == null) {
|
||||
return
|
||||
}
|
||||
@@ -664,7 +668,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
updateAspects()
|
||||
}
|
||||
|
||||
private fun switchPose(nextIndex: Boolean) {
|
||||
fun switchPose(nextIndex: Boolean) {
|
||||
if (nextIndex) {
|
||||
if (selectedPoseIndex < poseList.lastIndex) selectedPoseIndex++
|
||||
else selectedPoseIndex = 0
|
||||
@@ -756,6 +760,8 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
val delay = 3
|
||||
if (ticksElapsed % delay == 0) pokeBallBackgroundFrame++
|
||||
if (pokeBallBackgroundFrame == 16) pokeBallBackgroundFrame = 0
|
||||
|
||||
lastRotationY = rotationY
|
||||
}
|
||||
|
||||
fun isWithinPortraitSpace(mouseX: Double, mouseY: Double): Boolean =
|
||||
|
||||
+41
-5
@@ -9,6 +9,9 @@
|
||||
package com.cobblemon.mod.common.client.gui.snapshots
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBuildDetails
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerScreen
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.OptionInstance
|
||||
|
||||
@@ -20,10 +23,12 @@ import net.minecraft.client.gui.components.Tooltip
|
||||
import net.minecraft.client.gui.layouts.FrameLayout
|
||||
import net.minecraft.client.gui.layouts.GridLayout
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.network.chat.CommonComponents
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
|
||||
class SnapshotWarningScreen(val consumer: (Acknowledgement, Boolean) -> Unit) : Screen(TITLE) {
|
||||
class SnapshotWarningScreen(val consumer: (Acknowledgement, Boolean) -> Unit) : Screen(TITLE), ControllerScreen {
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -36,6 +41,12 @@ class SnapshotWarningScreen(val consumer: (Acknowledgement, Boolean) -> Unit) :
|
||||
private val layout = GridLayout().columnSpacing(10).rowSpacing(20)
|
||||
private val dontShowAgain = OptionInstance.createBoolean("cobblemon\$snapshots\$dont_show_again", false)
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(this)
|
||||
|
||||
override fun getComponentManager(): ControllerComponentManager {
|
||||
return controllerComponentManager
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
@@ -45,17 +56,42 @@ class SnapshotWarningScreen(val consumer: (Acknowledgement, Boolean) -> Unit) :
|
||||
helper.addChild(StringWidget(this.title, this.font), 2, settings)
|
||||
helper.addChild(MultiLineTextWidget(DESCRIPTION, this.font).setCentered(true).setMaxWidth(310), 2, settings)
|
||||
|
||||
helper.addChild(Checkbox.builder(Component.translatable("cobblemon.snapshots.warning.dont_show_again"), Minecraft.getInstance().font)
|
||||
val checkbox = Checkbox.builder(Component.translatable("cobblemon.snapshots.warning.dont_show_again"), Minecraft.getInstance().font)
|
||||
.selected(dontShowAgain)
|
||||
.tooltip(Tooltip.create(DONT_SHOW_AGAIN_TIP))
|
||||
.build(), 2, settings)
|
||||
.build()
|
||||
helper.addChild(checkbox, 2, settings)
|
||||
|
||||
helper.addChild(Button.builder(CommonComponents.GUI_YES) { this.consumer.invoke(Acknowledgement.YES, dontShowAgain.get()) }.build())
|
||||
helper.addChild(Button.builder(CommonComponents.GUI_NO) { this.consumer.invoke(Acknowledgement.NO, false) }.build())
|
||||
val yesButton = Button.builder(CommonComponents.GUI_YES) { this.consumer.invoke(Acknowledgement.YES, dontShowAgain.get()) }.build()
|
||||
helper.addChild(yesButton)
|
||||
val noButton = Button.builder(CommonComponents.GUI_NO) { this.consumer.invoke(Acknowledgement.NO, false) }.build()
|
||||
helper.addChild(noButton)
|
||||
|
||||
this.layout.visitWidgets { this.addRenderableWidget(it) }
|
||||
this.layout.arrangeElements()
|
||||
this.repositionElements()
|
||||
|
||||
controllerComponentManager.componentList["checkbox"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = checkbox
|
||||
it.down = { controllerComponentManager.componentList["yes_button"] }
|
||||
it.onPress = {
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F))
|
||||
checkbox.onPress()
|
||||
}
|
||||
}
|
||||
controllerComponentManager.componentList["yes_button"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = yesButton
|
||||
it.up = { controllerComponentManager.componentList["checkbox"] }
|
||||
it.right = { controllerComponentManager.componentList["no_button"] }
|
||||
}
|
||||
controllerComponentManager.componentList["no_button"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = noButton
|
||||
it.up = { controllerComponentManager.componentList["checkbox"] }
|
||||
it.left = { controllerComponentManager.componentList["yes_button"] }
|
||||
}
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["yes_button"]
|
||||
}
|
||||
}
|
||||
|
||||
override fun repositionElements() {
|
||||
|
||||
+130
-59
@@ -25,9 +25,10 @@ import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.gui.VariableObjectSelectionScrollbar
|
||||
import com.cobblemon.mod.common.client.gui.startselection.widgets.CategoryList
|
||||
import com.cobblemon.mod.common.client.gui.startselection.widgets.SelectionButton
|
||||
import com.cobblemon.mod.common.client.gui.startselection.widgets.preview.StarterRoundabout
|
||||
import com.cobblemon.mod.common.client.gui.summary.Summary.Companion.PARTY
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.ModelWidget
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerScreen
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.SummaryBinding
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.config.starter.RenderableStarterCategory
|
||||
@@ -41,8 +42,9 @@ import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.toasts.Toast
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import org.joml.Vector4i
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
/**
|
||||
@@ -51,7 +53,7 @@ import java.io.FileNotFoundException
|
||||
* @author Qu
|
||||
* @since 2022-06-18
|
||||
*/
|
||||
class StarterSelectionScreen(private val categories: List<RenderableStarterCategory>): Screen("cobblemon.ui.starter.title".asTranslated()), CobblemonRenderable {
|
||||
class StarterSelectionScreen(private val categories: List<RenderableStarterCategory>): Screen("cobblemon.ui.starter.title".asTranslated()), CobblemonRenderable, ControllerScreen {
|
||||
companion object {
|
||||
// Size of UI at scale 1
|
||||
private const val BASE_WIDTH = 239
|
||||
@@ -66,14 +68,20 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
private val platformShadow = cobblemonResource("textures/gui/pokedex/platform_shadow.png")
|
||||
private val typeSpacerResource = cobblemonResource("textures/gui/starterselection/type_spacer_single.png")
|
||||
private val ballResource = cobblemonResource("textures/item/poke_balls/poke_ball.png")
|
||||
|
||||
private val unknownResource = cobblemonResource("textures/gui/pokedex/platform_unknown.png")
|
||||
}
|
||||
|
||||
var currentSelection = 0
|
||||
lateinit var currentCategory: RenderableStarterCategory
|
||||
private lateinit var modelWidget: ModelWidget
|
||||
private var currentPokemon: RenderablePokemon? = null
|
||||
private lateinit var currentPokemon: RenderablePokemon
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(this)
|
||||
var lastSelectedCategory = 0
|
||||
var lastSelectedButton = 0
|
||||
|
||||
override fun getComponentManager(): ControllerComponentManager {
|
||||
return controllerComponentManager
|
||||
}
|
||||
|
||||
var ticksElapsed = 0
|
||||
var currentBallBackgroundFrame = 0
|
||||
@@ -100,9 +108,9 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
}
|
||||
|
||||
currentCategory = categories.first()
|
||||
currentPokemon = currentCategory.pokemon[0]
|
||||
currentPokemon = currentCategory.pokemon[currentSelection]
|
||||
|
||||
with(currentPokemon!!) {
|
||||
with(currentPokemon) {
|
||||
modelWidget = ModelWidget(
|
||||
pX = x + 6, pY = y + 17,
|
||||
pWidth = 118, pHeight = 100,
|
||||
@@ -153,6 +161,91 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
width = 3,
|
||||
height = 145
|
||||
))
|
||||
|
||||
|
||||
controllerComponentManager.componentList["select"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = selectionButton
|
||||
it.right = { controllerComponentManager.componentList["pokemon_${lastSelectedCategory}_${lastSelectedButton}"] }
|
||||
}
|
||||
categoryList.generatedCategoriesWidgets.forEachIndexed { categoryIndex, category ->
|
||||
for (buttonIndex in 0..<category.category.pokemon.size) {
|
||||
val column = buttonIndex % 3
|
||||
controllerComponentManager.componentList["pokemon_${categoryIndex}_${buttonIndex}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = category
|
||||
it.right = {
|
||||
if (column < 2) controllerComponentManager.componentList["pokemon_${categoryIndex}_${buttonIndex+1}"]
|
||||
else null
|
||||
}
|
||||
it.left = {
|
||||
if (column > 0) controllerComponentManager.componentList["pokemon_${categoryIndex}_${buttonIndex-1}"]
|
||||
else controllerComponentManager.componentList["select"]
|
||||
}
|
||||
it.up = {
|
||||
if (buttonIndex-3 < 0) {
|
||||
var returnVal: ControllerComponent? = null
|
||||
var index = categoryIndex
|
||||
while (index-1 >= 0) {
|
||||
val lastCategory = categoryList.generatedCategoriesWidgets[index-1]
|
||||
val row = (lastCategory.category.pokemon.size - 1) / 3 + if ((lastCategory.category.pokemon.size - 1) % 3 < column) -1 else 0
|
||||
if (row >= 0) {
|
||||
returnVal = controllerComponentManager.componentList["pokemon_${index-1}_${row*3+column}"]
|
||||
break
|
||||
}
|
||||
index -= 1
|
||||
}
|
||||
returnVal
|
||||
}
|
||||
else controllerComponentManager.componentList["pokemon_${categoryIndex}_${buttonIndex-3}"]
|
||||
}
|
||||
it.down = {
|
||||
if (buttonIndex+3 >= category.category.pokemon.size) {
|
||||
var returnVal: ControllerComponent? = null
|
||||
var index = categoryIndex
|
||||
while (index+1 < categoryList.generatedCategoriesWidgets.size) {
|
||||
val nextCategory = categoryList.generatedCategoriesWidgets[index+1]
|
||||
if (nextCategory.category.pokemon.size > column) {
|
||||
returnVal = controllerComponentManager.componentList["pokemon_${index+1}_${column}"]
|
||||
break
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
returnVal
|
||||
}
|
||||
else controllerComponentManager.componentList["pokemon_${categoryIndex}_${buttonIndex+3}"]
|
||||
}
|
||||
it.onPress = {
|
||||
selectPokemon(category.category, buttonIndex)
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
}
|
||||
it.onSelect = {
|
||||
lastSelectedCategory = categoryIndex
|
||||
lastSelectedButton = buttonIndex
|
||||
}
|
||||
it.onFocus = {
|
||||
category.controllerButtonSelected = buttonIndex
|
||||
categoryList.ensureVisible(category)
|
||||
}
|
||||
it.onUnfocus = {
|
||||
category.controllerButtonSelected = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
controllerComponentManager.let {
|
||||
it.focused = controllerComponentManager.componentList["select"]
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSelectionPosAndSize(): Vector4i? {
|
||||
if (controllerComponentManager.currentSelectedComponent.startsWith("pokemon_")) {
|
||||
val category = controllerComponentManager.focused?.associatedGUIElement as? CategoryList.Category ?: return null
|
||||
val row = category.controllerButtonSelected / 3
|
||||
val column = category.controllerButtonSelected % 3
|
||||
val x = category.getRowLeft() + (CategoryList.CONTAINER_BUTTON_SIZE + 2) * column + CategoryList.MARGIN + 3
|
||||
val y = category.getRowTop() + (CategoryList.CONTAINER_BUTTON_SIZE + CategoryList.BUTTON_SEPERATION) * row + CategoryList.HEADER_HEIGHT + CategoryList.BUTTON_SEPERATION - 1
|
||||
return Vector4i(x, y, CategoryList.CONTAINER_BUTTON_SIZE + 4, CategoryList.CONTAINER_BUTTON_SIZE + 4)
|
||||
}
|
||||
return super.getSelectionPosAndSize()
|
||||
}
|
||||
|
||||
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
@@ -190,7 +283,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
width = 113,
|
||||
height = 32
|
||||
)
|
||||
val typePlatform = currentPokemon?.let { getPlatformResource(it.form.primaryType) }
|
||||
val typePlatform = getPlatformResource(currentPokemon.form.primaryType)
|
||||
if (typePlatform != null) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
@@ -211,18 +304,6 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
scale = 0.5F
|
||||
)
|
||||
|
||||
if (currentPokemon == null) { // Model widget won't render
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = unknownResource,
|
||||
x = (x + 36) / 0.15f,
|
||||
y = (y + 36) / 0.15f,
|
||||
width = 390,
|
||||
height = 444,
|
||||
scale = 0.15f
|
||||
)
|
||||
}
|
||||
|
||||
//Base
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
@@ -234,25 +315,23 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
)
|
||||
|
||||
//Type Icon
|
||||
if (currentPokemon != null) {
|
||||
TypeIcon(
|
||||
x = x + 65,
|
||||
y = y + 120,
|
||||
type = currentPokemon!!.form.primaryType,
|
||||
secondaryType = currentPokemon!!.form.secondaryType,
|
||||
centeredX = true
|
||||
).render(context)
|
||||
TypeIcon(
|
||||
x = x + 65,
|
||||
y = y + 120,
|
||||
type = currentPokemon.form.primaryType,
|
||||
secondaryType = currentPokemon.form.secondaryType,
|
||||
centeredX = true
|
||||
).render(context)
|
||||
|
||||
if (currentPokemon!!.form.secondaryType == null) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = typeSpacerResource,
|
||||
x = x + 47,
|
||||
y = y + 123,
|
||||
width = 36,
|
||||
height = 12
|
||||
)
|
||||
}
|
||||
if (currentPokemon.form.secondaryType == null) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = typeSpacerResource,
|
||||
x = x + 47,
|
||||
y = y + 123,
|
||||
width = 36,
|
||||
height = 12
|
||||
)
|
||||
}
|
||||
|
||||
//Name
|
||||
@@ -269,7 +348,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = currentPokemon?.species?.translatedName?.bold() ?: lang("ui.starter.random").bold(),
|
||||
text = currentPokemon.species.translatedName.bold(),
|
||||
x = x + 14,
|
||||
y = y + 2,
|
||||
shadow = true
|
||||
@@ -277,13 +356,11 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
|
||||
// Pokédex Number
|
||||
// Add preceding zeroes if Pokédex number is less than 4 digits
|
||||
var dexNo = currentPokemon?.species?.nationalPokedexNumber?.toString()
|
||||
if (dexNo != null) {
|
||||
while (dexNo!!.length < 4) {
|
||||
dexNo = "0$dexNo"
|
||||
}
|
||||
dexNo = "#$dexNo"
|
||||
} else dexNo = "#????"
|
||||
var dexNo = currentPokemon.species.nationalPokedexNumber.toString()
|
||||
while (dexNo.length < 4) {
|
||||
dexNo = "0$dexNo"
|
||||
}
|
||||
dexNo = "#$dexNo"
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
@@ -308,12 +385,10 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
//Description Text
|
||||
val smallTextScale = 0.5F
|
||||
|
||||
val textEntry = currentPokemon?.form?.pokedex?.first()?.asTranslated() ?: lang("ui.starter.random_description")
|
||||
|
||||
matrices.pushPose()
|
||||
matrices.scale(smallTextScale, smallTextScale, 1F)
|
||||
MultiLineLabelK.create(
|
||||
component = textEntry,
|
||||
component = currentPokemon.form.pokedex.first().asTranslated(),
|
||||
width = 114 / smallTextScale,
|
||||
maxLines = 5
|
||||
).renderLeftAligned(
|
||||
@@ -327,6 +402,8 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
matrices.popPose()
|
||||
|
||||
super.render(context, mouseX, mouseY, delta)
|
||||
|
||||
renderSelection(matrices)
|
||||
}
|
||||
|
||||
fun getPlatformResource(type: ElementalType): ResourceLocation? {
|
||||
@@ -340,14 +417,8 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
fun selectPokemon(category: RenderableStarterCategory, slot: Int) {
|
||||
currentCategory = category
|
||||
currentSelection = slot
|
||||
if (currentSelection < currentCategory.pokemon.size) {
|
||||
currentPokemon = currentCategory.pokemon[currentSelection].also {
|
||||
modelWidget.pokemon = it
|
||||
}
|
||||
modelWidget.shouldRender = true
|
||||
} else {
|
||||
currentPokemon = null
|
||||
modelWidget.shouldRender = false
|
||||
currentPokemon = currentCategory.pokemon[currentSelection].also {
|
||||
modelWidget.pokemon = it
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+33
-35
@@ -52,8 +52,6 @@ class CategoryList(
|
||||
private val buttonResource = cobblemonResource("textures/gui/starterselection/starter_selection.png")
|
||||
private val buttonActiveResource = cobblemonResource("textures/gui/starterselection/starter_selection_active.png")
|
||||
|
||||
private val unknownResource = cobblemonResource("textures/gui/pokedex/platform_unknown.png")
|
||||
|
||||
const val MARGIN = 4
|
||||
const val HEADER_HEIGHT = 7
|
||||
const val BUTTON_SEPERATION = 3
|
||||
@@ -61,9 +59,12 @@ class CategoryList(
|
||||
const val CONTAINER_WIDTH = 89
|
||||
}
|
||||
|
||||
val generatedCategoriesWidgets = mutableListOf<Category>()
|
||||
|
||||
init {
|
||||
addEntry(EmptySeparator(MARGIN))
|
||||
createEntries().forEach {
|
||||
generatedCategoriesWidgets.add(it)
|
||||
addEntry(it)
|
||||
addEntry(EmptySeparator(MARGIN))
|
||||
}
|
||||
@@ -75,7 +76,18 @@ class CategoryList(
|
||||
Category(category, height)
|
||||
}
|
||||
|
||||
inner class Category(private val category: RenderableStarterCategory, height: Int) : Entry(height) {
|
||||
override fun ensureVisible(entry: Entry) {
|
||||
if (entry !in children) return
|
||||
val yTop = entry.scrolledY - scrollAmount.toInt()
|
||||
if (yTop - MARGIN < 0) {
|
||||
setScroll((entry.scrolledY - MARGIN).toDouble())
|
||||
} else if (yTop + entry.height + MARGIN > height) {
|
||||
setScroll((entry.scrolledY + entry.height - height + MARGIN).toDouble())
|
||||
}
|
||||
}
|
||||
|
||||
inner class Category(val category: RenderableStarterCategory, height: Int) : Entry(height) {
|
||||
var controllerButtonSelected = -1
|
||||
|
||||
override fun render(
|
||||
guiGraphics: GuiGraphics,
|
||||
@@ -86,8 +98,7 @@ class CategoryList(
|
||||
mouseY: Int,
|
||||
partialTick: Float
|
||||
) {
|
||||
val itemSize = category.pokemon.size + if (category.randomStarter) 1 else 0
|
||||
val rows = (itemSize - 1) / 3 + 1
|
||||
val rows = (category.pokemon.size - 1) / 3 + 1
|
||||
val matrices = guiGraphics.pose()
|
||||
//Header
|
||||
blitk(
|
||||
@@ -155,7 +166,7 @@ class CategoryList(
|
||||
)
|
||||
|
||||
//Buttons
|
||||
for (index in 0..<itemSize) {
|
||||
for (index in 0..<category.pokemon.size) {
|
||||
val row = index / 3
|
||||
val column = index % 3
|
||||
|
||||
@@ -173,36 +184,24 @@ class CategoryList(
|
||||
width = CONTAINER_BUTTON_SIZE,
|
||||
height = CONTAINER_BUTTON_SIZE,
|
||||
textureHeight = CONTAINER_BUTTON_SIZE * 2,
|
||||
vOffset = if (hovered) CONTAINER_BUTTON_SIZE else 0
|
||||
vOffset = if (hovered || controllerButtonSelected == index) CONTAINER_BUTTON_SIZE else 0
|
||||
)
|
||||
|
||||
if (index == category.pokemon.size) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = unknownResource,
|
||||
x = (startX + 4.5F) / 0.04f,
|
||||
y = (startY + 3.5F) / 0.04f,
|
||||
width = 390,
|
||||
height = 444,
|
||||
scale = 0.04f
|
||||
)
|
||||
} else {
|
||||
matrices.pushPose()
|
||||
matrices.translate(startX + (CONTAINER_BUTTON_SIZE / 2.0), startY.toDouble() - 6, 0.0)
|
||||
matrices.scale(3.5F, 3.5F, 1F)
|
||||
matrices.pushPose()
|
||||
matrices.translate(startX + (CONTAINER_BUTTON_SIZE / 2.0), startY.toDouble() - 6, 0.0)
|
||||
matrices.scale(3.5F, 3.5F, 1F)
|
||||
|
||||
val state = FloatingState()
|
||||
state.currentAspects = category.pokemon[index].aspects
|
||||
drawProfilePokemon(
|
||||
species = category.pokemon[index].species.resourceIdentifier,
|
||||
matrixStack = matrices,
|
||||
rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(13F, 35F, 0F)),
|
||||
state = state,
|
||||
scale = 4.5F,
|
||||
partialTicks = 0F
|
||||
)
|
||||
matrices.popPose()
|
||||
}
|
||||
val state = FloatingState()
|
||||
state.currentAspects = category.pokemon[index].aspects
|
||||
drawProfilePokemon(
|
||||
species = category.pokemon[index].species.resourceIdentifier,
|
||||
matrixStack = matrices,
|
||||
rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(13F, 35F, 0F)),
|
||||
state = state,
|
||||
scale = 4.5F,
|
||||
partialTicks = 0F
|
||||
)
|
||||
matrices.popPose()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,8 +217,7 @@ class CategoryList(
|
||||
}
|
||||
|
||||
private fun getButtonHovered(x: Int, y: Int, mouseX: Double, mouseY: Double): Int {
|
||||
val itemSize = category.pokemon.size + if (category.randomStarter) 1 else 0
|
||||
for (index in 0..<itemSize) {
|
||||
for (index in 0..<category.pokemon.size) {
|
||||
val row = index / 3
|
||||
val column = index % 3
|
||||
|
||||
|
||||
+1
-5
@@ -8,8 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.startselection.widgets
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.ColourLibrary
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
@@ -17,10 +15,8 @@ import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.client.sounds.SoundManager
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
@@ -50,7 +46,7 @@ class SelectionButton(
|
||||
width = BUTTON_WIDTH,
|
||||
height = BUTTON_HEIGHT,
|
||||
textureHeight = BUTTON_HEIGHT * 2,
|
||||
vOffset = if (isHovered) BUTTON_HEIGHT else 0
|
||||
vOffset = if (isHovered || isFocused) BUTTON_HEIGHT else 0
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
|
||||
@@ -20,14 +20,27 @@ import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.ExitButton
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.EvolutionSelectScreen
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.MarkingsWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.ModelWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.NicknameEntryWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.PartySlotWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.PartyWidget
|
||||
import com.cobblemon.mod.common.client.gui.*
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.*
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.SummaryTab
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.info.InfoWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.marks.MarksScrollingWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.marks.MarksWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MoveSwapScreen
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MovesWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.StatWidget
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerScreen
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.SubManagerComponent
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.SummaryBinding
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
|
||||
@@ -50,6 +63,8 @@ import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import org.joml.Vector2i
|
||||
import org.joml.Vector4i
|
||||
|
||||
/**
|
||||
* The screen responsible for displaying various information regarding a Pokémon team.
|
||||
@@ -60,7 +75,7 @@ import net.minecraft.sounds.SoundEvent
|
||||
* @param selection The index the [party] will have as the base [selectedPokemon].
|
||||
*/
|
||||
class Summary private constructor(party: Collection<Pokemon?>, private val editable: Boolean, private val selection: Int): Screen(
|
||||
Component.translatable("cobblemon.ui.summary.title")), Schedulable, CobblemonRenderable {
|
||||
Component.translatable("cobblemon.ui.summary.title")), Schedulable, CobblemonRenderable, ControllerScreen {
|
||||
|
||||
companion object {
|
||||
const val BASE_WIDTH = 331
|
||||
@@ -69,10 +84,10 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
private const val SCALE = 0.5F
|
||||
|
||||
// Main Screen Index
|
||||
private const val INFO = 0
|
||||
private const val MOVES = 1
|
||||
private const val STATS = 2
|
||||
private const val MARKS = 3
|
||||
const val INFO = 0
|
||||
const val MOVES = 1
|
||||
const val STATS = 2
|
||||
const val MARKS = 3
|
||||
|
||||
// Side Screen Index
|
||||
const val PARTY = 0
|
||||
@@ -116,19 +131,32 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
|
||||
override val schedulingTracker = SchedulingTracker()
|
||||
|
||||
private val party = ArrayList(party)
|
||||
val party = ArrayList(party)
|
||||
internal var selectedPokemon: Pokemon = this.party[selection] ?: this.party.first { it != null }!!
|
||||
private lateinit var mainScreen: AbstractWidget
|
||||
lateinit var mainScreen: AbstractWidget
|
||||
lateinit var sideScreen: GuiEventListener
|
||||
private lateinit var modelWidget: ModelWidget
|
||||
private lateinit var nicknameEntryWidget: NicknameEntryWidget
|
||||
lateinit var nicknameEntryWidget: NicknameEntryWidget
|
||||
private lateinit var markingsWidget: MarkingsWidget
|
||||
private val summaryTabs = mutableListOf<SummaryTab>()
|
||||
val summaryTabs = mutableListOf<SummaryTab>()
|
||||
private var showCosmeticItem = false
|
||||
private lateinit var heldItemVisibilityButton: SummaryButton
|
||||
private var mainScreenIndex = INFO
|
||||
lateinit var heldItemVisibilityButton: SummaryButton
|
||||
lateinit var heldItemSwitchTypeButton: SummaryButton
|
||||
lateinit var evolveButton: SummaryButton
|
||||
var mainScreenIndex = INFO
|
||||
var sideScreenIndex = PARTY
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(this)
|
||||
var mainScreenComponentManager: ControllerComponentManager? = null
|
||||
var sideScreenComponentManager: ControllerComponentManager? = null
|
||||
var selectedMoveIndex = 0
|
||||
var moveSwitchingMode = false
|
||||
var lastSelectedPartySlot = 0
|
||||
|
||||
override fun getComponentManager(): ControllerComponentManager {
|
||||
return controllerComponentManager
|
||||
}
|
||||
|
||||
override fun renderBlurredBackground(delta: Float) { }
|
||||
|
||||
/**
|
||||
@@ -158,7 +186,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
addRenderableWidget(markingsWidget)
|
||||
|
||||
// Evolve Button
|
||||
addRenderableWidget(
|
||||
evolveButton = addRenderableWidget(
|
||||
SummaryButton(
|
||||
buttonX = x + 12F,
|
||||
buttonY = y + 145F,
|
||||
@@ -204,22 +232,22 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
heldItemVisibilityButton.buttonActive = !selectedPokemon.heldItemVisible
|
||||
addRenderableWidget(heldItemVisibilityButton)
|
||||
|
||||
|
||||
// Held/Cosmetic Item Button
|
||||
addRenderableWidget(
|
||||
SummaryButton(
|
||||
buttonX = x + 67F,
|
||||
buttonY = y + 113F,
|
||||
buttonWidth = 12,
|
||||
buttonHeight = 12,
|
||||
scale = 0.5F,
|
||||
resource = iconCosmeticItemResource,
|
||||
activeResource = iconHeldItemResource,
|
||||
clickAction = {
|
||||
showCosmeticItem = !showCosmeticItem
|
||||
(it as SummaryButton).buttonActive = showCosmeticItem
|
||||
}
|
||||
)
|
||||
heldItemSwitchTypeButton = SummaryButton(
|
||||
buttonX = x + 67F,
|
||||
buttonY = y + 113F,
|
||||
buttonWidth = 12,
|
||||
buttonHeight = 12,
|
||||
scale = 0.5F,
|
||||
resource = iconCosmeticItemResource,
|
||||
activeResource = iconHeldItemResource,
|
||||
clickAction = {
|
||||
showCosmeticItem = !showCosmeticItem
|
||||
(it as SummaryButton).buttonActive = showCosmeticItem
|
||||
}
|
||||
)
|
||||
addRenderableWidget(heldItemSwitchTypeButton)
|
||||
|
||||
// Init Tabs
|
||||
summaryTabs.clear()
|
||||
@@ -253,14 +281,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
// Add Exit Button
|
||||
addRenderableWidget(
|
||||
ExitButton(pX = x + 302, pY = y + 145) {
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
if (sideScreenIndex != PARTY) {
|
||||
displaySideScreen(PARTY)
|
||||
} else {
|
||||
saveActiveMark()
|
||||
saveMarkings()
|
||||
Minecraft.getInstance().setScreen(null)
|
||||
}
|
||||
onBack()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -294,9 +315,214 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
addRenderableWidget(this.modelWidget)
|
||||
|
||||
updatePokemonInfo()
|
||||
|
||||
//Controller Support
|
||||
controllerComponentManager.componentList["nickname"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = nicknameEntryWidget
|
||||
it.right = { controllerComponentManager.componentList["center_screen"] }
|
||||
}
|
||||
controllerComponentManager.componentList["held_item"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = heldItemVisibilityButton
|
||||
it.right = { controllerComponentManager.componentList["center_screen"] }
|
||||
it.pairUp(controllerComponentManager.componentList["nickname"]!!)
|
||||
it.down = { if (evolveButton.renderRequirement.invoke(evolveButton)) controllerComponentManager.componentList["evolve"] else null }
|
||||
it.onSpecial1 = {
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
heldItemSwitchTypeButton.onPress()
|
||||
}
|
||||
it.onPress = {
|
||||
if (heldItemVisibilityButton.clickRequirement.invoke(heldItemVisibilityButton)) heldItemVisibilityButton.onPress()
|
||||
}
|
||||
}
|
||||
controllerComponentManager.componentList["evolve"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = evolveButton
|
||||
it.right = { controllerComponentManager.componentList["center_screen"] }
|
||||
it.up = { controllerComponentManager.componentList["held_item"] }
|
||||
}
|
||||
controllerComponentManager.componentList["center_screen"] = SubManagerComponent().also {
|
||||
it.subManager = { mainScreenComponentManager }
|
||||
it.left = {
|
||||
var returnVal: ControllerComponent? = null
|
||||
if (mainScreenComponentManager?.left() ?: true && !moveSwitchingMode) {
|
||||
returnVal = controllerComponentManager.componentList["nickname"]
|
||||
}
|
||||
returnVal
|
||||
}
|
||||
it.right = {
|
||||
var returnVal: ControllerComponent? = null
|
||||
if (mainScreenComponentManager?.right() ?: true && !moveSwitchingMode) {
|
||||
returnVal = controllerComponentManager.componentList["right_screen"]
|
||||
}
|
||||
returnVal
|
||||
}
|
||||
it.up = {
|
||||
if (mainScreenIndex == STATS) {
|
||||
val statWidget = mainScreen as StatWidget
|
||||
var index = statWidget.statTabIndex + 1
|
||||
if (index > statWidget.statOptions.size - 1) {
|
||||
index = 0
|
||||
}
|
||||
statWidget.statTabIndex = index
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
} else {
|
||||
mainScreenComponentManager?.up()
|
||||
}
|
||||
null
|
||||
}
|
||||
it.down = {
|
||||
if (mainScreenIndex == STATS) {
|
||||
val statWidget = mainScreen as StatWidget
|
||||
var index = statWidget.statTabIndex - 1
|
||||
if (index < 0) {
|
||||
index = statWidget.statOptions.size - 1
|
||||
}
|
||||
statWidget.statTabIndex = index
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
} else {
|
||||
mainScreenComponentManager?.down()
|
||||
}
|
||||
null
|
||||
}
|
||||
it.onPress = {
|
||||
if (mainScreenIndex == STATS) {
|
||||
val statWidget = mainScreen as StatWidget
|
||||
if (statWidget.statOptions.get(statWidget.statTabIndex) == StatWidget.RIDE && (statWidget.pokemon.riding.behaviours?.size ?: 0) > 1) {
|
||||
statWidget.rideBehaviourIndex = (statWidget.rideBehaviourIndex + 1) % (statWidget.pokemon.riding.behaviours?.size ?: 1)
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
}
|
||||
} else {
|
||||
mainScreenComponentManager?.press()
|
||||
}
|
||||
}
|
||||
}
|
||||
controllerComponentManager.componentList["right_screen"] = SubManagerComponent().also {
|
||||
it.subManager = { sideScreenComponentManager }
|
||||
it.left = {
|
||||
var returnVal: ControllerComponent? = null
|
||||
if (sideScreenComponentManager?.left() ?: true && sideScreenIndex == PARTY && (sideScreen as? PartyWidget)?.swapSource == null) {
|
||||
returnVal = controllerComponentManager.componentList["center_screen"]
|
||||
}
|
||||
returnVal
|
||||
}
|
||||
}
|
||||
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["center_screen"]
|
||||
it.onTabNav = { nextTab ->
|
||||
var index = mainScreenIndex + if (nextTab) 1 else -1
|
||||
if (index < 0) index = summaryTabs.size - 1
|
||||
if (index > summaryTabs.size - 1) index = 0
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
displayMainScreen(index)
|
||||
}
|
||||
it.onSecondaryTabNav = { nextTab ->
|
||||
val pokemonIndex = party.indexOf(selectedPokemon)
|
||||
val indexList = party.filter { it != null }.map { party.indexOf(it) }.toMutableList()
|
||||
var index = indexList.indexOf(pokemonIndex) + if (nextTab) 1 else -1
|
||||
if (index < 0) index = indexList.size - 1
|
||||
if (index > indexList.size - 1) index = 0
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
displaySideScreen(PARTY)
|
||||
switchSelection(indexList[index])
|
||||
}
|
||||
it.onBack = {
|
||||
if (moveSwitchingMode) {
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
moveSwitchingMode = false
|
||||
val movesWidget = mainScreen as? MovesWidget
|
||||
if (movesWidget != null) {
|
||||
movesWidget.controllerComponentManager.focused?.onUnselect()
|
||||
}
|
||||
} else {
|
||||
val partyWidget = sideScreen as? PartyWidget
|
||||
if (partyWidget != null && partyWidget.swapSource != null) {
|
||||
partyWidget.draggedSlot = null
|
||||
partyWidget.controllerComponentManager.let {
|
||||
it.focused = it.componentList["slot_${partyWidget.swapSource}"]
|
||||
}
|
||||
partyWidget.swapSource = null
|
||||
playSound(CobblemonSounds.PC_DROP)
|
||||
} else {
|
||||
onBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
it.onActiveChange = { state ->
|
||||
if (!state) {
|
||||
moveSwitchingMode = false
|
||||
}
|
||||
if (mainScreenComponentManager != null) {
|
||||
if (!state) {
|
||||
mainScreenComponentManager?.active = false
|
||||
} else {
|
||||
if (it.currentSelectedComponent == "center_screen") mainScreenComponentManager?.active = true
|
||||
}
|
||||
}
|
||||
if (sideScreenComponentManager != null) {
|
||||
if (!state) {
|
||||
sideScreenComponentManager?.active = false
|
||||
} else {
|
||||
if (it.currentSelectedComponent == "right_screen") sideScreenComponentManager?.active = true
|
||||
}
|
||||
}
|
||||
}
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SECONDARY_TAB,
|
||||
{ true },
|
||||
Component.translatable("cobblemon.controller.summary.quick_switch"),
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SPECIAL_1,
|
||||
{ it.currentSelectedComponent == "center_screen" && mainScreenIndex == MOVES },
|
||||
Component.translatable("cobblemon.controller.summary.change_move"),
|
||||
true
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SPECIAL_1,
|
||||
{ it.currentSelectedComponent == "held_item" },
|
||||
Component.translatable("cobblemon.controller.toggle_held_item_type"),
|
||||
true
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SPECIAL_2,
|
||||
{ it.currentSelectedComponent == "center_screen" && mainScreenIndex == MOVES },
|
||||
Component.translatable("cobblemon.controller.summary.adjust_move"),
|
||||
true
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SPECIAL_2,
|
||||
{ it.currentSelectedComponent == "center_screen" && mainScreenIndex == MARKS },
|
||||
Component.translatable("cobblemon.controller.summary.clear_mark"),
|
||||
true
|
||||
))
|
||||
it.buttonGuides.add(ControllerComponentManager.ButtonGuide(
|
||||
ControllerComponentManager.ButtonGuideType.SPECIAL_2,
|
||||
{ it.currentSelectedComponent == "right_screen" && sideScreenIndex == PARTY },
|
||||
Component.translatable("cobblemon.controller.summary.swap_position"),
|
||||
true
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fun onBack() {
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
if (sideScreenIndex != PARTY) {
|
||||
if (sideScreenIndex == EVOLVE) {
|
||||
controllerComponentManager.focused = controllerComponentManager.componentList["evolve"]
|
||||
} else {
|
||||
controllerComponentManager.focused = controllerComponentManager.componentList["center_screen"]
|
||||
}
|
||||
displaySideScreen(PARTY)
|
||||
} else {
|
||||
saveActiveMark()
|
||||
saveMarkings()
|
||||
Minecraft.getInstance().setScreen(null)
|
||||
}
|
||||
}
|
||||
|
||||
fun swapPartySlot(sourceIndex: Int, targetIndex: Int) {
|
||||
moveSwitchingMode = false
|
||||
|
||||
if (sourceIndex >= this.party.size || targetIndex >= this.party.size) {
|
||||
return
|
||||
}
|
||||
@@ -357,11 +583,11 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveMarkings() {
|
||||
fun saveMarkings() {
|
||||
if (::markingsWidget.isInitialized) markingsWidget.saveMarkingsToPokemon()
|
||||
}
|
||||
|
||||
private fun saveActiveMark() {
|
||||
fun saveActiveMark() {
|
||||
if (mainScreenIndex == MARKS && mainScreen is MarksWidget) (mainScreen as MarksWidget).saveActiveMarkToPokemon()
|
||||
}
|
||||
|
||||
@@ -373,12 +599,16 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
/**
|
||||
* Switch center screen
|
||||
*/
|
||||
private fun displayMainScreen(screen: Int) {
|
||||
fun displayMainScreen(screen: Int) {
|
||||
// Save mark to Pokémon if any changes when switching out of marks screen
|
||||
saveActiveMark()
|
||||
|
||||
// Get stat tab index if currently displaying stat screen
|
||||
val subIndex = if (mainScreenIndex == STATS && mainScreen is StatWidget) (mainScreen as StatWidget).statTabIndex else 0
|
||||
val subIndex = if (mainScreenIndex == STATS && mainScreen is StatWidget) (mainScreen as StatWidget).rawStatTabIndex else 0
|
||||
|
||||
if (screen != MOVES) {
|
||||
moveSwitchingMode = false
|
||||
}
|
||||
|
||||
mainScreenIndex = screen
|
||||
if (::mainScreen.isInitialized) removeWidget(mainScreen)
|
||||
@@ -398,6 +628,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
pY = y + 12,
|
||||
pokemon = this.selectedPokemon
|
||||
)
|
||||
mainScreenComponentManager = null
|
||||
}
|
||||
|
||||
MOVES -> {
|
||||
@@ -405,7 +636,13 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
pX = x + 77,
|
||||
pY = y + 12,
|
||||
summary = this
|
||||
)
|
||||
).also { movesWidget ->
|
||||
mainScreenComponentManager = movesWidget.controllerComponentManager
|
||||
movesWidget.controllerComponentManager.let {
|
||||
it.focused = it.componentList["move_${selectedMoveIndex}"]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
STATS -> {
|
||||
@@ -413,25 +650,38 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
pX = x + 77,
|
||||
pY = y + 12,
|
||||
pokemon = this.selectedPokemon,
|
||||
tabIndex = subIndex
|
||||
tabIndex = subIndex,
|
||||
summary = this
|
||||
)
|
||||
mainScreenComponentManager = null
|
||||
}
|
||||
|
||||
MARKS -> {
|
||||
mainScreen = MarksWidget(
|
||||
pX = x + 77,
|
||||
pY = y + 12,
|
||||
pokemon = this.selectedPokemon
|
||||
)
|
||||
pokemon = this.selectedPokemon,
|
||||
summary = this
|
||||
).also {
|
||||
mainScreenComponentManager = it.controllerComponentManager
|
||||
}
|
||||
}
|
||||
}
|
||||
addRenderableWidget(mainScreen)
|
||||
|
||||
if (controllerComponentManager.currentSelectedComponent == "center_screen" && controllerComponentManager.active) {
|
||||
focused = null
|
||||
mainScreenComponentManager?.active = true
|
||||
controllerComponentManager.focused?.onFocus?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch right screen
|
||||
*/
|
||||
fun displaySideScreen(screen: Int, move: Move? = null) {
|
||||
moveSwitchingMode = false
|
||||
|
||||
sideScreenIndex = screen
|
||||
if (::sideScreen.isInitialized) removeWidget(sideScreen)
|
||||
|
||||
@@ -446,7 +696,9 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
isParty = selectedPokemon in CobblemonClient.storage.party,
|
||||
summary = this,
|
||||
partyList = this.party
|
||||
)
|
||||
).also {
|
||||
sideScreenComponentManager = it.controllerComponentManager
|
||||
}
|
||||
}
|
||||
|
||||
MOVE_SWAP -> {
|
||||
@@ -468,7 +720,9 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
moveSlotList += MoveSwapScreen.MoveSlot(switchPane, null, 0, pokemon)
|
||||
}
|
||||
moveSlotList.forEach { switchPane.addEntry(it) }
|
||||
sideScreenComponentManager = switchPane.controllerComponentManager
|
||||
}
|
||||
controllerComponentManager.focused = controllerComponentManager.componentList["right_screen"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,14 +732,67 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
sideScreen = EvolutionSelectScreen(
|
||||
x + 216,
|
||||
y + 23,
|
||||
pokemon = selectedPokemon
|
||||
)
|
||||
pokemon = selectedPokemon,
|
||||
summary = this
|
||||
).also {
|
||||
sideScreenComponentManager = it.controllerComponentManager
|
||||
}
|
||||
controllerComponentManager.focused = controllerComponentManager.componentList["right_screen"]
|
||||
}
|
||||
}
|
||||
val element = sideScreen
|
||||
if (element is Renderable && element is NarratableEntry) {
|
||||
addRenderableWidget(element)
|
||||
}
|
||||
|
||||
if (controllerComponentManager.currentSelectedComponent == "right_screen" && controllerComponentManager.active) {
|
||||
focused = null
|
||||
sideScreenComponentManager?.active = true
|
||||
controllerComponentManager.focused?.onFocus?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSelectionPosAndSize(): Vector4i? {
|
||||
val data = super.getSelectionPosAndSize()
|
||||
if (data != null) {
|
||||
when (focused) {
|
||||
nicknameEntryWidget -> return Vector4i(nicknameEntryWidget.x - 12, nicknameEntryWidget.y - 4, 78, 17)
|
||||
heldItemVisibilityButton -> return Vector4i(data.x + 1, data.y + 1, data.z / 2, data.w / 2)
|
||||
is PartySlotWidget -> return data.add(-2, -2, 0, 2)
|
||||
else -> return data
|
||||
}
|
||||
} else {
|
||||
when (controllerComponentManager.currentSelectedComponent) {
|
||||
"center_screen" -> {
|
||||
when (mainScreenIndex) {
|
||||
MARKS -> {
|
||||
val marksScreen = mainScreen as MarksWidget
|
||||
val horizontalSpacing = if (marksScreen.marksScrollList.children().size > 5) MarksScrollingWidget.SLOT_SPACING else (MarksScrollingWidget.SLOT_SPACING + 1)
|
||||
val row = marksScreen.lastSelectedMark / 6
|
||||
val column = marksScreen.lastSelectedMark % 6
|
||||
val startX = marksScreen.marksScrollList.rowLeft + (horizontalSpacing + MarksScrollingWidget.SLOT_SIZE) * column
|
||||
val startY = marksScreen.marksScrollList.getRowTop(row) + MarksScrollingWidget.SLOT_SPACING
|
||||
return Vector4i(startX - 2, startY - 2, MarksScrollingWidget.SLOT_SIZE + 4, MarksScrollingWidget.SLOT_SIZE + 4)
|
||||
}
|
||||
else -> return Vector4i(mainScreen.x + 2, mainScreen.y + 2, 130, 144)
|
||||
}
|
||||
}
|
||||
"right_screen" -> {
|
||||
when (sideScreenIndex) {
|
||||
EVOLVE -> {
|
||||
val evolveScreen = sideScreen as EvolutionSelectScreen
|
||||
return Vector4i(evolveScreen.rowLeft - 5, evolveScreen.getRowTop(evolveScreen.children().indexOf(focused)) - 2, evolveScreen.rowWidth + 4, EvolutionSelectScreen.SLOT_HEIGHT + 4)
|
||||
}
|
||||
MOVE_SWAP -> {
|
||||
val moveSwapScreen = sideScreen as MoveSwapScreen
|
||||
return Vector4i(moveSwapScreen.rowLeft - 11, moveSwapScreen.getRowTop(moveSwapScreen.children().indexOf(focused)) - 2, moveSwapScreen.rowWidth + 13, MoveSwapScreen.SLOT_HEIGHT + 4)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
override fun renderMenuBackground(context: GuiGraphics) {}
|
||||
@@ -625,8 +932,11 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
val itemX = x + 3
|
||||
val itemY = y + 104
|
||||
if (!displayedItem.isEmpty) {
|
||||
matrices.pushPose()
|
||||
matrices.translate(0.0,0.0,-200.0)
|
||||
context.renderItem(displayedItem, itemX, itemY)
|
||||
context.renderItemDecorations(Minecraft.getInstance().font, displayedItem, itemX, itemY)
|
||||
matrices.popPose()
|
||||
}
|
||||
|
||||
drawScaledText(
|
||||
@@ -655,19 +965,25 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
matrices.pushPose()
|
||||
// Prevent widgets from being overlapped by other components
|
||||
matrices.translate(0.0, 0.0, 1000.0)
|
||||
// Set Look View Pos
|
||||
val selectionData = getSelectionPosAndSize()
|
||||
if (controllerComponentManager.active && selectionData != null) {
|
||||
modelWidget.overrideLookPos = Vector2i(selectionData.x + selectionData.z / 2, selectionData.y + selectionData.w / 2)
|
||||
} else {
|
||||
modelWidget.overrideLookPos = null
|
||||
}
|
||||
|
||||
// Render all added Widgets
|
||||
super.render(context, mouseX, mouseY, delta)
|
||||
|
||||
// Selection Rendering
|
||||
renderSelection(matrices)
|
||||
|
||||
// Render Item Tooltip
|
||||
if (!displayedItem.isEmpty) {
|
||||
val itemHovered = mouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && mouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
|
||||
if (itemHovered) context.renderTooltip(Minecraft.getInstance().font, displayedItem, mouseX, mouseY)
|
||||
}
|
||||
matrices.popPose()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,8 +32,8 @@ class SummaryButton(
|
||||
private val text: MutableComponent = Component.empty(),
|
||||
private val resource: ResourceLocation,
|
||||
private val activeResource: ResourceLocation? = null,
|
||||
private val renderRequirement: ((button: SummaryButton) -> Boolean) = { true },
|
||||
private val clickRequirement: ((button: SummaryButton) -> Boolean) = { true },
|
||||
val renderRequirement: ((button: SummaryButton) -> Boolean) = { true },
|
||||
val clickRequirement: ((button: SummaryButton) -> Boolean) = { true },
|
||||
private val hoverTexture: Boolean = true,
|
||||
private val silent: Boolean = false,
|
||||
private val boldText: Boolean = true,
|
||||
@@ -67,7 +67,7 @@ class SummaryButton(
|
||||
y = buttonY / scale,
|
||||
width = buttonWidth,
|
||||
height = buttonHeight,
|
||||
vOffset = if (hoverTexture && isButtonHovered(pMouseX, pMouseY)) buttonHeight else 0,
|
||||
vOffset = if (hoverTexture && (isButtonHovered(pMouseX, pMouseY) || isFocused)) buttonHeight else 0,
|
||||
textureHeight = if (hoverTexture) (buttonHeight.toFloat() * 2) else buttonHeight,
|
||||
scale = scale
|
||||
)
|
||||
|
||||
+34
-2
@@ -15,8 +15,11 @@ import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.gui.summary.Summary
|
||||
import com.cobblemon.mod.common.client.gui.summary.SummaryButton
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.common.SummaryScrollList
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
@@ -34,7 +37,8 @@ import org.joml.Vector3f
|
||||
class EvolutionSelectScreen(
|
||||
x: Int,
|
||||
y: Int,
|
||||
val pokemon: Pokemon
|
||||
val pokemon: Pokemon,
|
||||
val summary: Summary
|
||||
): SummaryScrollList<EvolutionSelectScreen.EvolveSlot>(
|
||||
x,
|
||||
y,
|
||||
@@ -52,8 +56,36 @@ class EvolutionSelectScreen(
|
||||
|
||||
private var entriesCreated = false
|
||||
|
||||
var controllerComponentManager = ControllerComponentManager(summary).also {
|
||||
it.onActiveChange = { state ->
|
||||
if (state) it.focused?.onSelect?.invoke() else it.focused?.onUnselect?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
public override fun addEntry(entry: EvolveSlot): Int {
|
||||
return super.addEntry(entry)
|
||||
val i = super.addEntry(entry)
|
||||
controllerComponentManager.componentList["moves_${i}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = entry
|
||||
it.onPress = {
|
||||
entry.selectButton.onPress()
|
||||
}
|
||||
it.onSelect = {
|
||||
ensureVisible(entry)
|
||||
entry.selectButton.isFocused = true
|
||||
}
|
||||
it.onUnselect = {
|
||||
entry.selectButton.isFocused = false
|
||||
}
|
||||
}
|
||||
if (children().size > 1) {
|
||||
controllerComponentManager.componentList["moves_${i}"]?.let {
|
||||
it.pairUp(controllerComponentManager.componentList["moves_${i-1}"])
|
||||
it.pairDown(controllerComponentManager.componentList["moves_${0}"])
|
||||
}
|
||||
} else {
|
||||
controllerComponentManager.focused = controllerComponentManager.componentList["moves_${i}"]
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTicks: Float) {
|
||||
|
||||
+15
-5
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.network.chat.Component
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector2i
|
||||
import org.joml.Vector3f
|
||||
|
||||
class ModelWidget(
|
||||
@@ -36,6 +37,7 @@ class ModelWidget(
|
||||
): SoundlessWidget(pX, pY, pWidth, pHeight, Component.literal("Summary - ModelWidget")) {
|
||||
|
||||
companion object {
|
||||
var render = true
|
||||
const val MAX_SECONDS_LOOKING = 6L
|
||||
}
|
||||
|
||||
@@ -58,10 +60,10 @@ class ModelWidget(
|
||||
var currentYawAndPitch: Pair<Float, Float> = Pair(0f, 0f)
|
||||
val rotationVector = Vector3f(13F, rotationY, 0F)
|
||||
|
||||
var shouldRender = true
|
||||
var overrideLookPos: Vector2i? = null
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, partialTicks: Float) {
|
||||
if (!shouldRender) {
|
||||
if (!render) {
|
||||
return
|
||||
}
|
||||
isHovered = pMouseX >= x && pMouseY >= y && pMouseX < x + width && pMouseY < y + height
|
||||
@@ -89,7 +91,7 @@ class ModelWidget(
|
||||
|
||||
val timeLooking = lookStartTime?.let { (System.currentTimeMillis() - it) / 1000 } ?: MAX_SECONDS_LOOKING
|
||||
val lookedTooMuch = timeLooking >= MAX_SECONDS_LOOKING
|
||||
val resetToCenter = (!isHovered && lookedTooMuch) || !shouldFollowCursor || !Cobblemon.config.summaryPokemonFollowCursor
|
||||
val resetToCenter = (!isHovered && lookedTooMuch && overrideLookPos == null) || !shouldFollowCursor || !Cobblemon.config.summaryPokemonFollowCursor
|
||||
|
||||
if (resetToCenter) {
|
||||
lookStartTime = null
|
||||
@@ -98,12 +100,19 @@ class ModelWidget(
|
||||
val rotation = Quaternionf().fromEulerXYZDegrees(rotationVector)
|
||||
|
||||
if (shouldFollowCursor) {
|
||||
var xPos = mouseX
|
||||
var yPos = mouseY
|
||||
if (overrideLookPos != null) {
|
||||
xPos = overrideLookPos!!.x
|
||||
yPos = overrideLookPos!!.y
|
||||
}
|
||||
|
||||
currentYawAndPitch = calculateHeadYawAndPitch(
|
||||
x + width / 2f,
|
||||
y + height / 2f,
|
||||
rotation,
|
||||
mouseX,
|
||||
mouseY,
|
||||
xPos,
|
||||
yPos,
|
||||
currentYawAndPitch.first,
|
||||
currentYawAndPitch.second,
|
||||
resetToCenter
|
||||
@@ -121,6 +130,7 @@ class ModelWidget(
|
||||
blockLight = blockLight,
|
||||
profileTransformType = ProfileTransformType.SUMMARY,
|
||||
)
|
||||
context.flush()
|
||||
|
||||
heldItemRenderer.renderOnModel(
|
||||
pokemon.heldItem,
|
||||
|
||||
+11
-6
@@ -88,7 +88,7 @@ class PartySlotWidget(
|
||||
y = y,
|
||||
width = width,
|
||||
height = height,
|
||||
vOffset = getSlotVOffset(slotPokemon, isHovered, isSelected),
|
||||
vOffset = getSlotVOffset(slotPokemon, isHovered || isFocused, isSelected),
|
||||
textureHeight = height * 2,
|
||||
)
|
||||
|
||||
@@ -223,16 +223,21 @@ class PartySlotWidget(
|
||||
if (partyWidget.swapEnabled) {
|
||||
toggleDrag(true)
|
||||
} else {
|
||||
if ((index > -1) && (summary.selectedPokemon.uuid != pokemon?.uuid)) {
|
||||
summary.switchSelection(index)
|
||||
if (pokemon != null) partyWidget.playSound(CobblemonSounds.GUI_CLICK)
|
||||
return true
|
||||
}
|
||||
if (onPress()) return true
|
||||
}
|
||||
}
|
||||
return super.mouseClicked(pMouseX, pMouseY, pButton)
|
||||
}
|
||||
|
||||
fun onPress(): Boolean {
|
||||
if ((index > -1) && (summary.selectedPokemon.uuid != pokemon?.uuid)) {
|
||||
summary.switchSelection(index)
|
||||
if (pokemon != null) partyWidget.playSound(CobblemonSounds.GUI_CLICK)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun mouseReleased(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
|
||||
if (partyWidget.swapEnabled) toggleDrag(false)
|
||||
return partyWidget.mouseReleased(pMouseX, pMouseY, pButton)
|
||||
|
||||
+85
-1
@@ -14,6 +14,8 @@ import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.summary.Summary
|
||||
import com.cobblemon.mod.common.client.gui.summary.SummaryButton
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -67,6 +69,8 @@ class PartyWidget(
|
||||
}
|
||||
)
|
||||
|
||||
var controllerComponentManager = ControllerComponentManager(summary)
|
||||
|
||||
init {
|
||||
if (partySize > 6 || partySize < 1)
|
||||
throw InvalidParameterException("Invalid party size")
|
||||
@@ -85,7 +89,7 @@ class PartyWidget(
|
||||
y += (32 * offsetIndex) + offsetY
|
||||
}
|
||||
|
||||
PartySlotWidget(
|
||||
val slot = PartySlotWidget(
|
||||
pX = x,
|
||||
pY = y,
|
||||
partyWidget = this,
|
||||
@@ -97,6 +101,86 @@ class PartyWidget(
|
||||
this.addWidget(widget)
|
||||
partySlots.add(widget)
|
||||
}
|
||||
|
||||
controllerComponentManager.componentList["slot_${index}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = slot
|
||||
it.onPress = {
|
||||
if (swapEnabled) {
|
||||
if (swapSource == null) {
|
||||
if (partyList[index] != null) {
|
||||
swapSource = index
|
||||
draggedSlot = PartySlotWidget(
|
||||
pX = slot.x + 4,
|
||||
pY = slot.y + 4,
|
||||
partyWidget = this,
|
||||
summary = summary,
|
||||
pokemon = partyList[index],
|
||||
index = -1,
|
||||
isClientPartyMember = isParty
|
||||
)
|
||||
draggedSlot?.isFocused = true
|
||||
playSound(CobblemonSounds.PC_GRAB)
|
||||
}
|
||||
} else {
|
||||
if (swapSource != index) {
|
||||
summary.swapPartySlot(swapSource!!, index)
|
||||
}
|
||||
swapSource = null
|
||||
draggedSlot = null
|
||||
playSound(CobblemonSounds.PC_DROP)
|
||||
}
|
||||
} else slot.onPress()
|
||||
}
|
||||
it.onSpecial1 = {
|
||||
swapEnabled = !swapEnabled
|
||||
swapButton.buttonActive = swapEnabled
|
||||
if (!swapEnabled) {
|
||||
swapSource = null
|
||||
draggedSlot = null
|
||||
}
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
}
|
||||
|
||||
fun updateDraggedSlotPos() {
|
||||
draggedSlot?.let { theSlot ->
|
||||
theSlot.x = slot.x + 4
|
||||
theSlot.y = slot.y + 4
|
||||
}
|
||||
}
|
||||
|
||||
if (index-2 >= 0) {
|
||||
it.up = {
|
||||
controllerComponentManager.componentList["slot_${index-2}"]
|
||||
}
|
||||
}
|
||||
if (index+2 <= 5) {
|
||||
it.down = {
|
||||
controllerComponentManager.componentList["slot_${index+2}"]
|
||||
}
|
||||
}
|
||||
if (index.mod(2) == 0) {
|
||||
it.right = {
|
||||
controllerComponentManager.componentList["slot_${index+1}"]
|
||||
}
|
||||
} else {
|
||||
it.left = {
|
||||
controllerComponentManager.componentList["slot_${index-1}"]
|
||||
}
|
||||
}
|
||||
it.onSelect = {
|
||||
updateDraggedSlotPos()
|
||||
summary.lastSelectedPartySlot = index
|
||||
}
|
||||
}
|
||||
}
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["slot_${summary.lastSelectedPartySlot}"]
|
||||
it.onActiveChange = { state ->
|
||||
if (!state) {
|
||||
swapSource = null
|
||||
draggedSlot = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
@@ -125,6 +125,23 @@ abstract class SummaryScrollList<T : ObjectSelectionList.Entry<T>>(
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
}
|
||||
|
||||
public override fun ensureVisible(entry: T) {
|
||||
val i = getRowTop(children().indexOf(entry))
|
||||
val topDifference = i - y - 4
|
||||
if (topDifference < 0) {
|
||||
scroll(topDifference)
|
||||
}
|
||||
|
||||
val bottomDifference = bottom - i - itemHeight
|
||||
if (bottomDifference < 0) {
|
||||
scroll(-bottomDifference)
|
||||
}
|
||||
}
|
||||
|
||||
public override fun getRowTop(index: Int): Int {
|
||||
return super.getRowTop(index)
|
||||
}
|
||||
|
||||
private fun updateScrollingState(mouseX: Double, mouseY: Double) {
|
||||
scrolling = mouseX >= this.scrollbarPosition.toDouble()
|
||||
&& mouseX < (this.scrollbarPosition + 3).toDouble()
|
||||
@@ -135,4 +152,6 @@ abstract class SummaryScrollList<T : ObjectSelectionList.Entry<T>>(
|
||||
private fun correctSize() {
|
||||
setRectangle(WIDTH, HEIGHT, listX, listY)
|
||||
}
|
||||
|
||||
open fun onPress(entry: T) {}
|
||||
}
|
||||
+4
-3
@@ -106,10 +106,11 @@ class MarksScrollingWidget(val pX: Int, val pY: Int, val setSelectedMark: () ->
|
||||
companion object {
|
||||
private val slotResource = cobblemonResource("textures/gui/summary/summary_mark_slot.png")
|
||||
}
|
||||
|
||||
var x: Int = 0
|
||||
var y: Int = 0
|
||||
|
||||
var slotSelected: (Int) -> Boolean = { false }
|
||||
|
||||
fun renderRow(context: GuiGraphics, y: Int, x: Int, mouseX: Int, mouseY: Int): Boolean {
|
||||
var anyHovered = false
|
||||
val horizontalSpacing = if (isScrollVisible()) SLOT_SPACING else (SLOT_SPACING + 1)
|
||||
@@ -133,7 +134,7 @@ class MarksScrollingWidget(val pX: Int, val pY: Int, val setSelectedMark: () ->
|
||||
y = startPosY,
|
||||
width = SLOT_SIZE,
|
||||
height = SLOT_SIZE,
|
||||
vOffset = if (slotHovered) SLOT_SIZE else 0,
|
||||
vOffset = if (slotHovered || (mark != null && slotSelected.invoke(index))) SLOT_SIZE else 0,
|
||||
textureHeight = SLOT_SIZE * 2
|
||||
)
|
||||
|
||||
@@ -160,7 +161,7 @@ class MarksScrollingWidget(val pX: Int, val pY: Int, val setSelectedMark: () ->
|
||||
return true
|
||||
}
|
||||
|
||||
private fun isScrollVisible(): Boolean = parent.children().size > 5
|
||||
fun isScrollVisible(): Boolean = parent.children().size > 5
|
||||
|
||||
private fun getHoveredSlotIndex(mouseX: Int, mouseY: Int): Int {
|
||||
val horizontalSpacing = if (isScrollVisible()) SLOT_SPACING else (SLOT_SPACING + 1)
|
||||
|
||||
+56
-1
@@ -15,7 +15,10 @@ import com.cobblemon.mod.common.api.gui.MultiLineLabelK
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.mark.Mark
|
||||
import com.cobblemon.mod.common.client.gui.common.MarkIcon
|
||||
import com.cobblemon.mod.common.client.gui.summary.Summary
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
@@ -27,7 +30,8 @@ import net.minecraft.network.chat.Component
|
||||
|
||||
class MarksWidget(
|
||||
pX: Int, pY: Int,
|
||||
val pokemon: Pokemon
|
||||
val pokemon: Pokemon,
|
||||
val summary: Summary
|
||||
): SoundlessWidget(pX, pY, WIDTH, HEIGHT, Component.literal("MarksWidget")) {
|
||||
companion object {
|
||||
private const val WIDTH = 134
|
||||
@@ -53,6 +57,9 @@ class MarksWidget(
|
||||
setHoveredMark = { hoveredMark = it }
|
||||
)
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(summary)
|
||||
var lastSelectedMark = 0
|
||||
|
||||
init {
|
||||
children.clear()
|
||||
activeMark = pokemon.activeMark
|
||||
@@ -64,6 +71,54 @@ class MarksWidget(
|
||||
while ((marksList.size < 30) || (marksList.size > 30 && marksList.size % 6 != 0)) marksList.add(null)
|
||||
marksScrollList.createEntries(marksList)
|
||||
addWidget(marksScrollList)
|
||||
|
||||
marksScrollList.children().forEachIndexed { firstIndex, row ->
|
||||
row.markList.forEachIndexed { secondIndex, mark ->
|
||||
controllerComponentManager.componentList["mark_${firstIndex}_${secondIndex}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = row
|
||||
it.onPress = {
|
||||
if (mark != null) {
|
||||
selectedMark = mark
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
|
||||
}
|
||||
}
|
||||
it.onSpecial1 = {
|
||||
if (selectedMark !== null) {
|
||||
selectedMark = null
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
|
||||
}
|
||||
}
|
||||
it.onSelect = {
|
||||
lastSelectedMark = firstIndex * 6 + secondIndex
|
||||
marksScrollList.ensureVisible(row)
|
||||
}
|
||||
if (firstIndex - 1 >= 0) {
|
||||
it.up = {
|
||||
controllerComponentManager.componentList["mark_${firstIndex-1}_${secondIndex}"]
|
||||
}
|
||||
}
|
||||
if (firstIndex + 1 <= marksScrollList.children().size - 1) {
|
||||
it.down = {
|
||||
controllerComponentManager.componentList["mark_${firstIndex+1}_${secondIndex}"]
|
||||
}
|
||||
}
|
||||
if (secondIndex - 1 >= 0) {
|
||||
it.left = {
|
||||
controllerComponentManager.componentList["mark_${firstIndex}_${secondIndex-1}"]
|
||||
}
|
||||
}
|
||||
if (secondIndex + 1 <= row.markList.size - 1) {
|
||||
it.right = {
|
||||
controllerComponentManager.componentList["mark_${firstIndex}_${secondIndex+1}"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
row.slotSelected = { index ->
|
||||
controllerComponentManager.active && lastSelectedMark / 6 == firstIndex && lastSelectedMark % 6 == index
|
||||
}
|
||||
}
|
||||
controllerComponentManager.let { it.focused = it.componentList["mark_${lastSelectedMark / 6}_${lastSelectedMark % 6}"] }
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
|
||||
+2
-2
@@ -45,14 +45,14 @@ class MoveSlotWidget(
|
||||
const val MOVE_HEIGHT = 22
|
||||
}
|
||||
|
||||
private val moveUpButton = ReorderMoveButton(x, y, true) {
|
||||
val moveUpButton = ReorderMoveButton(x, y, true) {
|
||||
movesWidget.selectMove(null)
|
||||
movesWidget.reorderMove(this, true)
|
||||
}.apply {
|
||||
addWidget(this)
|
||||
}
|
||||
|
||||
private val moveDownButton = ReorderMoveButton(x, y, false) {
|
||||
val moveDownButton = ReorderMoveButton(x, y, false) {
|
||||
movesWidget.selectMove(null)
|
||||
movesWidget.reorderMove(this, false)
|
||||
}.apply {
|
||||
|
||||
+58
-16
@@ -20,6 +20,8 @@ import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.gui.summary.Summary
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.common.SummaryScrollList
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
@@ -49,13 +51,45 @@ class MoveSwapScreen(
|
||||
private val forgetMoveIconResource = cobblemonResource("textures/gui/summary/summary_move_condensed_empty_icon.png")
|
||||
}
|
||||
|
||||
var controllerComponentManager = ControllerComponentManager(movesWidget.summary).also {
|
||||
it.onActiveChange = { state ->
|
||||
if (state) it.focused?.onSelect?.invoke() else it.focused?.onUnselect?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
public override fun addEntry(entry: MoveSlot): Int {
|
||||
return super.addEntry(entry)
|
||||
val i = super.addEntry(entry)
|
||||
controllerComponentManager.componentList["moves_${i}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = entry
|
||||
it.onPress = {
|
||||
entry.onPress()
|
||||
movesWidget.summary.controllerComponentManager.let { controller -> controller.focused = controller.componentList["center_screen"] }
|
||||
}
|
||||
it.onSelect = {
|
||||
ensureVisible(entry)
|
||||
entry.slotSelected = true
|
||||
}
|
||||
it.onUnselect = {
|
||||
entry.slotSelected = false
|
||||
}
|
||||
}
|
||||
if (children().size > 1) {
|
||||
controllerComponentManager.componentList["moves_${i}"]?.let {
|
||||
it.pairUp(controllerComponentManager.componentList["moves_${i-1}"])
|
||||
it.pairDown(controllerComponentManager.componentList["moves_${0}"])
|
||||
}
|
||||
} else {
|
||||
controllerComponentManager.focused = controllerComponentManager.componentList["moves_${i}"]
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
class MoveSlot(val pane: MoveSwapScreen, val move: MoveTemplate?, val ppRaisedStages: Int, pokemon: Pokemon) : Entry<MoveSlot>() {
|
||||
override fun getNarration() = move?.displayName ?: lang("ui.moves.forget")
|
||||
|
||||
val elementalType = move?.getEffectiveElementalType(pokemon) ?: ElementalTypes.NORMAL
|
||||
var slotSelected = false
|
||||
|
||||
override fun render(
|
||||
context: GuiGraphics,
|
||||
index: Int,
|
||||
@@ -80,7 +114,7 @@ class MoveSwapScreen(
|
||||
y = tweakedRowTop,
|
||||
height = SLOT_HEIGHT,
|
||||
width = rowWidth,
|
||||
vOffset = if (isHovered) SLOT_HEIGHT else 0,
|
||||
vOffset = if (isHovered || slotSelected) SLOT_HEIGHT else 0,
|
||||
textureHeight = SLOT_HEIGHT * 2,
|
||||
red = rgb.first,
|
||||
green = rgb.second,
|
||||
@@ -196,7 +230,7 @@ class MoveSwapScreen(
|
||||
y = tweakedRowTop,
|
||||
height = SLOT_HEIGHT,
|
||||
width = 100,
|
||||
vOffset = if (isHovered) SLOT_HEIGHT else 0,
|
||||
vOffset = if (isHovered || slotSelected) SLOT_HEIGHT else 0,
|
||||
textureHeight = SLOT_HEIGHT * 2,
|
||||
)
|
||||
blitk(
|
||||
@@ -206,7 +240,7 @@ class MoveSwapScreen(
|
||||
y = tweakedRowTop + SLOT_HEIGHT / 2 - 4,
|
||||
height = 7,
|
||||
width = 7,
|
||||
vOffset = if (isHovered) 7 else 0,
|
||||
vOffset = if (isHovered || slotSelected) 7 else 0,
|
||||
textureHeight = 14,
|
||||
)
|
||||
}
|
||||
@@ -215,21 +249,29 @@ class MoveSwapScreen(
|
||||
|
||||
override fun mouseClicked(d: Double, e: Double, i: Int): Boolean {
|
||||
if (isMouseOver(d, e)) {
|
||||
val pokemon = pane.movesWidget.summary.selectedPokemon
|
||||
val isParty = pokemon in CobblemonClient.storage.party
|
||||
CobblemonNetwork.sendToServer(
|
||||
BenchMovePacket(
|
||||
isParty = isParty,
|
||||
uuid = pokemon.uuid,
|
||||
oldMove = pane.replacedMove?.template,
|
||||
newMove = move
|
||||
)
|
||||
)
|
||||
pane.movesWidget.summary.playSound(CobblemonSounds.GUI_CLICK)
|
||||
pane.movesWidget.summary.displaySideScreen(Summary.PARTY)
|
||||
onPress()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun onPress() {
|
||||
switchMove()
|
||||
pane.movesWidget.summary.playSound(CobblemonSounds.GUI_CLICK)
|
||||
pane.movesWidget.summary.displaySideScreen(Summary.PARTY)
|
||||
}
|
||||
|
||||
fun switchMove() {
|
||||
val pokemon = pane.movesWidget.summary.selectedPokemon
|
||||
val isParty = pokemon in CobblemonClient.storage.party
|
||||
CobblemonNetwork.sendToServer(
|
||||
BenchMovePacket(
|
||||
isParty = isParty,
|
||||
uuid = pokemon.uuid,
|
||||
oldMove = pane.replacedMove?.template,
|
||||
newMove = move
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
-2
@@ -9,12 +9,15 @@
|
||||
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.moves.Move
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.gui.summary.Summary
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.net.messages.server.RequestMoveSwapPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -52,8 +55,8 @@ class MovesWidget(
|
||||
|
||||
var selectedMove: Move? = null
|
||||
|
||||
private var index = -1
|
||||
private val moves = (
|
||||
var index = -1
|
||||
val moves = (
|
||||
if (summary.selectedPokemon.relearnableMoves.count() > 0)
|
||||
summary.selectedPokemon.moveSet.getMovesWithNulls()
|
||||
else summary.selectedPokemon.moveSet
|
||||
@@ -76,6 +79,79 @@ class MovesWidget(
|
||||
5
|
||||
)
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(summary).also { controller ->
|
||||
var i = 0
|
||||
moves.forEach { move ->
|
||||
controller.componentList["move_${i}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = move
|
||||
var upLoc = i - 1
|
||||
if (upLoc < 0) upLoc = moves.size - 1
|
||||
it.up = {
|
||||
var returnVal: ControllerComponent?
|
||||
if (!summary.moveSwitchingMode) {
|
||||
returnVal = controller.componentList["move_${upLoc}"]
|
||||
} else {
|
||||
returnVal = null
|
||||
summary.selectedMoveIndex = upLoc
|
||||
reorderMove(move, true)
|
||||
}
|
||||
returnVal
|
||||
}
|
||||
var downLoc = i + 1
|
||||
if (downLoc > moves.size - 1) downLoc = 0
|
||||
it.down = {
|
||||
var returnVal: ControllerComponent?
|
||||
if (!summary.moveSwitchingMode) {
|
||||
returnVal = controller.componentList["move_${downLoc}"]
|
||||
} else {
|
||||
returnVal = null
|
||||
summary.selectedMoveIndex = downLoc
|
||||
reorderMove(move, false)
|
||||
}
|
||||
returnVal
|
||||
}
|
||||
it.onSpecial1 = {
|
||||
if (!summary.moveSwitchingMode) {
|
||||
summary.playSound(CobblemonSounds.GUI_CLICK)
|
||||
summary.displaySideScreen(Summary.MOVE_SWAP, move.move)
|
||||
}
|
||||
}
|
||||
it.onSpecial2 = {
|
||||
summary.playSound(CobblemonSounds.GUI_CLICK)
|
||||
summary.moveSwitchingMode = !summary.moveSwitchingMode
|
||||
if (summary.moveSwitchingMode) {
|
||||
move.moveUpButton.isFocused = true
|
||||
move.moveDownButton.isFocused = true
|
||||
} else {
|
||||
move.moveUpButton.isFocused = false
|
||||
move.moveDownButton.isFocused = false
|
||||
}
|
||||
}
|
||||
val staticI = i
|
||||
it.onSelect = {
|
||||
summary.selectedMoveIndex = staticI
|
||||
if (selectedMove != move.move) selectMove(move.move)
|
||||
if (summary.moveSwitchingMode) {
|
||||
move.moveUpButton.isFocused = true
|
||||
move.moveDownButton.isFocused = true
|
||||
}
|
||||
}
|
||||
it.onUnselect = {
|
||||
move.moveUpButton.isFocused = false
|
||||
move.moveDownButton.isFocused = false
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
controller.onNoneSelected = {
|
||||
summary.selectedMoveIndex = summary.selectedMoveIndex.coerceIn(0, moves.size - 1)
|
||||
controller.focused = controller.componentList["move_${summary.selectedMoveIndex}"]
|
||||
true
|
||||
}
|
||||
controller.onActiveChange = { state ->
|
||||
if (state) controller.focused?.onSelect?.invoke() else controller.focused?.onUnselect?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
val matrices = context.pose()
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ class ReorderMoveButton(
|
||||
texture = if (isUp) moveReorderUpResource else moveReorderDownResource,
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
vOffset = if (isHovered(pMouseX.toDouble(), pMouseY.toDouble(), offsetY.toFloat())) HEIGHT else 0,
|
||||
vOffset = if (isHovered(pMouseX.toDouble(), pMouseY.toDouble(), offsetY.toFloat()) || isFocused) HEIGHT else 0,
|
||||
textureHeight = HEIGHT * 2,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
+13
-11
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.summary.Summary
|
||||
import com.cobblemon.mod.common.client.gui.summary.SummaryButton
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
|
||||
@@ -49,16 +50,17 @@ import org.joml.Vector3f
|
||||
class StatWidget(
|
||||
pX: Int, pY: Int,
|
||||
val pokemon: Pokemon,
|
||||
val tabIndex: Int = 0
|
||||
val tabIndex: Int = 0,
|
||||
val summary: Summary
|
||||
): SoundlessWidget(pX, pY, WIDTH, HEIGHT, Component.literal("StatWidget")) {
|
||||
|
||||
companion object {
|
||||
// Stat tab options
|
||||
private const val STATS = "stats"
|
||||
private const val IV = "ivs"
|
||||
private const val EV = "evs"
|
||||
private const val RIDE = "ride"
|
||||
private const val OTHER = "other"
|
||||
const val STATS = "stats"
|
||||
const val IV = "ivs"
|
||||
const val EV = "evs"
|
||||
const val RIDE = "ride"
|
||||
const val OTHER = "other"
|
||||
|
||||
private const val OTHER_STAT_BARS_PER_PAGE = 4
|
||||
private const val STAT_TAB_WIDTH_6 = 22
|
||||
@@ -113,13 +115,13 @@ class StatWidget(
|
||||
)
|
||||
}
|
||||
|
||||
private val statOptions = if (pokemon.form.riding.behaviours != null) listOf(STATS, IV, EV, RIDE, OTHER) else listOf(STATS, IV, EV, OTHER)
|
||||
val statOptions = if (pokemon.form.riding.behaviours != null) listOf(STATS, IV, EV, RIDE, OTHER) else listOf(STATS, IV, EV, OTHER)
|
||||
|
||||
private var _statTabIndex: Int = tabIndex
|
||||
var rawStatTabIndex: Int = tabIndex
|
||||
var statTabIndex: Int
|
||||
get() = _statTabIndex.coerceIn(0, statOptions.size - 1)
|
||||
get() = rawStatTabIndex.coerceIn(0, statOptions.size - 1)
|
||||
set(value) {
|
||||
_statTabIndex = value.coerceIn(0, statOptions.size - 1)
|
||||
rawStatTabIndex = value.coerceIn(0, statOptions.size - 1)
|
||||
}
|
||||
|
||||
private var _rideBehaviourIndex: Int = 0
|
||||
@@ -302,7 +304,7 @@ class StatWidget(
|
||||
pokemon.form.riding.behaviours?.let {
|
||||
val behaviours = it.entries.toList()
|
||||
val selectedBehaviour = behaviours[rideBehaviourIndex]
|
||||
val canSwitchStyle = behaviours.size > 1 && centerHovered(pMouseX, pMouseY)
|
||||
val canSwitchStyle = behaviours.size > 1 && (centerHovered(pMouseX, pMouseY) || (summary.controllerComponentManager.let { it.active && it.currentSelectedComponent == "center_screen" }))
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
|
||||
-13
@@ -1267,19 +1267,6 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
|
||||
}
|
||||
|
||||
override fun hasClickedOutside(
|
||||
mouseX: Double,
|
||||
mouseY: Double,
|
||||
guiLeft: Int,
|
||||
guiTop: Int,
|
||||
mouseButton: Int
|
||||
): Boolean {
|
||||
partySlotList.forEach {
|
||||
if (it.pokemon != null && it.isMouseOver(mouseX, mouseY)) return false
|
||||
}
|
||||
return super.hasClickedOutside(mouseX, mouseY, guiLeft, guiTop, mouseButton)
|
||||
}
|
||||
|
||||
class MoveDescriptionWidget(pX: Int, pY: Int, width: Int, height: Int) : ScrollingWidget<MoveDescriptionWidget.TextSlot>(
|
||||
left = pX,
|
||||
top = pY - height,
|
||||
|
||||
@@ -49,7 +49,7 @@ open class PartySlot(
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
val matrices = context.pose()
|
||||
if (!isOpposing && isHovered(mouseX, mouseY)) {
|
||||
if (!isOpposing && (isHovered(mouseX, mouseY) || isFocused)) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = hoverBackgroundResource,
|
||||
|
||||
@@ -49,7 +49,7 @@ class TradeButton(
|
||||
y = y,
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble())) HEIGHT else 0,
|
||||
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble()) || isFocused) HEIGHT else 0,
|
||||
textureHeight = HEIGHT * 2
|
||||
)
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.gui.summary.Summary
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.MarkingsWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.common.reformatNatureTextIfMinted
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponentManager
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerComponent
|
||||
import com.cobblemon.mod.common.client.integration.controlify.gui.component.ControllerScreen
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
|
||||
import com.cobblemon.mod.common.client.trade.ClientTrade
|
||||
@@ -55,7 +58,7 @@ class TradeGUI(
|
||||
val traderName: MutableComponent,
|
||||
val traderParty: MutableList<TradeablePokemon?>,
|
||||
val party: MutableList<TradeablePokemon?>
|
||||
): Screen(lang("trade.gui.title")), CobblemonRenderable {
|
||||
): Screen(lang("trade.gui.title")), CobblemonRenderable, ControllerScreen {
|
||||
|
||||
companion object {
|
||||
const val BASE_WIDTH = 293
|
||||
@@ -118,6 +121,13 @@ class TradeGUI(
|
||||
|
||||
var tradeSoundInstance: SoundInstance? = null
|
||||
|
||||
val controllerComponentManager = ControllerComponentManager(this)
|
||||
var lastSelectedRow = 0
|
||||
|
||||
override fun getComponentManager(): ControllerComponentManager {
|
||||
return controllerComponentManager
|
||||
}
|
||||
|
||||
init {
|
||||
trade.cancelEmitter.subscribe {
|
||||
cancelTradeSound()
|
||||
@@ -178,21 +188,25 @@ class TradeGUI(
|
||||
)
|
||||
|
||||
// Trade Button
|
||||
this.addRenderableWidget(
|
||||
TradeButton(
|
||||
x = x + 120,
|
||||
y = y + 119,
|
||||
parent = this,
|
||||
onPress = {
|
||||
if (offeredPokemon != null && opposingOfferedPokemon != null && protectiveTicks <= 0 && !tradeProcessing) {
|
||||
ticksElapsed = 0
|
||||
readyProgress = 0
|
||||
CobblemonNetwork.sendToServer(ChangeTradeAcceptancePacket(opposingOfferedPokemon!!.uuid, !trade.acceptedOppositeOffer))
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
}
|
||||
val tradeButton = TradeButton(
|
||||
x = x + 120,
|
||||
y = y + 119,
|
||||
parent = this,
|
||||
onPress = {
|
||||
if (offeredPokemon != null && opposingOfferedPokemon != null && protectiveTicks <= 0 && !tradeProcessing) {
|
||||
ticksElapsed = 0
|
||||
readyProgress = 0
|
||||
CobblemonNetwork.sendToServer(ChangeTradeAcceptancePacket(opposingOfferedPokemon!!.uuid, !trade.acceptedOppositeOffer))
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
this.addRenderableWidget(tradeButton)
|
||||
|
||||
controllerComponentManager.componentList["trade"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = tradeButton
|
||||
it.left = { controllerComponentManager.componentList["slot_${lastSelectedRow}_${1}"] }
|
||||
}
|
||||
|
||||
// Party
|
||||
for (partyIndex in 0..5) {
|
||||
@@ -210,7 +224,7 @@ class TradeGUI(
|
||||
}
|
||||
|
||||
val pokemon = party[partyIndex]
|
||||
PartySlot(
|
||||
val slot = PartySlot(
|
||||
x = slotX,
|
||||
y = slotY,
|
||||
pokemon = pokemon,
|
||||
@@ -223,6 +237,22 @@ class TradeGUI(
|
||||
}
|
||||
}
|
||||
).also { widget -> addRenderableWidget(widget) }
|
||||
|
||||
val row = partyIndex / 2
|
||||
val column = partyIndex % 2
|
||||
controllerComponentManager.componentList["slot_${row}_${column}"] = ControllerComponent().also {
|
||||
it.associatedGUIElement = slot
|
||||
if (column == 0) it.right = { controllerComponentManager.componentList["slot_${row}_${1}"] }
|
||||
else {
|
||||
it.left = { controllerComponentManager.componentList["slot_${row}_${0}"] }
|
||||
it.right = { controllerComponentManager.componentList["trade"] }
|
||||
}
|
||||
if (row - 1 >= 0) it.up = { controllerComponentManager.componentList["slot_${row-1}_${column}"] }
|
||||
if (row + 1 <= 2) it.down = { controllerComponentManager.componentList["slot_${row+1}_${column}"] }
|
||||
it.onSelect = {
|
||||
lastSelectedRow = row
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Opposing Party
|
||||
@@ -257,6 +287,10 @@ class TradeGUI(
|
||||
opposingOfferedPokemonMarkings = MarkingsWidget(x + 177.5, y + 21, opposingOfferedPokemon, false)
|
||||
addRenderableWidget(offeredPokemonMarkings)
|
||||
addRenderableWidget(opposingOfferedPokemonMarkings)
|
||||
|
||||
controllerComponentManager.let {
|
||||
it.focused = it.componentList["slot_0_0"]
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
@@ -445,6 +479,8 @@ class TradeGUI(
|
||||
val itemHovered = mouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && mouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
|
||||
if (itemHovered) context.renderTooltip(Minecraft.getInstance().font, opposingOfferedPokemon!!.heldItemNoCopy(), mouseX, mouseY)
|
||||
}
|
||||
|
||||
renderSelection(matrices)
|
||||
}
|
||||
|
||||
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
|
||||
|
||||
Loaded 100 of 3097 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user