forked from mirrors/cobblemon
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a12f1c80e7 |
@@ -0,0 +1,3 @@
|
||||
### Additions
|
||||
- Added the Poké Dex as a craftable item.
|
||||
- Poké Dexes can be placed on lecterns.
|
||||
@@ -0,0 +1,6 @@
|
||||
### Additions
|
||||
- Added `moves` option to PokemonProperties, allowing you to set the moves of a Pokémon in commands and spawn files.
|
||||
- Added /spawnnpc and /spawnnpcat commands.
|
||||
|
||||
### Fixes
|
||||
- Fixed sync issues with recently learned moves where their PP would not go down until you logout and in.
|
||||
@@ -0,0 +1,24 @@
|
||||
### Additions
|
||||
- Pokémon are now animated when seen in any GUI that isn't the party GUI.
|
||||
- Quirk animations can now occur for Pokémon that are shoulder mounted.
|
||||
|
||||
### Changes
|
||||
- Improved the performance of display cases that contain Pokémon photos.
|
||||
|
||||
### Fixes
|
||||
- Fixed some Pokémon photos in display cases flashing if the Pokémon has gender differences.
|
||||
- Fixed Bidoof and Jigglypuff sleep animations stopping after some time, causing them to be 'asleep' while T-posing menacingly.
|
||||
- Fixed entities not changing poses unless you're looking at them.
|
||||
- Fixed cries not playing on send-out if the Pokémon is off-screen.
|
||||
|
||||
### Developer
|
||||
- Rebuilt large swaths of the model animation code to simplify it.
|
||||
- Renamed a bunch of things from %Poseable% to %Posable% because spelling.
|
||||
- Renamed StatelessAnimation to PoseAnimation.
|
||||
- Renamed StatefulAnimation to ActiveAnimation.
|
||||
- Documented the animation system.
|
||||
|
||||
### Data Pack & Resource Pack Creators
|
||||
- Added MoLang compatibility in the isVisible property for transformed parts.
|
||||
- Added q.has_aspect('some_aspect') function to animations, posers, and entity particle effects.
|
||||
- Added support for conditional pose animations.
|
||||
+15
-235
@@ -3,24 +3,22 @@
|
||||
#### "Now that there's a fishing mechanic, the mod is actually good!"
|
||||
|
||||
### Additions
|
||||
- Added the Poké Dex as a craftable item.
|
||||
- Poké Dexes can be placed on lecterns.
|
||||
- Added Pokémon (and item) fishing using modified fishing rods - Poké Rods! You'll need a Poké Rod smithing template, a fishing rod, and some type of Poké Ball. Each Poké Ball makes a differently themed rod. Why not?
|
||||
- Added Lure Ball functionality, increasing the catch rate of Pokémon that were caught on a fishing rod.
|
||||
- Added Repeat Ball functionality, increasing the catch rate of Pokémon that are already registered as caught in a player's Pokédex.
|
||||
- Added property chaining support for duplicate CustomPokemonPropertyType elements.
|
||||
- Added flat level battling with options to set all Pokemon to level 50, 100, or 5 for the duration of a battle. No experience or EVs are granted for a flat battle.
|
||||
- Added `aspect` and `unaspect` PokemonProperty arguments (which also includes commands such as `/pokemonedit`, `/spawnpokemon`, and `/givepokemon`) to allow forcing or un-forcing an aspect on a Pokémon.
|
||||
- Added `type` alternatively `elemental_type` PokemonProperty argument, this is only used for filtering and is not applied to Pokémon. Example `type=fire` would be true for Charmander but false for Squirtle.
|
||||
- Added support for Double Battles, Triple Battles, and Multi-battles.
|
||||
- CriticalCaptures and Pokédex progress capture multiplier now work with the Pokédex.
|
||||
- Added extra visuals to early berry growth stages.
|
||||
- Hidden Power now displays its effective typing.
|
||||
- Moves impacted by the abilities Pixelate, Refrigerate, Aerilate, Galvanize, and Normalize now display as their altered typing.
|
||||
- Added Polished Tumblestone and Tumblestone Brick block sets from Tumblestone, Black Tumblestone, and Sky Tumblestone.
|
||||
- Added Metronome, Protective Pads, Punching Glove, Room Service, Scope Lens, Shed Shell, Terrain Extender, Throat Spray, Utility Umbrella, Wide Lens, and Zoom Lens held items
|
||||
- Added particles for moves: Watersport, Mud Sport, and Psychic.
|
||||
- Added Fire, Water, Thunder, Leaf, Ice, Sun, Moon, Shiny, Dawn, and Dusk Stone storage blocks.
|
||||
- Added Eject Pack, Metronome, Protective Pads, Punching Glove, Room Service, Scope Lens, Shed Shell, Terrain Extender, Throat Spray, Utility Umbrella, Wide Lens, and Zoom Lens held items
|
||||
- Moves can now be benched without specifying a replacement move, allowing for empty move slots.
|
||||
- Moves learned via Sketch now persist after battle.
|
||||
- Added `stonjourner_henge_ruins`, `luna_henge_ruins`, and `sol_henge_ruins` structures.
|
||||
- Added a `battleInvulnerability` gamerule to make players invulnerable to any damage during a battle.
|
||||
- Added a `mobTargetInBattle` gamerule to exclude players from being targeted by mobs during a battle.
|
||||
@@ -32,29 +30,9 @@
|
||||
- New config setting `displayEntityNameLabel` and `displayEntityLabelsWhenCrouchingOnly` to control what and when is displayed for the pokemon label
|
||||
- `/freezepokemon` command to pause a Pokémon's animation at a specific point in time.
|
||||
- Added `no_ai` and `freeze_frame` options to the `/spawnpokemon` command.
|
||||
- Added shiny Pokémon effects.
|
||||
- Added effects for the burn status effect.
|
||||
- Added effects for the moves: Psychic, Water Sport, and Mud Sport.
|
||||
- Added a new universal locator called "top".
|
||||
- Added `moves` option to Pokémon properties, allowing you to set the moves of a Pokémon in commands and spawn files using comma-separated move names.
|
||||
- Added shiny Pokémon effects.
|
||||
- Added effects for the burn status effect.
|
||||
- Added effects for the moves: Seismic Toss, Withdraw, Bite, Crunch, Super Fang, Hyper Fang, Pursuit, Mist, Haze, Lick, Kinesis, Psychic, Water Sport, and Mud Sport.
|
||||
- Added a new universal locator called `top`.
|
||||
- Added shiny Pokémon particles with sound effects.
|
||||
- Added animation for trading.
|
||||
- Added icons for pending trade, team-up, and battle requests from other players.
|
||||
- Added `/spawnnpc` and `/spawnnpcat` commands.
|
||||
- Pokémon are now animated when seen in any GUI that isn't the party GUI.
|
||||
- Quirk animations can now occur for Pokémon that are shoulder mounted.
|
||||
- Added `fishing boat` structures that contain a map to a shipwreck cove and a pokerod smithing template.
|
||||
- Added `submerged_shipwreck_cove` and `lush_shipwreck_cove` structures.
|
||||
|
||||
### Pokémon Added
|
||||
#### Gen 1
|
||||
- Alola Bias Cubone
|
||||
- Alolan Marowak
|
||||
|
||||
#### Gen 2
|
||||
- Ledyba
|
||||
- Ledian
|
||||
@@ -62,28 +40,18 @@
|
||||
- Sunflora
|
||||
- Wobbuffet
|
||||
- Girafarig
|
||||
- Corsola
|
||||
- Remoraid
|
||||
- Octillery
|
||||
- Mantine
|
||||
- Smeargle
|
||||
- Corsola
|
||||
|
||||
#### Gen 3
|
||||
- Slakoth
|
||||
- Vigoroth
|
||||
- Slaking
|
||||
- Corphish
|
||||
- Crawdaunt
|
||||
- Wynaut
|
||||
- Feebas
|
||||
- Milotic
|
||||
- Wynaut
|
||||
- Absol
|
||||
- Spheal
|
||||
- Sealeo
|
||||
- Walrein
|
||||
- Bagon
|
||||
- Shelgon
|
||||
- Salamence
|
||||
- Kecleon
|
||||
|
||||
#### Gen 4
|
||||
@@ -97,194 +65,31 @@
|
||||
#### Gen 5
|
||||
- Purrloin
|
||||
- Liepard
|
||||
- Scraggy
|
||||
- Scrafty
|
||||
|
||||
#### Gen 6
|
||||
- Clauncher
|
||||
- Clawitzer
|
||||
|
||||
#### Gen 7
|
||||
- Pikipek
|
||||
- Trumbeak
|
||||
- Toucannon
|
||||
- Bruxish
|
||||
- Mareanie
|
||||
- Toxapex
|
||||
- Sandygast
|
||||
- Palossand
|
||||
- Bruxish
|
||||
|
||||
#### Gen 8
|
||||
- Cramorant
|
||||
- Hatenna
|
||||
- Hattrem
|
||||
- Hatterene
|
||||
- Pincurchin
|
||||
|
||||
#### Gen 9
|
||||
- Wattrel
|
||||
- Kilowattrel
|
||||
- Wiglett
|
||||
- Wugtrio
|
||||
- Flamigo
|
||||
- Veluza
|
||||
- Farigiraf
|
||||
- Klawf
|
||||
- Finizen
|
||||
- Palafin
|
||||
- Wiglett
|
||||
- Wugtrio
|
||||
- Flamigo
|
||||
- Dondozo
|
||||
|
||||
#### Unique Forms
|
||||
- Magikarp Jump Variants
|
||||
- Gyarados Jump Variants
|
||||
|
||||
### Added cries to the following Pokémon
|
||||
- Sandshrew, Sandslash
|
||||
- Magnemite, Magneton, Magnezone
|
||||
- Gastly, Haunter, Gengar
|
||||
- Rhyhorn, Rhydon, Rhyperior
|
||||
- Happiny, Chansey
|
||||
- Horsea, Seadra, Kingdra
|
||||
- Magby, Magmar, Magmortar
|
||||
- Porygon, Porygon2, Porygon-Z
|
||||
- Sentret, Furret
|
||||
- Misdreavus, Mismagius
|
||||
- Corsola
|
||||
- Mantyke, Mantine
|
||||
- Girafarig, Farigiraf
|
||||
- Wynaut, Wobbuffet
|
||||
- Lunatone
|
||||
- Solrock
|
||||
- Corphish, Crawdaunt
|
||||
- Feebas, Milotic
|
||||
- Kecleon
|
||||
- Absol
|
||||
- Spheal, Sealeo, Walrein
|
||||
- Relicanth
|
||||
- Bagon, Shelgon, Salamence
|
||||
- Munchlax
|
||||
- Finneon, Lumineon
|
||||
- Purrloin, Liepard
|
||||
- Timburr, Gurdurr, Conkeldurr
|
||||
- Joltik, Galvantula
|
||||
- Elgyem, Beheeyem
|
||||
- Golett, Golurk
|
||||
- Deino, Zweilous, Hydreigon
|
||||
- Flabébé, Floette, Florges
|
||||
- Clauncher, Clawitzer
|
||||
- Mareanie, Toxapex
|
||||
- Wimpod, Golisopod
|
||||
- Bruxish
|
||||
- Hatenna, Hattrem, Hatterene
|
||||
- Pincurchin
|
||||
- Klawf
|
||||
- Finizen, Palafin \[Zero\], Palafin \[Hero\]
|
||||
- Dondozo
|
||||
|
||||
### Added shoulder mounts for the following Pokémon
|
||||
- Weedle
|
||||
- Caterpie
|
||||
- Spearow
|
||||
- Mew
|
||||
- Murkrow
|
||||
- Smoochum
|
||||
- Larvitar
|
||||
- Tailow
|
||||
- Plusle
|
||||
- Minun
|
||||
- Beldum
|
||||
- Starly
|
||||
- Buneary
|
||||
- Combee
|
||||
- Pachirisu
|
||||
- Tepig
|
||||
- Pidove
|
||||
- Petilil, Hisui Bias Petilil
|
||||
- Zorua, Hisui Bias Zorua
|
||||
- Elgyem
|
||||
- Fletchling
|
||||
- Skrelp
|
||||
- Klefki
|
||||
- Litten
|
||||
- Fomantis
|
||||
- Morelull
|
||||
- Dreepy
|
||||
- Sprigatito
|
||||
- Shroodle
|
||||
- Tatsugiri
|
||||
- Glimmet
|
||||
- Gimmighoul \[Roaming\]
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
- Bellsprout, Weepinbell, Victreebel
|
||||
- Shellder, Cloyster
|
||||
- Porygon, Porygon2, Porygon-Z
|
||||
- Furret
|
||||
- Swinub, Piloswine, Mamoswine
|
||||
- Skarmory
|
||||
- Tyrogue
|
||||
- Spinda
|
||||
- Torkoal
|
||||
- Nincada, Ninjask
|
||||
- Lunatone
|
||||
- Solrock
|
||||
- Buneary, Lopunny
|
||||
- Magnezone
|
||||
- Alomomola
|
||||
- Tepig, Pignite, Emboar
|
||||
- Flabebe, Floette, Florges
|
||||
- Litten, Torracat, Incineroar
|
||||
- Fomantis, Lurantis
|
||||
- Dreepy, Drakloak
|
||||
- Kleavor
|
||||
- Scorbunny
|
||||
- Gimmighoul \[Roaming\]
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Bulbasaur, Ivysaur, Venusaur
|
||||
- Charmander, Charmeleon, Charizard
|
||||
- Kakuna, Beedrill
|
||||
- Lapras
|
||||
- Porygon, Porygon2, Porygon-Z
|
||||
- Igglybuff, Jigglypuff, Wigglytuff
|
||||
- Poliwhirl, Poliwrath
|
||||
- Bellsprout, Weepinbell, Victreebel
|
||||
- Shellder, Cloyster
|
||||
- Gastly, Haunter
|
||||
- Onix
|
||||
- Krabby, Kingler
|
||||
- Rhyhorn, Rhydon, Rhyperior
|
||||
- Horsea, Seadra, Kingdra
|
||||
- Goldeen, Seaking
|
||||
- Magby, Magmar, Magmortar
|
||||
- Magikarp, Gyarados
|
||||
- Munchlax, Snorlax
|
||||
- Swinub, Piloswine, Mamoswine
|
||||
- Mudkip, Marshtomp, Swampert
|
||||
- Baltoy, Claydol
|
||||
- Carvanha
|
||||
- Relicanth
|
||||
- Prinplup, Empoleon
|
||||
- Gible, Gabite, Garchomp
|
||||
- Riolu, Lucario
|
||||
- Snivy, Servine, Serperior
|
||||
- Tepig, Pignite, Emboar
|
||||
- Krookodile
|
||||
- Dwebble, Crustle
|
||||
- Joltik, Galvantula
|
||||
- Klink, Klang, Klinglang
|
||||
- Golett, Golurk
|
||||
- Fennekin, Braixen, Delphox
|
||||
- Phantump, Trevenant
|
||||
- Litten, Torracat, Incineroar
|
||||
- Popplio, Brionne, Primarina
|
||||
- Lurantis
|
||||
- Scorbunny, Cinderace
|
||||
- Quaxly
|
||||
- Tatsugiri
|
||||
|
||||
### Changes
|
||||
- Buffed Dusk Ball catch multipliers: Increased multiplier to 3.5 (from 3.0) in light level 0. Increased multiplier to 3.0 (from 1.5) in light levels 1-7.
|
||||
@@ -292,7 +97,9 @@
|
||||
- Adjusted some berry balance values like yield and growth times.
|
||||
- Adjusted volumes of sounds made by Display Cases, Berry Bushes, Energy Root, Medicinal Leek, Vivichoke, Mints, Revival Herbs and Gilded Chests.
|
||||
- Changes to mulch buffs/durations.
|
||||
- Dreepy is now shoulder mountable.
|
||||
- Updated potion sprites.
|
||||
- Weedle, Caterpie, Spearow, Smoochum, Mew, Murkrow, Larvitar, Taillow, Plusle, Minun, Beldum, Starly, Buneary, Combee, Pachirisu, Pidove, Petilil, Hisui Bias Petilil, Zorua, Hisuian Zorua, Elgyem, Fletchling, Skrelp, Klefki, Fomantis, Morelull, Dreepy, Shroodle, Tatsugiri, Glimmet, and Roaming Gimmighoul are now shoulder mountable.
|
||||
- Link Cable recipe has been redesigned.
|
||||
- Updated sounds for Medicinal Leeks, Big Roots, Energy Roots and Revival Herbs.
|
||||
- Updated UI sounds for clicking and evolving Pokémon.
|
||||
@@ -310,26 +117,16 @@
|
||||
- Updated dialogue GUI assets with proper assets.
|
||||
- Updated party switching interface when in battle.
|
||||
- Relic coin pouches can now be waterlogged.
|
||||
- Pokémon name and level labels are now only visible when crouching by default. Set the `displayEntityLabelsWhenCrouchingOnly` config to `false` to always have them displayed.
|
||||
- Pokemon Name (and level) labels are now only visible when crouching by default, set the `displayEntityLabelsWhenCrouchingOnly` to `false` to always them even when not crouching
|
||||
- The "Press R to start battle prompt" will now disappear after a player's first battle has been won.
|
||||
- The default number of digits for the Pokédex number in the summary has been increased, from 3 to 4.
|
||||
- The pasture block model's screen is now off by default.
|
||||
- The Poké Balls creative tab is now named Utility Items as it also encompasses Poké Rod and Pokédex items.
|
||||
- Moves can now be benched without specifying a replacement move, allowing for empty move slots.
|
||||
- Moves learned via Sketch now persist after battle.
|
||||
- Updated potion sprites
|
||||
- Removed the interchangeable evolution results for the first stage Hisuian starters to prevent confusion. The method still exists for stage 2 to 3, but stage 1 to 2 was not distinguishable enough for many users.
|
||||
- Hidden Power now displays its effective typing.
|
||||
- Updated particles for moves: Confusion, Protect, Sand Attack, and Quick Attack.
|
||||
- Updated particles for status effects: Paralysis, Poison, and Sleep.
|
||||
- Updated particles on Gastly.
|
||||
- Revamped stat buff and de-buff particles.
|
||||
- Improved the performance of display cases that contain Pokémon photos.
|
||||
|
||||
### Fixes
|
||||
- Scaled down Amaura's fetus model to avoid clipping through the tank while animating.
|
||||
- Fixed Cubone's cry not having a sound.
|
||||
- Fixed the sendout sound erroneously playing when a wild Pokémon breaks out of a Poké ball.
|
||||
- Fixed the sendout sound erronously playing when a wild Pokémon breaks out of a Poké ball.
|
||||
- Flamethrower is no longer missing sounds.
|
||||
- Fixed the Seafloor spawning context not being a usable context.
|
||||
- Fixed Pokemon spawning in non-full blocks like slabs.
|
||||
@@ -337,6 +134,11 @@
|
||||
- Fixed Pokémon losing their Hidden Ability through evolution if the middle stage did not have a Hidden Ability.
|
||||
- Hidden Power no longer plays the water type action effect (It now plays the normal type action effect).
|
||||
- Fixed Chimchar and Monferno T-posing whenever they sleep.
|
||||
- Fixed the Magby line not having any placeholder walk animations.
|
||||
- Fixed Duskull and Dusclops using skylight levels for their nether spawn data. There is no sun in the Nether!
|
||||
- Fixed Hisuian Zoroark using base Zoroark stats.
|
||||
- Fixed Bellossom clipping into the player head when shoulder mounted.
|
||||
- Fixed Shroomish's look range to prevent it from looking higher than it should.
|
||||
- Fixed Cetitan's cry breaking its walk and sleep animations.
|
||||
- Fixed Crumbling Arch not blending in with the world.
|
||||
- Fixed Energy Root applying being usable on a fainted Pokémon.
|
||||
@@ -375,7 +177,6 @@
|
||||
- Fixed Slowking's battle idle.
|
||||
- Fixed Grafaiai's walk speed to prevent model sliding.
|
||||
- Fixed Alolan Exeggutor's tail not showing in the party UI. It will now also stand in front of all Pokémon in the party menu :)
|
||||
- Fixed Timburr duplicating its log while fainting.
|
||||
- Fixed reviving items throwing an exception when used in battle.
|
||||
- Fixed messages for Focus Sash, Confusion, Mummy, Ice Face, Own Tempo, and Revive.
|
||||
- Improve error handling when loading spawn-sets to ensure invalid configurations don't crash the server.
|
||||
@@ -392,16 +193,6 @@
|
||||
- Fixed village generation caps not applying for berry farms. You were not meant to see more than two berry farms per village!
|
||||
- Fixed the Ice Face ability activation being displayed incorrectly in a battle.
|
||||
- Fixed edge case of F1 and R locking you in battle with no R functionality (requiring ESC to get out).
|
||||
- Fixed Poké Ball render orientation in battle interface when capturing.
|
||||
- Fixed sync issues with recently learned moves where their PP would not go down until you logout and in.
|
||||
- Fixed some Pokémon photos in display cases flashing if the Pokémon has gender differences.
|
||||
- Fixed Bidoof and Jigglypuff sleep animations stopping after some time, causing them to be 'asleep' while T-posing menacingly.
|
||||
- Fixed entities not changing poses unless you're looking at them.
|
||||
- Fixed cries not playing on send-out if the Pokémon is off-screen.
|
||||
- Fixed text row selection highlight in battle log.
|
||||
- Fixed top black border rendering in scroll interfaces in summary UI.
|
||||
- Fixed aspect tracking for Advancements.
|
||||
- Fixed illusion not copying aspects or caught ball.
|
||||
|
||||
### Developer
|
||||
- `SpawnCause` is now an implementation of `SpawningInfluence`.
|
||||
@@ -432,12 +223,6 @@
|
||||
- Pokémon Heal (with context)
|
||||
- Added HealingSource, an interface applied to all sources of healing from player actions, for easier tracking of healing sources.
|
||||
- Move Change
|
||||
- Shiny Chance Calculation Event (with player context)
|
||||
- Rebuilt large swaths of the model animation code to simplify it.
|
||||
- Renamed a bunch of things from %Poseable% to %Posable% because spelling.
|
||||
- Renamed StatelessAnimation to PoseAnimation.
|
||||
- Renamed StatefulAnimation to ActiveAnimation.
|
||||
- Documented the animation system.
|
||||
|
||||
### Data Pack & Resource Pack Creators
|
||||
- Added support for "shedders" similar to Shedinja's evolution logic.
|
||||
@@ -454,11 +239,6 @@
|
||||
- Sound events for all blocks now start with "block."
|
||||
- Gimmighoul chest and item interaction sounds have been moved to where its cry is.
|
||||
- Unused sound files and sound events have been removed.
|
||||
- Added MoLang compatibility in the isVisible property for transformed parts.
|
||||
- Added q.has_aspect('some_aspect') function to animations, posers, and entity particle effects.
|
||||
- Added support for conditional pose animations.
|
||||
- Added a new universal locator called "top".
|
||||
- Added `eggs_collected` and `eggs_hatched` Advancement triggers.
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.client.render.ClientPlayerIcon;
|
||||
import com.cobblemon.mod.common.client.CobblemonClient;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
@@ -20,9 +20,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(PlayerRenderer.class)
|
||||
public class PlayerRendererMixin {
|
||||
|
||||
@Inject(method = "render", at = @At(value = "TAIL"))
|
||||
@Inject(
|
||||
method = "render(Lnet/minecraft/client/player/AbstractClientPlayer;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V",
|
||||
at = @At(value = "TAIL")
|
||||
)
|
||||
public void renderPlayer(AbstractClientPlayer player, float f, float g, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, CallbackInfo ci) {
|
||||
ClientPlayerIcon.Companion.onRenderPlayer(player);
|
||||
CobblemonClient.INSTANCE.getRequests().onRenderPlayer(player, g);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.drop.DropEntry
|
||||
import com.cobblemon.mod.common.api.drop.ItemDropEntry
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
|
||||
import com.cobblemon.mod.common.api.interaction.RequestManager
|
||||
import com.cobblemon.mod.common.api.permission.PermissionValidator
|
||||
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculator
|
||||
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculators
|
||||
@@ -44,7 +43,6 @@ import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
|
||||
import com.cobblemon.mod.common.api.starter.StarterHandler
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStoreManager
|
||||
import com.cobblemon.mod.common.api.storage.adapter.conversions.ReforgedConversion
|
||||
import com.cobblemon.mod.common.api.storage.adapter.database.MongoDBStoreAdapter
|
||||
import com.cobblemon.mod.common.api.storage.adapter.flatfile.FileStoreAdapter
|
||||
import com.cobblemon.mod.common.api.storage.adapter.flatfile.JSONStoreAdapter
|
||||
import com.cobblemon.mod.common.api.storage.adapter.flatfile.NBTStoreAdapter
|
||||
@@ -54,7 +52,8 @@ import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreManager
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.*
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.DexDataNbtBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataJsonBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
@@ -98,7 +97,6 @@ import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.NoAIProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
|
||||
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
|
||||
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
|
||||
@@ -107,10 +105,7 @@ import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
|
||||
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
||||
import com.mongodb.ConnectionString
|
||||
import com.mongodb.MongoClientSettings
|
||||
import com.mongodb.client.MongoClient
|
||||
import com.mongodb.client.MongoClients
|
||||
import java.io.File
|
||||
import java.io.FileReader
|
||||
import java.io.FileWriter
|
||||
@@ -214,7 +209,7 @@ object Cobblemon {
|
||||
BattleRegistry.onPlayerDisconnect(it.player)
|
||||
storage.onPlayerDisconnect(it.player)
|
||||
playerDataManager.onPlayerDisconnect(it.player)
|
||||
RequestManager.onLogoff(it.player)
|
||||
TradeManager.onLogoff(it.player)
|
||||
}
|
||||
PlatformEvents.PLAYER_DEATH.subscribe {
|
||||
PCLinkManager.removeLink(it.player.uuid)
|
||||
@@ -278,7 +273,6 @@ object Cobblemon {
|
||||
FlagSpeciesFeatureProvider(keys = listOf(DataKeys.HAS_BEEN_SHEARED), default = false))
|
||||
|
||||
CustomPokemonProperty.register(UncatchableProperty)
|
||||
CustomPokemonProperty.register(BattleCloneProperty)
|
||||
CustomPokemonProperty.register(PokemonFlagProperty)
|
||||
CustomPokemonProperty.register(HiddenAbilityPropertyType)
|
||||
CustomPokemonProperty.register(AspectPropertyType)
|
||||
@@ -286,8 +280,6 @@ object Cobblemon {
|
||||
CustomPokemonProperty.register(FreezeFrameProperty)
|
||||
CustomPokemonProperty.register(NoAIProperty)
|
||||
|
||||
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit()
|
||||
|
||||
ifDedicatedServer {
|
||||
isDedicatedServer = true
|
||||
}
|
||||
@@ -328,7 +320,9 @@ object Cobblemon {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
"mongodb" -> {
|
||||
/*
|
||||
try {
|
||||
Class.forName("com.mongodb.client.MongoClient")
|
||||
|
||||
@@ -336,22 +330,18 @@ object Cobblemon {
|
||||
.applyConnectionString(ConnectionString(config.mongoDBConnectionString))
|
||||
.build()
|
||||
mongoClient = MongoClients.create(mongoClientSettings)
|
||||
val generalMongoFactory = CachedPlayerDataStoreFactory(PlayerDataMongoBackend(mongoClient, config.mongoDBDatabaseName, "PlayerDataCollection"))
|
||||
generalMongoFactory.setup(server)
|
||||
|
||||
val pokedexMongoFactory = CachedPlayerDataStoreFactory(DexDataMongoBackend(mongoClient, config.mongoDBDatabaseName, "PokeDexCollection"))
|
||||
pokedexMongoFactory.setup(server)
|
||||
|
||||
playerDataManager.setFactory(generalMongoFactory, PlayerInstancedDataStoreTypes.GENERAL)
|
||||
playerDataManager.setFactory(pokedexMongoFactory, PlayerInstancedDataStoreTypes.POKEDEX)
|
||||
val mongoFactory = MongoPlayerDataStoreFactory(mongoClient, config.mongoDBDatabaseName)
|
||||
playerData.setFactory(mongoFactory, PlayerInstancedDataStoreType.GENERAL)
|
||||
MongoDBStoreAdapter(mongoClient, config.mongoDBDatabaseName)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
LOGGER.error("MongoDB driver not found.")
|
||||
throw e
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
else -> throw IllegalArgumentException("Unsupported storageFormat: ${config.storageFormat}")
|
||||
}
|
||||
@@ -388,9 +378,6 @@ object Cobblemon {
|
||||
}
|
||||
|
||||
registerEventHandlers()
|
||||
|
||||
CobblemonEvents.COBBLEMON_INITIALISED.emit()
|
||||
|
||||
}
|
||||
|
||||
fun registerEventHandlers() {
|
||||
|
||||
@@ -155,19 +155,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
|
||||
val pokedexes = mutableListOf<PokedexItem>()
|
||||
@JvmField
|
||||
val POKEDEX_RED = pokedexItem(PokedexTypes.RED)
|
||||
@JvmField
|
||||
val POKEDEX_YELLOW = pokedexItem(PokedexTypes.YELLOW)
|
||||
@JvmField
|
||||
val POKEDEX_GREEN = pokedexItem(PokedexTypes.GREEN)
|
||||
val POKEDEX_BLACK = pokedexItem(PokedexTypes.BLACK)
|
||||
@JvmField
|
||||
val POKEDEX_BLUE = pokedexItem(PokedexTypes.BLUE)
|
||||
@JvmField
|
||||
val POKEDEX_GREEN = pokedexItem(PokedexTypes.GREEN)
|
||||
@JvmField
|
||||
val POKEDEX_PINK = pokedexItem(PokedexTypes.PINK)
|
||||
@JvmField
|
||||
val POKEDEX_BLACK = pokedexItem(PokedexTypes.BLACK)
|
||||
val POKEDEX_RED = pokedexItem(PokedexTypes.RED)
|
||||
@JvmField
|
||||
val POKEDEX_WHITE = pokedexItem(PokedexTypes.WHITE)
|
||||
@JvmField
|
||||
val POKEDEX_YELLOW = pokedexItem(PokedexTypes.YELLOW)
|
||||
|
||||
@JvmField
|
||||
val VIVICHOKE = compostableItem("vivichoke")
|
||||
|
||||
@@ -51,7 +51,13 @@ import com.cobblemon.mod.common.client.net.storage.pc.OpenPCHandler
|
||||
import com.cobblemon.mod.common.client.net.storage.pc.SetPCBoxPokemonHandler
|
||||
import com.cobblemon.mod.common.client.net.storage.pc.SetPCPokemonHandler
|
||||
import com.cobblemon.mod.common.client.net.toast.ToastPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.*
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeAcceptanceChangedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeCancelledHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeCompletedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeOfferExpiredHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeOfferNotificationHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
|
||||
import com.cobblemon.mod.common.net.PacketRegisterInfo
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
@@ -94,10 +100,20 @@ import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyPokemo
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyReferencePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeAcceptanceChangedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeCompletedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferExpiredPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferNotificationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.*
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.RemoveSpectatorPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
|
||||
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.net.messages.server.callback.party.PartyPokemonSelectedPacket
|
||||
@@ -125,7 +141,15 @@ import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePartyPokemonT
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.ReleasePCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.SwapPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.UnlinkPlayerFromPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.AcceptTradeRequestPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.CancelTradePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.ChangeTradeAcceptancePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.OfferTradePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.UpdateTradeOfferPacket
|
||||
import com.cobblemon.mod.common.net.serverhandling.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.BattleSelectActionsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.RemoveSpectatorHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.SpectateBattleHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectedHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSelectedHandler
|
||||
@@ -161,15 +185,10 @@ import com.cobblemon.mod.common.net.serverhandling.trade.CancelTradeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.ChangeTradeAcceptanceHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.OfferTradeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.UpdateTradeOfferHandler
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.*
|
||||
import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedRegisterPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.*
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.*
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
@@ -322,7 +341,6 @@ object CobblemonNetwork {
|
||||
|
||||
// Trade packets
|
||||
list.add(PacketRegisterInfo(TradeAcceptanceChangedPacket.ID, TradeAcceptanceChangedPacket::decode, TradeAcceptanceChangedHandler))
|
||||
list.add(PacketRegisterInfo(TradeProcessStartedPacket.ID, TradeProcessStartedPacket::decode, TradeProcessStartedHandler))
|
||||
list.add(PacketRegisterInfo(TradeCancelledPacket.ID, TradeCancelledPacket::decode, TradeCancelledHandler))
|
||||
list.add(PacketRegisterInfo(TradeCompletedPacket.ID, TradeCompletedPacket::decode, TradeCompletedHandler))
|
||||
list.add(PacketRegisterInfo(TradeUpdatedPacket.ID, TradeUpdatedPacket::decode, TradeUpdatedHandler))
|
||||
@@ -410,7 +428,6 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(AcceptTradeRequestPacket.ID, AcceptTradeRequestPacket::decode, AcceptTradeRequestHandler))
|
||||
list.add(PacketRegisterInfo(CancelTradePacket.ID, CancelTradePacket::decode, CancelTradeHandler))
|
||||
list.add(PacketRegisterInfo(ChangeTradeAcceptancePacket.ID, ChangeTradeAcceptancePacket::decode, ChangeTradeAcceptanceHandler))
|
||||
list.add(PacketRegisterInfo(PerformTradePacket.ID, PerformTradePacket::decode, PerformTradeHandler))
|
||||
list.add(PacketRegisterInfo(OfferTradePacket.ID, OfferTradePacket::decode, OfferTradeHandler))
|
||||
list.add(PacketRegisterInfo(UpdateTradeOfferPacket.ID, UpdateTradeOfferPacket::decode, UpdateTradeOfferHandler))
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
|
||||
@JvmField
|
||||
val GUI_CLICK = this.create("gui.click")
|
||||
@JvmField
|
||||
val GUI_TRADE = this.create("gui.trade")
|
||||
|
||||
@JvmField
|
||||
val PC_ON = this.create("pc.on")
|
||||
|
||||
@@ -29,8 +29,6 @@ object CobblemonCriteria : PlatformRegistry<Registry<CriterionTrigger<*>>, Resou
|
||||
|
||||
val CATCH_SHINY_POKEMON = this.create("catch_shiny_pokemon", SimpleCriterionTrigger(CountableCriterion.CODEC))
|
||||
|
||||
val EGG_COLLECT = this.create("eggs_collected", SimpleCriterionTrigger(CountableCriterion.CODEC))
|
||||
|
||||
val EGG_HATCH = this.create("eggs_hatched", SimpleCriterionTrigger(CountableCriterion.CODEC))
|
||||
|
||||
val EVOLVE_POKEMON = this.create("pokemon_evolved", SimpleCriterionTrigger(EvolvePokemonCriterion.CODEC))
|
||||
@@ -60,4 +58,4 @@ object CobblemonCriteria : PlatformRegistry<Registry<CriterionTrigger<*>>, Resou
|
||||
|
||||
override val registry = BuiltInRegistries.TRIGGER_TYPES
|
||||
override val resourceKey = Registries.TRIGGER_TYPE
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import net.minecraft.nbt.NbtOps
|
||||
* @author Qu
|
||||
* @since January 9th, 2022
|
||||
*/
|
||||
open class Ability internal constructor(var template: AbilityTemplate, forced: Boolean, priority: Priority) {
|
||||
open class Ability internal constructor(var template: AbilityTemplate, forced: Boolean) {
|
||||
|
||||
val name: String
|
||||
get() = template.name
|
||||
@@ -57,7 +57,7 @@ open class Ability internal constructor(var template: AbilityTemplate, forced: B
|
||||
*
|
||||
* @see [Pokemon.updateAbility].
|
||||
*/
|
||||
var priority = priority
|
||||
var priority = Priority.LOWEST
|
||||
internal set
|
||||
|
||||
@Deprecated("Please use the Codec instead", ReplaceWith("Ability.CODEC"))
|
||||
@@ -111,7 +111,7 @@ open class Ability internal constructor(var template: AbilityTemplate, forced: B
|
||||
Codec.BOOL.optionalFieldOf(DataKeys.POKEMON_ABILITY_FORCED, false).forGetter(Ability::forced),
|
||||
Codec.INT.optionalFieldOf(DataKeys.POKEMON_ABILITY_INDEX, -1).forGetter(Ability::index),
|
||||
Priority.CODEC.optionalFieldOf(DataKeys.POKEMON_ABILITY_PRIORITY, Priority.LOWEST).forGetter(Ability::priority)
|
||||
).apply(it) { template, forced, index, priority -> Ability(template, forced, priority).apply {
|
||||
).apply(it) { template, forced, index, priority -> Ability(template, forced).apply {
|
||||
this.index = index
|
||||
this.priority = priority
|
||||
} }
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.cobblemon.mod.common.pokemon.Species
|
||||
*/
|
||||
open class AbilityPool : PrioritizedList<PotentialAbility>() {
|
||||
fun select(species: Species, aspects: Set<String>): Pair<Ability, Priority> {
|
||||
for (priority in Priority.entries) {
|
||||
for (priority in Priority.values()) {
|
||||
val potentialAbilities = priorityMap[priority]?.filter { it.isSatisfiedBy(aspects) } ?: continue
|
||||
if (potentialAbilities.isNotEmpty()) {
|
||||
return potentialAbilities.random().template.create() to priority
|
||||
@@ -31,7 +31,7 @@ open class AbilityPool : PrioritizedList<PotentialAbility>() {
|
||||
}
|
||||
|
||||
LOGGER.error("Unable to select an ability from the pool for $species and aspects: ${aspects.joinToString()}")
|
||||
LOGGER.error("Please show this to the Cobblemon developers!")
|
||||
LOGGER.error("Usually this happens when a client is doing logic it shouldn't. Please show this to the Cobblemon developers!")
|
||||
Exception().printStackTrace()
|
||||
return Abilities.first().create() to Priority.LOWEST
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.abilities
|
||||
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.util.codec.CodecUtils
|
||||
import com.google.gson.JsonObject
|
||||
import com.mojang.serialization.Codec
|
||||
@@ -22,14 +21,14 @@ import net.minecraft.nbt.CompoundTag
|
||||
*/
|
||||
class AbilityTemplate(
|
||||
val name: String = "",
|
||||
var builder: (AbilityTemplate, forced: Boolean, priority: Priority) -> Ability = { template, forced, priority -> Ability(template, forced, priority) },
|
||||
var builder: (AbilityTemplate, forced: Boolean) -> Ability = { template, forced -> Ability(template, forced) },
|
||||
val displayName: String = "cobblemon.ability.$name",
|
||||
val description: String = "cobblemon.ability.$name.desc"
|
||||
) {
|
||||
/**
|
||||
* Returns the Ability or if applicable the extension connected to this template
|
||||
*/
|
||||
fun create(forced: Boolean = false, priority: Priority = Priority.LOWEST) = builder(this, forced, priority)
|
||||
fun create(forced: Boolean = false) = builder(this, forced)
|
||||
|
||||
/**
|
||||
* Returns the Ability and loads the given NBT Tag into it.
|
||||
|
||||
-19
@@ -1,19 +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.api.battles.interpreter
|
||||
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
|
||||
/**
|
||||
* An exception thrown when properties from a [BattleMessage] cannot be interpreted for an [InterpreterInstruction].
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since November 10th, 2024
|
||||
*/
|
||||
class InvalidInstructionException(battleMessage: BattleMessage) : Exception("Failed to interpret ${battleMessage.rawMessage}")
|
||||
@@ -25,7 +25,6 @@ import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
@@ -84,8 +83,6 @@ open class PokemonBattle(
|
||||
|
||||
val onEndHandlers: MutableList<(PokemonBattle) -> Unit> = mutableListOf()
|
||||
|
||||
val battlePartyStores = mutableListOf<PlayerPartyStore>()
|
||||
|
||||
init {
|
||||
side1.battle = this
|
||||
side2.battle = this
|
||||
@@ -374,13 +371,6 @@ open class PokemonBattle(
|
||||
|
||||
}
|
||||
|
||||
fun dispatchWaitingToFront(delaySeconds: Float = 1F, dispatcher: () -> Unit) {
|
||||
dispatches.addFirst(BattleDispatch {
|
||||
dispatcher()
|
||||
WaitDispatch(delaySeconds)
|
||||
})
|
||||
}
|
||||
|
||||
fun dispatchGo(dispatcher: () -> Unit) {
|
||||
dispatch {
|
||||
dispatcher()
|
||||
|
||||
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.data
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
|
||||
// TODO this is implemented in multiple places but unused. Can we scratch this?
|
||||
/**
|
||||
* Represents an object that will possibly be synchronized to the client during datapack synchronization.
|
||||
*
|
||||
|
||||
@@ -11,12 +11,11 @@ package com.cobblemon.mod.common.api.drop
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.LOOT_DROPPED
|
||||
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* A table of drops that can produce a list of [DropEntry]. You can produce a drop list from [getDrops] to
|
||||
@@ -97,14 +96,4 @@ class DropTable {
|
||||
ifSucceeded = { it.drops.forEach { it.drop(entity, world, pos, player) } }
|
||||
)
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeInt(this.entries.size)
|
||||
this.entries.filterIsInstance<ItemDropEntry>().forEach { it.encode(buffer) }
|
||||
}
|
||||
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
val entries = buffer.readInt()
|
||||
repeat(entries) { this.entries.add(ItemDropEntry().decode(buffer)) }
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,9 @@ import com.cobblemon.mod.common.util.toBlockPos
|
||||
import net.minecraft.core.component.DataComponentMap
|
||||
import net.minecraft.core.component.DataComponentPatch
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
@@ -34,12 +33,12 @@ import net.minecraft.world.phys.Vec3
|
||||
* @since July 24th, 2022
|
||||
*/
|
||||
open class ItemDropEntry : DropEntry {
|
||||
override var percentage = 100F
|
||||
override var quantity = 1
|
||||
open var quantityRange: IntRange? = null
|
||||
override var maxSelectableTimes = 1
|
||||
override val percentage = 100F
|
||||
override val quantity = 1
|
||||
open val quantityRange: IntRange? = null
|
||||
override val maxSelectableTimes = 1
|
||||
open val dropMethod: ItemDropMethod? = null
|
||||
open var item = ResourceLocation.parse("minecraft:fish")
|
||||
open val item = ResourceLocation.parse("minecraft:fish")
|
||||
open val components: DataComponentMap? = null
|
||||
|
||||
override fun drop(entity: LivingEntity?, world: ServerLevel, pos: Vec3, player: ServerPlayer?) {
|
||||
@@ -77,19 +76,4 @@ open class ItemDropEntry : DropEntry {
|
||||
world.addFreshEntity(ItemEntity(world, pos.x, pos.y, pos.z, stack))
|
||||
}
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeFloat(this.percentage)
|
||||
buffer.writeVarInt(this.quantity)
|
||||
buffer.writeResourceLocation(this.item)
|
||||
buffer.writeNullable(this.quantityRange) { _, it -> buffer.writeVarInt(it.first); buffer.writeVarInt(it.last) }
|
||||
}
|
||||
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): ItemDropEntry {
|
||||
this.percentage = buffer.readFloat()
|
||||
this.quantity = buffer.readVarInt()
|
||||
this.item = buffer.readResourceLocation()
|
||||
this.quantityRange = buffer.readNullable { buffer.readVarInt()..buffer.readVarInt() }
|
||||
return this
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,11 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.entity.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.effects.IllusionEffect
|
||||
import com.cobblemon.mod.common.entity.pokemon.effects.TransformEffect
|
||||
import com.cobblemon.mod.common.pokeball.PokeBall
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
@@ -97,7 +95,4 @@ interface MocKEffect : PhysicalEffect {
|
||||
get() = this.mock.form?.let {
|
||||
formID -> this.exposedSpecies?.forms?.firstOrNull { it.formOnlyShowdownId().equals(formID, true) } }
|
||||
?: this.exposedSpecies?.standardForm
|
||||
|
||||
val exposedBall: PokeBall?
|
||||
get() = this.mock.pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace()) }
|
||||
}
|
||||
|
||||
@@ -54,10 +54,6 @@ import net.minecraft.server.level.ServerPlayer
|
||||
@Suppress("unused")
|
||||
object CobblemonEvents {
|
||||
|
||||
@JvmField
|
||||
val POKEMON_PROPERTY_INITIALISED = SimpleObservable<Unit>()
|
||||
@JvmField
|
||||
val COBBLEMON_INITIALISED = SimpleObservable<Unit>()
|
||||
@JvmField
|
||||
val DATA_SYNCHRONIZED = SimpleObservable<ServerPlayer>()
|
||||
@JvmField
|
||||
@@ -133,8 +129,6 @@ object CobblemonEvents {
|
||||
val POKEMON_ENTITY_SAVE_TO_WORLD = CancelableObservable<PokemonEntitySaveToWorldEvent>()
|
||||
@JvmField
|
||||
val ENTITY_SPAWN = CancelableObservable<SpawnEvent<*>>()
|
||||
@JvmField
|
||||
val SHINY_CHANCE_CALCULATION = EventObservable<ShinyChanceCalculationEvent>()
|
||||
|
||||
@JvmField
|
||||
val POKEMON_ENTITY_SPAWN = ENTITY_SPAWN
|
||||
|
||||
-57
@@ -1,57 +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.api.events.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
* An event to modify the shiny chance of a Pokémon.
|
||||
*/
|
||||
class ShinyChanceCalculationEvent(
|
||||
val baseChance: Float,
|
||||
val pokemon: Pokemon
|
||||
) {
|
||||
val chance: Float = baseChance
|
||||
var isShiny: Boolean = false
|
||||
private val modifiers = mutableListOf<Float>()
|
||||
private val modificationFunctions = mutableListOf<(Float, ServerPlayer?, Pokemon) -> Float>()
|
||||
|
||||
/**
|
||||
* Adds a modifier to the shiny chance.
|
||||
*/
|
||||
fun addModifier(modifier: Float) {
|
||||
modifiers.add(modifier)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a function to modify the shiny chance.
|
||||
*/
|
||||
fun addModificationFunction(function: (Float, ServerPlayer?, Pokemon) -> Float) {
|
||||
modificationFunctions.add(function)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the shiny chance of a Pokémon.
|
||||
*/
|
||||
fun calculate(player: ServerPlayer?): Float {
|
||||
var result = baseChance
|
||||
for (modifier in modifiers) {
|
||||
result += modifier
|
||||
}
|
||||
for (function in modificationFunctions) {
|
||||
result = function(result, player, pokemon)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun isShiny(player: ServerPlayer?): Boolean {
|
||||
return calculate(player) >= chance || isShiny
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.item.Item
|
||||
@@ -88,8 +87,7 @@ data class FishingBait(
|
||||
val BITE_TIME = cobblemonResource("bite_time")
|
||||
val GENDER_CHANCE = cobblemonResource("gender_chance")
|
||||
val LEVEL_RAISE = cobblemonResource("level_raise")
|
||||
val TYPING = cobblemonResource("typing")
|
||||
val EGG_GROUP = cobblemonResource("egg_group")
|
||||
val TERA = cobblemonResource("tera")
|
||||
val SHINY_REROLL = cobblemonResource("shiny_reroll")
|
||||
val HIDDEN_ABILITY_CHANCE = cobblemonResource("ha_chance")
|
||||
val POKEMON_CHANCE = cobblemonResource("pokemon_chance")
|
||||
@@ -110,6 +108,7 @@ data class FishingBait(
|
||||
EFFECT_FUNCTIONS[SHINY_REROLL] = { entity, effect -> FishingSpawnCause.shinyReroll(entity, effect) }
|
||||
EFFECT_FUNCTIONS[GENDER_CHANCE] = { entity, effect -> FishingSpawnCause.alterGenderAttempt(entity, effect) }
|
||||
EFFECT_FUNCTIONS[LEVEL_RAISE] = { entity, effect -> FishingSpawnCause.alterLevelAttempt(entity, effect) }
|
||||
EFFECT_FUNCTIONS[TERA] = { entity, effect -> FishingSpawnCause.alterTeraAttempt(entity, effect) }
|
||||
EFFECT_FUNCTIONS[HIDDEN_ABILITY_CHANCE] = { entity, _ -> FishingSpawnCause.alterHAAttempt(entity) }
|
||||
EFFECT_FUNCTIONS[FRIENDSHIP] = { entity, effect -> FishingSpawnCause.alterFriendshipAttempt(entity, effect) }
|
||||
CobblemonEvents.BAIT_EFFECT_REGISTRATION.post(BaitEffectFunctionRegistryEvent()) { event ->
|
||||
|
||||
-106
@@ -1,106 +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.api.interaction
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.battles.TeamManager
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Represents an interaction request between players.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since September 3rd, 2024
|
||||
*/
|
||||
interface PlayerActionRequest {
|
||||
/** The unique ID of this request. */
|
||||
val requestID: UUID
|
||||
|
||||
/** The unique ID of the party that's initiating this request. */
|
||||
val senderID: UUID
|
||||
|
||||
/** The amount of seconds this request is valid for. */
|
||||
val expiryTime: Int
|
||||
}
|
||||
|
||||
/**
|
||||
* An outbound [PlayerActionRequest].
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since September 29th, 2024
|
||||
*/
|
||||
interface ServerPlayerActionRequest : PlayerActionRequest {
|
||||
|
||||
/** The subkey identifying lang entries associated with this request. */
|
||||
val key: String
|
||||
|
||||
/** The player initiating this interaction request. */
|
||||
val sender: ServerPlayer
|
||||
|
||||
/** The player that's the target of this interaction request. */
|
||||
val receiver: ServerPlayer
|
||||
|
||||
/** The unique ID of the party that's the target of this request. */
|
||||
val receiverID: UUID get() = receiver.uuid
|
||||
|
||||
override val senderID: UUID get() = sender.uuid
|
||||
|
||||
/** Sends [packet] to sending party of this request. */
|
||||
fun sendToSender(packet: NetworkPacket<*>) = sender.sendPacket(packet)
|
||||
|
||||
/** Sends [packet] to receiving party of this request. */
|
||||
fun sendToReceiver(packet: NetworkPacket<*>) = receiver.sendPacket(packet)
|
||||
|
||||
/** Notifies the sending party of this request about [langKey]. */
|
||||
fun notifySender(error: Boolean, langKey: String, vararg params: Any) = notify(sender, error, "$key.$langKey", *params)
|
||||
|
||||
/** Notifies the receiving party of this request about [langKey]. */
|
||||
fun notifyReceiver(error: Boolean, langKey: String, vararg params: Any) = notify(receiver, error, "$key.$langKey", *params)
|
||||
|
||||
/** System message to inform individual [player] about [langKey]. */
|
||||
fun notify(player: ServerPlayer, error: Boolean, langKey: String, vararg params: Any) {
|
||||
val lang = lang(langKey, *params).apply { if (error) red() else yellow() }
|
||||
player.sendSystemMessage(lang, false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An outbound [PlayerActionRequest] made on behalf of a team.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since October 29th, 2024
|
||||
*/
|
||||
interface ServerTeamActionRequest : ServerPlayerActionRequest {
|
||||
|
||||
/** The team initiating this request. */
|
||||
val senderTeam: TeamManager.MultiBattleTeam
|
||||
|
||||
/** The team that's the target of this request. */
|
||||
val receiverTeam: TeamManager.MultiBattleTeam
|
||||
|
||||
override val senderID: UUID get() = senderTeam.teamID
|
||||
|
||||
override val receiverID: UUID get() = receiverTeam.teamID
|
||||
|
||||
override fun sendToSender(packet: NetworkPacket<*>) = CobblemonNetwork.sendPacketToPlayers(senderTeam.teamPlayers, packet)
|
||||
|
||||
override fun sendToReceiver(packet: NetworkPacket<*>) = CobblemonNetwork.sendPacketToPlayers(receiverTeam.teamPlayers, packet)
|
||||
|
||||
override fun notifySender(error: Boolean, langKey: String, vararg params: Any) =
|
||||
senderTeam.teamPlayers.forEach { this.notify(it, error, "$key.$langKey", *params) }
|
||||
|
||||
override fun notifyReceiver(error: Boolean, langKey: String, vararg params: Any) =
|
||||
receiverTeam.teamPlayers.forEach { this.notify(it, error, "$key.$langKey", *params) }
|
||||
}
|
||||
@@ -1,230 +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.api.interaction
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
import com.cobblemon.mod.common.api.text.aqua
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.TeamManager
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Defines a manager for organizing active interaction requests between parties. Responsible for dispatching requests to clients,
|
||||
* notifying senders and receivers of requests, and handling their acceptance, expiration, or cancellation.
|
||||
*
|
||||
* Implementations can manage either PLAYER-to-PLAYER requests or TEAM-to-TEAM requests.
|
||||
*
|
||||
* @param T The type of interaction request this manager tracks.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since October 26th, 2024
|
||||
*/
|
||||
abstract class RequestManager<T : ServerPlayerActionRequest> {
|
||||
|
||||
/**
|
||||
* Tracking of the active [ServerPlayerActionRequest]s by their sender.
|
||||
*
|
||||
* NOTE: only 1 request can be sent by a sender at a time.
|
||||
*/
|
||||
private val outboundRequests = mutableMapOf<UUID, T>()
|
||||
|
||||
/**
|
||||
* Tracking of the active [ServerPlayerActionRequest]s by their recipient.
|
||||
*
|
||||
* NOTE: a recipient can receive multiple requests from different senders at the same time.
|
||||
*/
|
||||
private val inboundRequests = mutableMapOf<UUID, MutableList<T>>()
|
||||
|
||||
// we use the term 'sender' because BattleChallenges can be sent TEAM-to-TEAM instead of PLAYER-to-PLAYER for multi battles
|
||||
|
||||
/** Queries the latest pending request that was sent by [senderID]. */
|
||||
fun getOutboundRequest(senderID: UUID) = outboundRequests.get(senderID)
|
||||
|
||||
/** Queries the pending request of [requestID] that was sent by [senderID]. */
|
||||
fun getOutboundRequest(senderID: UUID, requestID: UUID) = this.getOutboundRequest(senderID)?.takeIf { it.requestID == requestID }
|
||||
|
||||
/** Queries the pending request that was sent by [senderID] and received by [receiverID]. */
|
||||
fun getOutboundRequestByRecipient(senderID: UUID, receiverID: UUID) = this.getOutboundRequest(senderID)?.takeIf { it.receiverID == receiverID }
|
||||
|
||||
/** Queries the pending request of [requestID] that was sent by [sender] or their team. */
|
||||
fun getOutboundRequest(sender: ServerPlayer, requestID: UUID) = TeamManager.getTeam(sender)?.let { this.getOutboundRequest(it.teamID, requestID) }
|
||||
?: this.getOutboundRequest(sender.uuid, requestID)
|
||||
|
||||
/** Queries all pending requests that were received by [receiverID]. */
|
||||
fun getInboundRequests(receiverID: UUID) = inboundRequests.get(receiverID)
|
||||
|
||||
/** Queries the pending request of [requestID] that was received by [receiverID]. */
|
||||
fun getInboundRequest(receiverID: UUID, requestID: UUID) = this.getInboundRequests(receiverID)?.find { it.requestID == requestID }
|
||||
|
||||
/** Queries the pending request that was sent by [senderID] and received by [receiverID]. */
|
||||
fun getInboundRequestBySender(receiverID: UUID, senderID: UUID) = this.getInboundRequests(receiverID)?.find { it.senderID == senderID }
|
||||
|
||||
/** Queries the pending request of [requestID] that was received by [receiver] or their team. */
|
||||
fun getInboundRequest(receiver: ServerPlayer, requestID: UUID) = TeamManager.getTeam(receiver)?.let { this.getInboundRequest(it.teamID, requestID) }
|
||||
?: this.getInboundRequest(receiver.uuid, requestID)
|
||||
|
||||
/** Determines whether [player] can receive a [T]. */
|
||||
open fun isBusy(player: ServerPlayer): Boolean = player.isInBattle() || player.isTrading()
|
||||
|
||||
/** Determines if the [target] is a valid interaction request target. */
|
||||
abstract fun isValidInteraction(player: ServerPlayer, target: ServerPlayer): Boolean
|
||||
|
||||
/** Determines whether [request] response is valid and can be accepted. If invalid, notifies the sender and receiver of the request why. */
|
||||
protected abstract fun canAccept(request: T): Boolean
|
||||
|
||||
/** Creates a packet for notifying a recipient about a received request. */
|
||||
protected abstract fun notificationPacket(request: T): NetworkPacket<*>
|
||||
|
||||
/** Creates a packet for canceling received requests. */
|
||||
protected abstract fun expirationPacket(request: T): NetworkPacket<*>
|
||||
|
||||
protected fun addRequest(request: T) {
|
||||
outboundRequests.put(request.senderID, request)
|
||||
inboundRequests.computeIfAbsent(request.receiverID) { mutableListOf() }.add(request)
|
||||
request.sendToReceiver(this.notificationPacket(request))
|
||||
}
|
||||
|
||||
protected fun removeRequest(request: T): Boolean {
|
||||
val inbound = inboundRequests.get(request.receiverID)
|
||||
val removeOutbound = outboundRequests.remove(request.senderID, request)
|
||||
val removeInbound = inbound?.remove(request) == true
|
||||
if (removeInbound && inbound?.size == 0) inboundRequests.remove(request.receiverID, inbound)
|
||||
if (removeInbound != removeOutbound)
|
||||
Cobblemon.LOGGER.error("RequestManager: PlayerActionRequest desync") // USE ADDREQUEST AND REMOVEREQUEST
|
||||
|
||||
val isRemoved = removeInbound && removeOutbound
|
||||
if (isRemoved) request.sendToReceiver(this.expirationPacket(request))
|
||||
return isRemoved
|
||||
}
|
||||
|
||||
/** Notifies and removes an outbound request [T] sent by [ServerPlayerActionRequest.sender] to [ServerPlayerActionRequest.receiver]. */
|
||||
open fun cancelRequest(request: T, expired: Boolean = false) {
|
||||
if (!this.removeRequest(request)) return
|
||||
// canceled by expiration
|
||||
if (expired) {
|
||||
request.notifySender(true, "expired.sender", request.receiver.name.copy().aqua())
|
||||
request.notifyReceiver(true, "expired.receiver", request.sender.name.copy().aqua())
|
||||
}
|
||||
// canceled by sender
|
||||
else {
|
||||
request.notifySender(true, "canceled.sender", request.receiver.name.copy().aqua())
|
||||
request.notifyReceiver(true, "canceled.receiver", request.sender.name.copy().aqua())
|
||||
}
|
||||
// receivers DECLINE, not cancel
|
||||
}
|
||||
|
||||
/** Hook into [sendRequest] immediately after the [request] is sent and added to the manager. Default behavior is to notify parties. */
|
||||
protected open fun onSend(request: T) {
|
||||
request.notifySender(false, "sent", request.receiver.name.copy().aqua())
|
||||
request.notifyReceiver(false, "received", request.sender.name.copy().aqua())
|
||||
}
|
||||
|
||||
/** Sends an outbound request [T] from [ServerPlayerActionRequest.sender] to [ServerPlayerActionRequest.receiver]. */
|
||||
fun sendRequest(request: T): Boolean {
|
||||
val existingRequest = this.getOutboundRequest(request.senderID)
|
||||
val pendingRequest = this.getInboundRequestBySender(request.senderID, request.receiverID)
|
||||
|
||||
// old request
|
||||
if (existingRequest != null && existingRequest.receiverID != request.receiverID)
|
||||
this.cancelRequest(existingRequest)
|
||||
// if sender already has a request awaiting a response from the receiver they're trying to send to.
|
||||
if (existingRequest != null && existingRequest.receiverID == request.receiverID)
|
||||
request.notify(request.sender, true, "${request.key}.error.duplicate", request.receiver.name.copy().aqua())
|
||||
// if sender already has a pending request from the receiver they're trying to send to.
|
||||
else if (pendingRequest != null)
|
||||
request.notify(request.sender, true, "${request.key}.error.pending", request.receiver.name.copy().aqua())
|
||||
// verify sending player can interact with target player
|
||||
else if (!this.isValidInteraction(request.sender, request.receiver))
|
||||
request.notify(request.sender, true, "ui.interact.failed")
|
||||
else if (this.isBusy(request.receiver)) // TODO call to isBusy when setting up the interaction packet
|
||||
request.notify(request.sender, true, "ui.interact.unavailable")
|
||||
// new request
|
||||
else {
|
||||
this.addRequest(request)
|
||||
afterOnServer(seconds = request.expiryTime.toFloat()) { this.cancelRequest(request, true) }
|
||||
this.onSend(request)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/** Hook into [acceptRequest] immediately after the [request] is accepted and removed from the manager. Default behavior is to notify parties. */
|
||||
protected open fun onAccept(request: T) {
|
||||
request.notifySender(false, "accept.sender", request.receiver.name.copy().aqua())
|
||||
request.notifyReceiver(false, "accept.receiver", request.sender.name.copy().aqua())
|
||||
}
|
||||
|
||||
/** Accepts a pending inbound [requestID] for [player]. */
|
||||
fun acceptRequest(player: ServerPlayer, requestID: UUID, target: ServerPlayer? = null): Boolean {
|
||||
var accepted = false
|
||||
val request = this.getInboundRequest(player, requestID)
|
||||
|
||||
// verify request being responded to still valid
|
||||
if (request == null)
|
||||
player.sendSystemMessage(lang("ui.interact.request_already_expired").red(), false)
|
||||
// verify accepting player can respond to sending player
|
||||
else if (!this.isValidInteraction(player, target ?: request.sender)) // with teams anyone can accept
|
||||
request.notify(player, true, "ui.interact.failed")
|
||||
// if sending player is occupied, can't accept response
|
||||
else if (this.isBusy(request.sender)) // TODO enhancement: allow retries - modify client so we don't remove request when sending acceptance
|
||||
request.notify(player, true, "ui.interact.unavailable")
|
||||
// if no condition is blocking acceptance, accept
|
||||
else if (this.canAccept(request))
|
||||
accepted = true
|
||||
|
||||
request?.let {
|
||||
this.removeRequest(it)
|
||||
if (accepted) this.onAccept(request) // order matters
|
||||
}
|
||||
return accepted
|
||||
}
|
||||
|
||||
/** Hook into [declineRequest] immediately after the [request] is declined and removed from the manager. Default behavior is to notify parties. */
|
||||
protected open fun onDecline(request: T) {
|
||||
request.notifySender(true, "decline.sender", request.receiver.name.copy().aqua())
|
||||
request.notifyReceiver(false, "decline.receiver", request.sender.name.copy().aqua())
|
||||
}
|
||||
|
||||
/** Declines a pending [request]. */
|
||||
fun declineRequest(request: T) {
|
||||
this.removeRequest(request)
|
||||
this.onDecline(request)
|
||||
}
|
||||
|
||||
/** Declines an inbound request [requestID] for [receiver]. */
|
||||
fun declineRequest(receiver: ServerPlayer, requestID: UUID) {
|
||||
val request = this.getInboundRequest(receiver, requestID) ?: return
|
||||
this.declineRequest(request)
|
||||
}
|
||||
|
||||
/** Cancels pending outbound requests sent from, and pending inbound request sent to, [player]. */
|
||||
protected open fun onLogoff(player: ServerPlayer) {
|
||||
// ONLY regarding the player. see TeamManager for how team requests are canceled on team disband.
|
||||
outboundRequests.get(player.uuid)?.let { request ->
|
||||
this.cancelRequest(request)
|
||||
}
|
||||
inboundRequests.get(player.uuid)?.toMutableList()?.let { requests ->
|
||||
requests.forEach { request ->
|
||||
this.declineRequest(player, request.requestID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val managers = mutableListOf<RequestManager<*>>()
|
||||
|
||||
fun register(manager: RequestManager<*>) = managers.add(manager)
|
||||
|
||||
fun onLogoff(player: ServerPlayer) = managers.forEach { it.onLogoff(player) }
|
||||
}
|
||||
}
|
||||
@@ -32,9 +32,6 @@ import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.PokemonMoveControl
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.*
|
||||
@@ -56,7 +53,6 @@ import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.damagesource.DamageSource
|
||||
import net.minecraft.world.damagesource.DamageTypes
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.level.Level
|
||||
@@ -196,7 +192,6 @@ object MoLangFunctions {
|
||||
map.put("is_in_water") { _ -> DoubleValue(entity.isUnderWater) }
|
||||
map.put("is_touching_water_or_rain") { _ -> DoubleValue(entity.isInWaterRainOrBubble) }
|
||||
map.put("is_touching_water") { _ -> DoubleValue(entity.isInWater) }
|
||||
map.put("is_underwater") { DoubleValue(entity.getIsSubmerged()) }
|
||||
map.put("is_in_lava") { _ -> DoubleValue(entity.isInLava) }
|
||||
map.put("is_on_fire") { _ -> DoubleValue(entity.isOnFire) }
|
||||
map.put("is_invisible") { _ -> DoubleValue(entity.isInvisible) }
|
||||
@@ -213,18 +208,10 @@ object MoLangFunctions {
|
||||
map.put("velocity_x") { _ -> DoubleValue(entity.deltaMovement.x) }
|
||||
map.put("velocity_y") { _ -> DoubleValue(entity.deltaMovement.y) }
|
||||
map.put("velocity_z") { _ -> DoubleValue(entity.deltaMovement.z) }
|
||||
map.put("width") { DoubleValue(entity.boundingBox.xsize) }
|
||||
map.put("height") { DoubleValue(entity.boundingBox.ysize) }
|
||||
map.put("entity_size") { DoubleValue(entity.boundingBox.run { if (xsize > ysize) xsize else ysize }) }
|
||||
map.put("entity_width") { DoubleValue(entity.boundingBox.xsize) }
|
||||
map.put("entity_height") { DoubleValue(entity.boundingBox.ysize) }
|
||||
|
||||
map.put("horizontal_velocity") { _ -> DoubleValue(entity.deltaMovement.horizontalDistance()) }
|
||||
map.put("vertical_velocity") { DoubleValue(entity.deltaMovement.y) }
|
||||
map.put("is_on_ground") { _ -> DoubleValue(entity.onGround()) }
|
||||
map.put("world") { _ -> entity.level().worldRegistry.wrapAsHolder(entity.level()).asWorldMoLangValue() }
|
||||
map.put("biome") { _ -> entity.level().getBiome(entity.blockPosition()).asBiomeMoLangValue() }
|
||||
map.put("is_passenger") { DoubleValue(entity.isPassenger) }
|
||||
map.put("is_healer_in_use") { params ->
|
||||
val pos = params.get<ArrayStruct>(0).asBlockPos()
|
||||
val healer = entity.level().getBlockEntity(pos, CobblemonBlockEntities.HEALING_MACHINE).orElse(null) ?: return@put DoubleValue.ONE
|
||||
@@ -252,8 +239,6 @@ object MoLangFunctions {
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("class") { StringValue(npc.npc.id.toString()) }
|
||||
map.put("name") { StringValue(npc.name.string) }
|
||||
map.put("level") { DoubleValue(npc.level) }
|
||||
map.put("has_aspect") { params -> DoubleValue(npc.aspects.contains(params.getString(0))) }
|
||||
map.put("face") { params -> ObjectValue(ReferenceDialogueFaceProvider(npc.id, params.getBooleanOrNull(0) != false)) }
|
||||
map.put("in_battle") { DoubleValue(npc.isInBattle()) }
|
||||
map.put("run_script_on_client") { params ->
|
||||
@@ -299,7 +284,7 @@ object MoLangFunctions {
|
||||
}
|
||||
map.put("put_pokemon_in_healer") { params ->
|
||||
val healer = npc.level().getBlockEntity(params.get<ArrayStruct>(0).asBlockPos(), CobblemonBlockEntities.HEALING_MACHINE).orElse(null) ?: return@put DoubleValue.ZERO
|
||||
val party = npc.party ?: return@put DoubleValue.ZERO
|
||||
val party = npc.staticParty ?: return@put DoubleValue.ZERO
|
||||
if (healer.canHeal(party)) {
|
||||
healer.activate(npc.uuid, party)
|
||||
return@put DoubleValue.ONE
|
||||
@@ -334,28 +319,6 @@ object MoLangFunctions {
|
||||
}
|
||||
)
|
||||
|
||||
val pokemonFunctions = mutableListOf<(Pokemon) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ pokemon ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("is_wild") { DoubleValue(pokemon.entity?.let { it.ownerUUID == null } == true) }
|
||||
map.put("is_shiny") { DoubleValue(pokemon.shiny) }
|
||||
map.put("form") { StringValue(pokemon.form.name) }
|
||||
map.put("weight") { DoubleValue(pokemon.species.weight.toDouble()) }
|
||||
map
|
||||
}
|
||||
)
|
||||
|
||||
val pokemonEntityFunctions = mutableListOf<(PokemonEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ pokemonEntity ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("in_battle") { DoubleValue(pokemonEntity.isBattling) }
|
||||
map.put("is_moving") { DoubleValue((pokemonEntity.moveControl as? PokemonMoveControl)?.hasWanted() == true) }
|
||||
map.put("is_flying") { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
|
||||
map.put("has_aspect") { DoubleValue(it.getString(0) in pokemonEntity.aspects) }
|
||||
map
|
||||
}
|
||||
)
|
||||
|
||||
val partyFunctions = mutableListOf<(PartyStore) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ party ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
@@ -425,30 +388,6 @@ object MoLangFunctions {
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addEntityFunctions(entity: LivingEntity): QueryStruct {
|
||||
val addedFunctions = entityFunctions
|
||||
.flatMap { it.invoke(entity).entries }
|
||||
.associate { it.key to it.value }
|
||||
functions.putAll(addedFunctions)
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addPokemonFunctions(pokemon: Pokemon): QueryStruct {
|
||||
val addedFunctions = pokemonFunctions
|
||||
.flatMap { it.invoke(pokemon).entries }
|
||||
.associate { it.key to it.value }
|
||||
functions.putAll(addedFunctions)
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addPokemonEntityFunctions(pokemonEntity: PokemonEntity): QueryStruct {
|
||||
val addedFunctions = pokemonEntityFunctions
|
||||
.flatMap { it.invoke(pokemonEntity).entries }
|
||||
.associate { it.key to it.value }
|
||||
functions.putAll(addedFunctions)
|
||||
return this
|
||||
}
|
||||
|
||||
fun <T : QueryStruct> T.addFunctions(functions: Map<String, java.util.function.Function<MoParams, Any>>): T {
|
||||
this.functions.putAll(functions)
|
||||
return this
|
||||
|
||||
@@ -35,14 +35,6 @@ class BenchedMoves : Iterable<BenchedMove> {
|
||||
emit = previousEmit
|
||||
}
|
||||
|
||||
fun copyFrom(other: BenchedMoves) {
|
||||
doWithoutEmitting {
|
||||
clear()
|
||||
other.forEach { add(it) }
|
||||
}
|
||||
update()
|
||||
}
|
||||
|
||||
fun doThenEmit(action: () -> Unit) {
|
||||
doWithoutEmitting(action)
|
||||
update()
|
||||
|
||||
+2
-5
@@ -11,14 +11,12 @@ package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.EntityProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.TargetsProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.scheduling.delayedFuture
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import net.minecraft.commands.arguments.EntityArgument.getPlayers
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
|
||||
@@ -31,8 +29,7 @@ import net.minecraft.server.level.ServerLevel
|
||||
class EntityParticlesActionEffectKeyframe : ConditionalActionEffectKeyframe(), EntityConditionalActionEffectKeyframe {
|
||||
override val entityCondition = "q.entity.is_user".asExpressionLike()
|
||||
var effect: String? = null
|
||||
var locators: List<String> = listOf("target")
|
||||
|
||||
var locator: String = "root"
|
||||
val delay: ExpressionLike = "0".asExpressionLike()
|
||||
val visibilityRange = 200
|
||||
|
||||
@@ -48,7 +45,7 @@ class EntityParticlesActionEffectKeyframe : ConditionalActionEffectKeyframe(), E
|
||||
}?.asIdentifierDefaultingNamespace() ?: return skip()
|
||||
|
||||
entities.filter { it is PosableEntity }.forEach { entity ->
|
||||
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, (locators).toList())
|
||||
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, listOf(locator))
|
||||
val players = (entity.level() as ServerLevel).getPlayers { it.distanceTo(entity) <= visibilityRange }
|
||||
packet.sendToPlayers(players)
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ class NPCClass {
|
||||
var variables = mutableMapOf<String, MoValue>() // Questionable whether this should be here.
|
||||
var party: NPCPartyProvider? = null
|
||||
var skill: Int = 0
|
||||
var autoHealParty: Boolean = false
|
||||
var battleTheme: ResourceLocation? = null
|
||||
var ai: MutableList<BrainConfig> = mutableListOf()
|
||||
|
||||
@@ -78,7 +77,6 @@ class NPCClass {
|
||||
buffer.writeString(v.defaultValue)
|
||||
}
|
||||
buffer.writeInt(skill)
|
||||
buffer.writeBoolean(autoHealParty)
|
||||
buffer.writeMapK(size = IntSize.U_BYTE, map = variables) { (key, value) ->
|
||||
buffer.writeString(key)
|
||||
buffer.writeString(value.asString())
|
||||
@@ -118,7 +116,6 @@ class NPCClass {
|
||||
NPCConfigVariable(variableName, displayName, description, type, defaultValue)
|
||||
}.toMutableList()
|
||||
skill = buffer.readInt()
|
||||
autoHealParty = buffer.readBoolean()
|
||||
buffer.readMapK(size = IntSize.U_BYTE, map = variables) {
|
||||
val key = buffer.readString()
|
||||
val value = buffer.readString()
|
||||
|
||||
@@ -8,11 +8,15 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.npc
|
||||
|
||||
import com.cobblemon.mod.common.api.npc.partyproviders.PoolPartyProvider
|
||||
import com.cobblemon.mod.common.api.npc.partyproviders.DynamicPartyProvider
|
||||
import com.cobblemon.mod.common.api.npc.partyproviders.NPCParty
|
||||
import com.cobblemon.mod.common.api.npc.partyproviders.SimplePartyProvider
|
||||
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.google.gson.JsonElement
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
* A provider of a party for battling the NPC. Completely custom party providers will only display
|
||||
@@ -25,13 +29,11 @@ interface NPCPartyProvider {
|
||||
companion object {
|
||||
val types = mutableMapOf<String, (String) -> NPCPartyProvider>(
|
||||
SimplePartyProvider.TYPE to { SimplePartyProvider() },
|
||||
PoolPartyProvider.TYPE to { PoolPartyProvider() }
|
||||
DynamicPartyProvider.TYPE to { DynamicPartyProvider() }
|
||||
)
|
||||
}
|
||||
|
||||
val type: String
|
||||
val isStatic: Boolean
|
||||
fun provide(npc: NPCEntity, level: Int): NPCPartyStore
|
||||
// Why did I opt for manual JSON loading??? I must have had a reason but I can't remember. Maybe for S2C? Use a codec doofus
|
||||
fun provide(npc: NPCEntity, level: Int): NPCParty
|
||||
fun loadFromJSON(json: JsonElement)
|
||||
}
|
||||
@@ -29,7 +29,6 @@ class NPCPreset {
|
||||
var hitbox: EntityDimensions? = null
|
||||
var ai: List<BrainConfig>? = null
|
||||
var skill: Int? = null
|
||||
var autoHealParty: Boolean? = null
|
||||
var battleTheme: ResourceLocation? = null
|
||||
|
||||
fun applyTo(npcClass: NPCClass) {
|
||||
@@ -48,7 +47,6 @@ class NPCPreset {
|
||||
names?.let { npcClass.names.addAll(it) }
|
||||
hitbox?.let { npcClass.hitbox = it }
|
||||
skill?.let { npcClass.skill = it }
|
||||
autoHealParty?.let { npcClass.autoHealParty = it }
|
||||
battleTheme?.let { npcClass.battleTheme = it }
|
||||
ai?.let { npcClass.ai.addAll(it) }
|
||||
}
|
||||
|
||||
+2
-2
@@ -8,15 +8,15 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.npc.configuration
|
||||
|
||||
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class NPCBattleConfiguration {
|
||||
var canChallenge = false
|
||||
@Deprecated("This is not likely to get used")
|
||||
var party: NPCPartyProvider? = null
|
||||
var simultaneousBattles = false
|
||||
@Deprecated("This is not likely to get used")
|
||||
var healAfterwards = true
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.npc.partyproviders
|
||||
|
||||
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.google.gson.JsonElement
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A provider of a party for battling the NPC. It generates a [StaticNPCParty] but using a more complex process
|
||||
* that takes an input level and makes a randomized selection of Pokémon and movesets.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 12th, 2024
|
||||
*/
|
||||
class DynamicPartyProvider : NPCPartyProvider {
|
||||
companion object {
|
||||
val TYPE = "dynamic"
|
||||
}
|
||||
|
||||
class DynamicPool {
|
||||
val id = ResourceLocation.parse("cobblemon:pool")
|
||||
val pokemon: MutableList<DynamicPokemon> = mutableListOf()
|
||||
}
|
||||
|
||||
|
||||
class DynamicPokemon(
|
||||
val pokemon: PokemonProperties,
|
||||
val possibleLevels: IntRange,
|
||||
val selectableTimes: IntRange,
|
||||
val guaranteed: Boolean = false
|
||||
)
|
||||
|
||||
override val type = "dynamic"
|
||||
|
||||
override fun loadFromJSON(json: JsonElement) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun provide(npc: NPCEntity, level: Int): NPCParty {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.npc.partyproviders
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.net.IntSize
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.readSizedInt
|
||||
import com.cobblemon.mod.common.util.writeSizedInt
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
* Represents a party of Pokemon that an NPC can have. This isn't directly a party of Pokémon so that
|
||||
* a dynamically generated party can exist where a party is generated at the time of a challenge.
|
||||
*
|
||||
* This interface is sealed. Implementations should be either [StaticNPCParty] or [DynamicNPCParty].
|
||||
* If you want a custom type then you should implement [DynamicNPCParty] and register it in the
|
||||
* [DynamicNPCParty.types] map.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 12th, 2024
|
||||
*/
|
||||
sealed interface NPCParty {
|
||||
fun getParty(player: ServerPlayer, npc: NPCEntity): PartyStore?
|
||||
fun encode(buffer: RegistryFriendlyByteBuf)
|
||||
fun decode(buffer: RegistryFriendlyByteBuf)
|
||||
fun saveToNBT(nbt: CompoundTag, registryAccess: RegistryAccess)
|
||||
fun loadFromNBT(nbt: CompoundTag, registryAccess: RegistryAccess)
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple wrapping around a [NPCPartyStore]. This is a straightforward party that is always the same.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 12th, 2024
|
||||
*/
|
||||
class StaticNPCParty(val party: NPCPartyStore) : NPCParty {
|
||||
override fun getParty(player: ServerPlayer, npc: NPCEntity) = party
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeSizedInt(IntSize.U_BYTE, party.size())
|
||||
for (pokemon in party) {
|
||||
Pokemon.S2C_CODEC.encode(buffer, pokemon)
|
||||
}
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
|
||||
party.add(Pokemon.S2C_CODEC.decode(buffer))
|
||||
}
|
||||
}
|
||||
|
||||
override fun saveToNBT(nbt: CompoundTag, registryAccess: RegistryAccess) {
|
||||
for ((index, pokemon) in party.withIndex()) {
|
||||
nbt.put(DataKeys.NPC_PARTY_POKEMON + index, pokemon.saveToNBT(registryAccess))
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadFromNBT(nbt: CompoundTag, registryAccess: RegistryAccess) {
|
||||
var index = 0
|
||||
while (nbt.contains(DataKeys.NPC_PARTY_POKEMON + index)) {
|
||||
this.party.add(Pokemon().loadFromNBT(registryAccess, nbt.getCompound(DataKeys.NPC_PARTY_POKEMON + index)))
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dynamic party that is generated at the time of a challenge.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 12th, 2024
|
||||
*/
|
||||
interface DynamicNPCParty : NPCParty {
|
||||
companion object {
|
||||
val types = mutableMapOf<String, Class<out NPCParty>>()
|
||||
init {}
|
||||
}
|
||||
}
|
||||
-128
@@ -1,128 +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.api.npc.partyproviders
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
|
||||
/**
|
||||
* A provider of a party for battling the NPC. It generates a party using a more complex process
|
||||
* that takes an input level and makes a randomized selection of Pokémon and movesets from a pool.
|
||||
*
|
||||
* The party can be static or dynamic based on [isStatic]. If it is dynamic, the pool will be used
|
||||
* to generate a party whenever the NPC is challenged and AI that depends on a reliable party will
|
||||
* not run.
|
||||
*
|
||||
* Not an actual pool party. That's for v2.0.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 12th, 2024
|
||||
*/
|
||||
class PoolPartyProvider : NPCPartyProvider {
|
||||
companion object {
|
||||
const val TYPE = "pool"
|
||||
}
|
||||
|
||||
@Transient
|
||||
override val type = TYPE
|
||||
|
||||
override var isStatic: Boolean = false
|
||||
var minPokemon: Expression = "1".asExpression()
|
||||
var maxPokemon: Expression = "6".asExpression()
|
||||
var pool = mutableListOf<DynamicPokemon>()
|
||||
|
||||
override fun loadFromJSON(json: JsonElement) {
|
||||
json as JsonObject
|
||||
this.minPokemon = json.getAsJsonPrimitive("minPokemon").asString?.asExpression() ?: "1".asExpression()
|
||||
this.maxPokemon = json.getAsJsonPrimitive("maxPokemon").asString?.asExpression() ?: "6".asExpression()
|
||||
this.pool = json.getAsJsonArray("pool").map {
|
||||
if (it is JsonPrimitive) {
|
||||
return@map DynamicPokemon(
|
||||
PokemonProperties.parse(it.asString),
|
||||
0..0,
|
||||
0..100,
|
||||
"1".asExpression(),
|
||||
"1".asExpression()
|
||||
)
|
||||
}
|
||||
|
||||
it as JsonObject
|
||||
DynamicPokemon(
|
||||
PokemonProperties.parse(it.getAsJsonPrimitive("pokemon").asString),
|
||||
it.getAsJsonPrimitive("levelVariation")?.asString?.split("-")?.let { it[0].toInt()..it[1].toInt() } ?: 0..0,
|
||||
it.getAsJsonPrimitive("npcLevels")?.asString?.split("-")?.let { it[0].toInt()..it[1].toInt() } ?: 0..100,
|
||||
it.getAsJsonPrimitive("selectableTimes")?.asString?.asExpression() ?: "1".asExpression(),
|
||||
it.getAsJsonPrimitive("weight")?.asString?.asExpression() ?: "1".asExpression()
|
||||
)
|
||||
}.toMutableList()
|
||||
isStatic = json.getAsJsonPrimitive("isStatic").asBoolean
|
||||
}
|
||||
|
||||
class DynamicPokemon(
|
||||
val pokemon: PokemonProperties,
|
||||
val levelVariation: IntRange,
|
||||
val npcLevels: IntRange,
|
||||
val selectableTimes: Expression,
|
||||
val weight: Expression = "1".asExpression()
|
||||
) {
|
||||
fun getWeight(runtime: MoLangRuntime): Float {
|
||||
return runtime.resolveFloat(weight)
|
||||
}
|
||||
|
||||
fun hasWeight(runtime: MoLangRuntime): Boolean {
|
||||
return runtime.resolveFloat(weight) != 0F
|
||||
}
|
||||
}
|
||||
|
||||
fun formulateParty(npc: NPCEntity, level: Int, party: NPCPartyStore) {
|
||||
val runtime = MoLangRuntime().setup().withQueryValue("npc", npc.struct)
|
||||
val minPokemon = runtime.resolveInt(this.minPokemon)
|
||||
val maxPokemon = runtime.resolveInt(this.maxPokemon)
|
||||
var desiredPokemonCount = (minPokemon..maxPokemon).random()
|
||||
|
||||
val workingPool = pool.filter { level in it.npcLevels }.toMutableList()
|
||||
val useCounts = mutableMapOf<DynamicPokemon, Int>()
|
||||
|
||||
while (desiredPokemonCount > 0 && workingPool.filter { it.hasWeight(runtime) }.isNotEmpty()) {
|
||||
val selected = workingPool.filter { it.getWeight(runtime) == -1F }.randomOrNull()
|
||||
?: workingPool.weightedSelection { it.getWeight(runtime) }
|
||||
?: break
|
||||
useCounts[selected] = useCounts.getOrDefault(selected, 0) + 1
|
||||
val allowedSelections = runtime.resolveInt(selected.selectableTimes)
|
||||
if (useCounts[selected]!! >= allowedSelections) {
|
||||
workingPool.remove(selected)
|
||||
}
|
||||
desiredPokemonCount--
|
||||
|
||||
// If the Pokémon's props specifies a level then use that, otherwise choose a random level within the range
|
||||
val randomLevel = (level + selected.levelVariation.random())
|
||||
val instance = selected.pokemon.copy().also { it.level = it.level ?: randomLevel }.create()
|
||||
party.add(instance)
|
||||
}
|
||||
}
|
||||
|
||||
override fun provide(npc: NPCEntity, level: Int): NPCPartyStore {
|
||||
val party = NPCPartyStore(npc)
|
||||
formulateParty(npc, level, party)
|
||||
return party
|
||||
}
|
||||
}
|
||||
+3
-8
@@ -16,7 +16,7 @@ import com.cobblemon.mod.common.util.toProperties
|
||||
import com.google.gson.JsonElement
|
||||
|
||||
/**
|
||||
* A basic party provider that just produces a [NPCPartyStore] based on a simple list of [PokemonProperties].
|
||||
* A basic party provider that just produces a [StaticNPCParty] based on a list of [PokemonProperties].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since August 19th, 2023
|
||||
@@ -28,15 +28,10 @@ class SimplePartyProvider : NPCPartyProvider {
|
||||
|
||||
@Transient
|
||||
override val type = TYPE
|
||||
override var isStatic = true
|
||||
|
||||
val pokemon = mutableListOf<PokemonProperties>()
|
||||
|
||||
override fun loadFromJSON(json: JsonElement) {
|
||||
val isStatic = json.asJsonObject.get("isStatic")?.asBoolean
|
||||
if (isStatic != null) {
|
||||
this.isStatic = isStatic
|
||||
}
|
||||
val pokemonList = json.asJsonObject.getAsJsonArray("pokemon")
|
||||
pokemonList.forEach {
|
||||
val pokemon = if (it.isJsonPrimitive) {
|
||||
@@ -52,10 +47,10 @@ class SimplePartyProvider : NPCPartyProvider {
|
||||
}
|
||||
}
|
||||
|
||||
override fun provide(npc: NPCEntity, level: Int): NPCPartyStore {
|
||||
override fun provide(npc: NPCEntity, level: Int): NPCParty {
|
||||
val pokemon = pokemon.map { it.copy().also { it.level = it.level ?: level }.create() }
|
||||
val party = NPCPartyStore(npc)
|
||||
pokemon.forEach(party::add)
|
||||
return party
|
||||
return StaticNPCParty(party)
|
||||
}
|
||||
}
|
||||
@@ -61,15 +61,11 @@ class FormDexRecord {
|
||||
@Transient
|
||||
lateinit var speciesDexRecord: SpeciesDexRecord
|
||||
|
||||
@Transient
|
||||
lateinit var formName: String
|
||||
|
||||
@Transient
|
||||
lateinit var struct: QueryStruct
|
||||
|
||||
fun initialize(speciesDexRecord: SpeciesDexRecord, formName: String) {
|
||||
fun initialize(speciesDexRecord: SpeciesDexRecord) {
|
||||
this.speciesDexRecord = speciesDexRecord
|
||||
this.formName = formName
|
||||
struct = QueryStruct(hashMapOf())
|
||||
.addFunction("data") { data }
|
||||
.addFunction("knowledge") { StringValue(knowledge.name) }
|
||||
|
||||
@@ -79,7 +79,7 @@ class SpeciesDexRecord {
|
||||
fun initialize(pokedexManager: AbstractPokedexManager, id: ResourceLocation) {
|
||||
this.id = id
|
||||
this.pokedexManager = pokedexManager
|
||||
this.formRecords.forEach { it.value.initialize(this, it.key) }
|
||||
this.formRecords.forEach { it.value.initialize(this) }
|
||||
}
|
||||
|
||||
fun onFormRecordUpdated(formDexRecord: FormDexRecord) {
|
||||
@@ -106,7 +106,7 @@ class SpeciesDexRecord {
|
||||
fun getOrCreateFormRecord(formName: String): FormDexRecord {
|
||||
return formRecords.getOrPut(formName.lowercase()) {
|
||||
val record = FormDexRecord()
|
||||
record.initialize(this, formName)
|
||||
record.initialize(this)
|
||||
onFormRecordUpdated(record)
|
||||
// Some more stuff eventually
|
||||
record
|
||||
|
||||
@@ -14,8 +14,6 @@ import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.abilities.Ability
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.pokemon.ShinyChanceCalculationEvent
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
@@ -58,8 +56,6 @@ import net.minecraft.nbt.Tag
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.Level
|
||||
|
||||
@@ -577,24 +573,18 @@ open class PokemonProperties {
|
||||
return true
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun create(player: ServerPlayer? = null): Pokemon {
|
||||
fun create(): Pokemon {
|
||||
val pokemon = Pokemon()
|
||||
apply(pokemon)
|
||||
pokemon.initialize()
|
||||
roll(pokemon, player)
|
||||
roll(pokemon)
|
||||
return pokemon
|
||||
}
|
||||
|
||||
// TEST YOUR LUCK!
|
||||
@JvmOverloads
|
||||
fun roll(pokemon: Pokemon, player: ServerPlayer? = null) {
|
||||
fun roll(pokemon: Pokemon) {
|
||||
val baseTypes = pokemon.form.types.toList()
|
||||
var shinyRate = Cobblemon.config.shinyRate
|
||||
CobblemonEvents.SHINY_CHANCE_CALCULATION.post(ShinyChanceCalculationEvent(shinyRate, pokemon)) { event ->
|
||||
shinyRate = event.calculate(player)
|
||||
}
|
||||
if (this.shiny == null) pokemon.shiny = shinyRate.checkRate()
|
||||
if (this.shiny == null) pokemon.shiny = Cobblemon.config.shinyRate.checkRate()
|
||||
if (this.teraType == null) pokemon.teraType =
|
||||
if (Cobblemon.config.teraTypeRate.checkRate()) {
|
||||
var picked = TeraTypes.random(true)
|
||||
@@ -606,9 +596,8 @@ open class PokemonProperties {
|
||||
else TeraTypes.forElementalType(baseTypes.random())
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun createEntity(world: Level, player: ServerPlayer? = null): PokemonEntity {
|
||||
return PokemonEntity(world, create(player)).also { applyCustomProperties(it) }
|
||||
fun createEntity(world: Level): PokemonEntity {
|
||||
return PokemonEntity(world, create()).also { applyCustomProperties(it) }
|
||||
}
|
||||
|
||||
// TODO Codecs at some point
|
||||
@@ -794,7 +783,7 @@ open class PokemonProperties {
|
||||
}
|
||||
|
||||
// If the config value is at least 1, then do 1/x and use that as the property chance
|
||||
private fun Float.checkRate(): Boolean = if (this >= 1) (Random.Default.nextFloat() < 1 / this) else Random.Default.nextFloat() < this
|
||||
private fun Float.checkRate(): Boolean = this >= 1 && (Random.Default.nextFloat() < 1 / this)
|
||||
|
||||
/**
|
||||
* Attempts to find an ability by ID and resolve if it should be forced or if it's legal for the given form.
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ fun interface PokemonPropertyExtractor {
|
||||
val EVS = add { pokemon, properties -> properties.evs = pokemon.evs }
|
||||
|
||||
@JvmField
|
||||
val ILLUSION = mutableListOf(SPECIES, FORM, ASPECTS, GENDER, NICKNAME, SHINY, POKEBALL)
|
||||
val ILLUSION = mutableListOf(SPECIES, FORM, ASPECTS, GENDER, NICKNAME, SHINY)
|
||||
@JvmField
|
||||
val TRANSFORM = mutableListOf(SPECIES, FORM, ASPECTS, GENDER)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ package com.cobblemon.mod.common.api.pokemon.egg
|
||||
*
|
||||
* @property showdownID used for data synchronization with Showdown data format.
|
||||
*/
|
||||
enum class EggGroup(val showdownID: String) {
|
||||
enum class EggGroup(internal val showdownID: String) {
|
||||
|
||||
MONSTER("Monster"),
|
||||
WATER_1("Water 1"),
|
||||
@@ -31,16 +31,6 @@ enum class EggGroup(val showdownID: String) {
|
||||
WATER_2("Water 2"),
|
||||
DITTO("Ditto"),
|
||||
DRAGON("Dragon"),
|
||||
UNDISCOVERED("Undiscovered");
|
||||
UNDISCOVERED("Undiscovered")
|
||||
|
||||
companion object {
|
||||
private val identifierMap: Map<String, EggGroup> = entries.associateBy {
|
||||
it.showdownID.lowercase().replace(" ", "_").replace("-", "_")
|
||||
}
|
||||
|
||||
fun fromIdentifier(identifier: String): EggGroup? {
|
||||
val key = identifier.lowercase().replace(" ", "_").replace("-", "_")
|
||||
return identifierMap[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,11 +156,12 @@ interface Evolution : EvolutionLike {
|
||||
}
|
||||
|
||||
val preEvoName = pokemon.getDisplayName()
|
||||
val evolveMessage = lang("ui.evolve.into", preEvoName, pokemon.species.translatedName)
|
||||
val pokemonEntity = pokemon.entity
|
||||
if (pokemonEntity == null || !useEvolutionEffect) {
|
||||
pokemon.getOwnerPlayer()?.playNotifySound(CobblemonSounds.EVOLUTION_UI, SoundSource.PLAYERS, 1F, 1F)
|
||||
evolutionMethod(pokemon)
|
||||
pokemon.getOwnerPlayer()?.sendSystemMessage(lang("ui.evolve.into", preEvoName, pokemon.species.translatedName))
|
||||
pokemon.getOwnerPlayer()?.sendSystemMessage(evolveMessage)
|
||||
} else {
|
||||
pokemonEntity.busyLocks.add("evolving")
|
||||
pokemonEntity.navigation.stop()
|
||||
@@ -173,7 +174,7 @@ interface Evolution : EvolutionLike {
|
||||
pokemonEntity.after( seconds = 12F ) {
|
||||
cryAnimation(pokemonEntity)
|
||||
pokemonEntity.busyLocks.remove("evolving")
|
||||
pokemon.getOwnerPlayer()?.sendSystemMessage(lang("ui.evolve.into", preEvoName, pokemon.species.translatedName))
|
||||
pokemon.getOwnerPlayer()?.sendSystemMessage(evolveMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-8
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.api.scheduling
|
||||
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import com.cobblemon.mod.common.util.runOnServer
|
||||
import net.minecraft.world.TickRateManager
|
||||
import net.minecraft.world.level.Level
|
||||
|
||||
@Deprecated("Use afterOnServer or afterOnClient; ambiguous side is not good for your health")
|
||||
@JvmOverloads
|
||||
@@ -35,13 +33,9 @@ fun delayedFuture(ticks: Int = 0, seconds: Float = 0F, serverThread: Boolean = f
|
||||
* being completed after the delay does things like entity removal or other thread-unsafe actions.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun afterOnServer(seconds: Float, action: () -> Unit) = ServerTaskTracker.after(seconds, action)
|
||||
fun afterOnServer(ticks: Int = 0, seconds: Float = 0F, action: () -> Unit) = ServerTaskTracker.after(seconds + ticks / 20F, action)
|
||||
@JvmOverloads
|
||||
fun afterOnServer(ticks: Int, level: Level, action: () -> Unit) = ServerTaskTracker.after(ticks / level.tickRateManager().tickrate(), action)
|
||||
@JvmOverloads
|
||||
fun afterOnClient(seconds: Float, action: () -> Unit) = ClientTaskTracker.after(seconds, action)
|
||||
@JvmOverloads
|
||||
fun afterOnClient(ticks: Int, level: Level, action: () -> Unit) = ClientTaskTracker.after(ticks / level.tickRateManager().tickrate(), action)
|
||||
fun afterOnClient(ticks: Int = 0, seconds: Float, action: () -> Unit) = ClientTaskTracker.after(seconds + ticks / 20F, action)
|
||||
|
||||
@Deprecated("Use lerpOnServer or lerpOnClient, side-ambiguity causes problems now")
|
||||
fun lerp(seconds: Float = 0F, serverThread: Boolean = false, action: (Float) -> Unit) = (if (serverThread) ServerTaskTracker else ClientTaskTracker).lerp(seconds, action = action)
|
||||
|
||||
+1
-4
@@ -15,8 +15,6 @@ import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.feature.SeasonFeatureHandler
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.player.Player
|
||||
|
||||
/**
|
||||
* A [SpawnAction] that will spawn a single [PokemonEntity].
|
||||
@@ -46,8 +44,7 @@ class PokemonSpawnAction(
|
||||
} else {
|
||||
null
|
||||
}?.createStack(ctx)
|
||||
val sourcePlayer = ctx.cause.entity as? ServerPlayer
|
||||
val entity = props.createEntity(ctx.world, sourcePlayer)
|
||||
val entity = props.createEntity(ctx.world)
|
||||
entity.spawnCause = ctx.cause
|
||||
SeasonFeatureHandler.updateSeason(entity.pokemon, Cobblemon.seasonResolver(ctx.world, ctx.position))
|
||||
if (heldItem != null) {
|
||||
|
||||
+8
-47
@@ -17,8 +17,6 @@ import com.cobblemon.mod.common.api.events.fishing.BobberSpawnPokemonEvent
|
||||
import com.cobblemon.mod.common.api.fishing.FishingBait
|
||||
import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
@@ -26,7 +24,6 @@ import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.api.types.tera.TeraTypes
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.item.interactive.PokerodItem
|
||||
@@ -104,6 +101,13 @@ class FishingSpawnCause(
|
||||
pokemonEntity.pokemon.level = level
|
||||
}
|
||||
|
||||
fun alterTeraAttempt(pokemonEntity: PokemonEntity, effect: FishingBait.Effect) {
|
||||
if (pokemonEntity.pokemon.teraType == effect.subcategory?.let { TeraTypes.get(it.path) } ||
|
||||
TeraTypes.get(effect.subcategory!!.path) == null) return
|
||||
|
||||
pokemonEntity.pokemon.teraType = TeraTypes.get(effect.subcategory.path)!!
|
||||
}
|
||||
|
||||
fun alterHAAttempt(pokemonEntity: PokemonEntity) {
|
||||
//val species = pokemonEntity.pokemon.species.let { PokemonSpecies.getByName(it.name) } ?: return
|
||||
//val ability = species.abilities.mapping[Priority.LOW]?.first()?.template?.name ?: return
|
||||
@@ -118,7 +122,7 @@ class FishingSpawnCause(
|
||||
.randomOrNull ()
|
||||
?.let { ability ->
|
||||
// No need to force, this is legal
|
||||
pokemonEntity.pokemon.ability = ability.template.create(false, ability.priority)
|
||||
pokemonEntity.pokemon.ability = ability.template.create(false)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -175,49 +179,6 @@ class FishingSpawnCause(
|
||||
}
|
||||
}
|
||||
}
|
||||
// if bait exists and any effects are related to Typing
|
||||
if (bait != null && bait.effects.any{ it.type == FishingBait.Effects.TYPING }){
|
||||
if (detail is PokemonSpawnDetail) {
|
||||
val detailSpecies = detail.pokemon.species?.let { PokemonSpecies.getByName(it) }
|
||||
val baitEffect = bait.effects.firstOrNull { it.type == FishingBait.Effects.TYPING }
|
||||
val baitTypingEffect = baitEffect?.subcategory?.path?.let { ElementalTypes.get(it) }
|
||||
|
||||
if (detailSpecies != null && baitTypingEffect != null) {
|
||||
val isMatchingType = detailSpecies.types.contains(baitTypingEffect)
|
||||
return when {
|
||||
isMatchingType -> super.affectWeight(detail, ctx, weight * baitEffect.value.toFloat()) // multiply weight by multiplier of bait effect if typing is found
|
||||
else -> super.affectWeight(detail, ctx, weight) // use base spawn weight if typing is not same as bait
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if bait exists and any effects are related to Egg Groups
|
||||
if (bait != null && bait.effects.any { it.type == FishingBait.Effects.EGG_GROUP }) {
|
||||
if (detail is PokemonSpawnDetail) {
|
||||
val detailSpecies = detail.pokemon.species?.let { PokemonSpecies.getByName(it) }
|
||||
|
||||
if (detailSpecies != null) {
|
||||
// Collect all the egg group effects
|
||||
val eggGroupEffects = bait.effects.filter { it.type == FishingBait.Effects.EGG_GROUP }
|
||||
|
||||
// Check if any of the egg group effects match the species' egg groups
|
||||
val matchingEffect = eggGroupEffects.firstOrNull { effect ->
|
||||
val effectEggGroupKey = effect.subcategory?.path ?: return@firstOrNull false
|
||||
val eggGroup = EggGroup.fromIdentifier(effectEggGroupKey)
|
||||
if (eggGroup == null) {
|
||||
LOGGER.warn("Unknown egg group identifier: $effectEggGroupKey")
|
||||
return@firstOrNull false
|
||||
}
|
||||
detailSpecies.eggGroups.contains(eggGroup)
|
||||
}
|
||||
|
||||
if (matchingEffect != null) {
|
||||
val multiplier = matchingEffect.value
|
||||
return super.affectWeight(detail, ctx, (weight * multiplier).toFloat())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.affectWeight(detail, ctx, weight)
|
||||
}
|
||||
|
||||
|
||||
@@ -166,9 +166,9 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
|
||||
}
|
||||
|
||||
override fun loadFromNBT(nbt: CompoundTag, registryAccess: RegistryAccess): PartyStore {
|
||||
val slotCount = nbt.getInt(DataKeys.STORE_SLOT_COUNT).takeIf { it in 1..6 } ?: 6
|
||||
while (slotCount < slots.size) { slots.removeLast() }
|
||||
while (slotCount > slots.size) { slots.add(null) }
|
||||
val slotCount = nbt.getInt(DataKeys.STORE_SLOT_COUNT)
|
||||
while (slotCount > slots.size) { slots.removeLast() }
|
||||
while (slotCount < slots.size) { slots.add(null) }
|
||||
for (slot in slots.indices) {
|
||||
val pokemonNBT = nbt.getCompound(DataKeys.STORE_SLOT + slot)
|
||||
try {
|
||||
|
||||
+4
-13
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.player
|
||||
|
||||
import com.cobblemon.mod.common.advancement.criterion.AspectCriterion
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
@@ -17,8 +18,6 @@ class PlayerAdvancementData {
|
||||
|
||||
var totalCaptureCount: Int = 0
|
||||
private set
|
||||
var totalEggsCollected: Int = 0
|
||||
private set
|
||||
var totalEggsHatched: Int = 0
|
||||
private set
|
||||
var totalEvolvedCount: Int = 0
|
||||
@@ -45,10 +44,6 @@ class PlayerAdvancementData {
|
||||
totalCaptureCount++
|
||||
}
|
||||
|
||||
fun updateTotalEggsCollected() {
|
||||
totalEggsCollected++
|
||||
}
|
||||
|
||||
fun updateTotalEggsHatched() {
|
||||
totalEggsHatched++
|
||||
}
|
||||
@@ -107,8 +102,7 @@ class PlayerAdvancementData {
|
||||
}
|
||||
|
||||
fun updateAspectsCollected(player: ServerPlayer, pokemon: Pokemon) {
|
||||
//TODO: take another look at using the Advancement progress
|
||||
/*val aspectConditions = player.advancements.progress.keys
|
||||
val aspectConditions = player.advancements.progress.keys
|
||||
.flatMap { it.value.criteria.values }
|
||||
.mapNotNull { it.trigger }
|
||||
.filterIsInstance<AspectCriterion>()
|
||||
@@ -120,9 +114,6 @@ class PlayerAdvancementData {
|
||||
if (trackedAspects.isNotEmpty()) {
|
||||
val collectedAspects = aspectsCollected.getOrPut(pokemon.species.resourceIdentifier) { mutableSetOf() }
|
||||
pokemon.aspects.filter(trackedAspects::contains).forEach(collectedAspects::add)
|
||||
}*/
|
||||
|
||||
val collectedAspects = aspectsCollected.getOrPut(pokemon.species.resourceIdentifier) { mutableSetOf() }
|
||||
pokemon.aspects.forEach(collectedAspects::add)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.storage.player.adapter
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexManager
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreType
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
@@ -28,7 +27,7 @@ class DexDataJsonBackend: JsonBackedPlayerDataStoreBackend<PokedexManager>("poke
|
||||
override val gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(PokedexManager::class.java, CodecBackedAdapter(PokedexManager.CODEC))
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.create()
|
||||
override val classToken = TypeToken.get(PokedexManager::class.java)
|
||||
override val defaultData = defaultDataFunc
|
||||
|
||||
-44
@@ -1,44 +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.api.storage.player.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexManager
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.mongodb.client.MongoClient
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* A [PlayerDataStoreBackend] for [PokedexManager]
|
||||
*
|
||||
* @author Pebbles
|
||||
* @since October 26, 2024
|
||||
*/
|
||||
|
||||
class DexDataMongoBackend(mongoClient: MongoClient, databaseName: String, collectionName: String) :
|
||||
MongoBackedPlayerDataStoreBackend<PokedexManager>(mongoClient, databaseName, collectionName, PlayerInstancedDataStoreTypes.POKEDEX) {
|
||||
override val gson = GsonBuilder()
|
||||
.registerTypeAdapter(PokedexManager::class.java, CodecBackedAdapter(PokedexManager.CODEC))
|
||||
.create()
|
||||
override val classToken = TypeToken.get(PokedexManager::class.java)
|
||||
override val defaultData = defaultDataFunc
|
||||
|
||||
override fun initialize(store: PokedexManager) {
|
||||
store.initialize()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val defaultDataFunc = { uuid: UUID ->
|
||||
PokedexManager(uuid, mutableMapOf())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-59
@@ -1,59 +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.api.storage.player.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.InstancedPlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreType
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.mongodb.client.MongoClient
|
||||
import com.mongodb.client.model.ReplaceOptions
|
||||
import org.bson.Document
|
||||
import java.io.BufferedReader
|
||||
import java.io.FileReader
|
||||
import java.io.PrintWriter
|
||||
import java.util.UUID
|
||||
import kotlin.reflect.KMutableProperty
|
||||
import kotlin.reflect.full.memberProperties
|
||||
|
||||
abstract class MongoBackedPlayerDataStoreBackend<T : InstancedPlayerData>(
|
||||
mongoClient: MongoClient, databaseName: String, collectionName: String, type: PlayerInstancedDataStoreType
|
||||
) : MongoBasedPlayerDataStoreBackend<T>(mongoClient, type, databaseName, collectionName) {
|
||||
abstract val gson: Gson
|
||||
|
||||
//The class GSON needs to deserialize to
|
||||
abstract val classToken: TypeToken<T>
|
||||
|
||||
override fun save(playerData: T) {
|
||||
collection.replaceOne(
|
||||
Document("uuid", playerData.uuid.toString()),
|
||||
Document.parse(gson.toJson(playerData)),
|
||||
ReplaceOptions().upsert(true)
|
||||
)
|
||||
}
|
||||
|
||||
override fun load(uuid: UUID): T {
|
||||
val filter = Document("uuid", uuid.toString())
|
||||
val document = collection.find(filter).first()
|
||||
|
||||
return if (document != null) {
|
||||
val jsonStr = document.toJson()
|
||||
gson.fromJson(jsonStr, classToken).also {
|
||||
val newProps = it::class.memberProperties.filterIsInstance<KMutableProperty<*>>()
|
||||
.filter { member -> member.getter.call(it) == null }
|
||||
if (newProps.isNotEmpty()) {
|
||||
val defaultData = defaultData(uuid)
|
||||
newProps.forEach { member -> member.setter.call(it, member.getter.call(defaultData)) }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
defaultData(uuid).also(::save)
|
||||
}
|
||||
}
|
||||
}
|
||||
-36
@@ -1,36 +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.api.storage.player.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.InstancedPlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreType
|
||||
import com.mongodb.client.MongoClient
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* A [PlayerDataStoreBackend] that stores the [InstancedPlayerData] in a file.
|
||||
*
|
||||
* @author Pebbles
|
||||
* @since October 27, 2024
|
||||
*/
|
||||
abstract class MongoBasedPlayerDataStoreBackend<T : InstancedPlayerData>(
|
||||
val mongoClient: MongoClient, val type: PlayerInstancedDataStoreType, databaseName: String, collectionName: String
|
||||
) : PlayerDataStoreBackend<T>(type) {
|
||||
|
||||
abstract val defaultData: (UUID) -> (T)
|
||||
val collection by lazy {
|
||||
mongoClient.getDatabase(databaseName).getCollection(collectionName)
|
||||
}
|
||||
|
||||
override fun setup(server: MinecraftServer) {
|
||||
// Ping the database to ensure it's connected
|
||||
mongoClient.listDatabaseNames().first()
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/*
|
||||
package com.cobblemon.mod.common.api.storage.player.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerDataExtension
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.mongodb.client.MongoClient
|
||||
import com.mongodb.client.model.ReplaceOptions
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import org.bson.Document
|
||||
import java.util.UUID
|
||||
import kotlin.reflect.KMutableProperty
|
||||
import kotlin.reflect.full.memberProperties
|
||||
|
||||
class MongoPlayerDataAdapter(
|
||||
mongoClient: MongoClient,
|
||||
databaseName: String
|
||||
) : PlayerDataStoreAdapter {
|
||||
|
||||
companion object {
|
||||
val gson = GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(PlayerDataExtension::class.java, PlayerDataExtensionAdapter)
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.create()
|
||||
}
|
||||
|
||||
private val collection = mongoClient.getDatabase(databaseName).getCollection("PlayerDataCollection")
|
||||
|
||||
override fun load(uuid: UUID): PlayerData {
|
||||
val filter = Document("uuid", uuid.toString())
|
||||
val document = collection.find(filter).first()
|
||||
|
||||
return if (document != null) {
|
||||
val jsonStr = document.toJson()
|
||||
gson.fromJson(jsonStr, PlayerData::class.java).also {
|
||||
val newProps = it::class.memberProperties.filterIsInstance<KMutableProperty<*>>().filter { member -> member.getter.call(it) == null }
|
||||
if (newProps.isNotEmpty()) {
|
||||
val defaultData = PlayerData.defaultData(uuid)
|
||||
newProps.forEach { member -> member.setter.call(it, member.getter.call(defaultData)) }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PlayerData.defaultData(uuid).also(::save)
|
||||
}
|
||||
}
|
||||
|
||||
override fun save(playerData: PlayerData) {
|
||||
val jsonStr = gson.toJson(playerData)
|
||||
val document = Document.parse(jsonStr)
|
||||
document.put("uuid", playerData.uuid.toString())
|
||||
val filter = Document("uuid", playerData.uuid.toString())
|
||||
|
||||
collection.replaceOne(filter, document, ReplaceOptions().upsert(true))
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
-49
@@ -1,49 +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.api.storage.player.adapter
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.storage.player.GeneralPlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerDataExtension
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.mongodb.client.MongoClient
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* A [PlayerDataStoreBackend] for [GeneralPlayerData]
|
||||
*
|
||||
* @author Apion
|
||||
* @since February 21, 2024
|
||||
*/
|
||||
class PlayerDataMongoBackend(mongoClient: MongoClient, databaseName: String, collectionName: String): MongoBackedPlayerDataStoreBackend<GeneralPlayerData>(
|
||||
mongoClient, databaseName, collectionName, PlayerInstancedDataStoreTypes.GENERAL
|
||||
){
|
||||
override val defaultData = { forPlayer: UUID -> GeneralPlayerData(
|
||||
uuid = forPlayer,
|
||||
starterPrompted = false,
|
||||
starterLocked = !Cobblemon.starterConfig.allowStarterOnJoin,
|
||||
starterSelected = false,
|
||||
starterUUID = null,
|
||||
keyItems = mutableSetOf(),
|
||||
extraData = mutableMapOf(),
|
||||
battleTheme = CobblemonSounds.PVP_BATTLE.location
|
||||
)}
|
||||
|
||||
override val gson = GsonBuilder()
|
||||
.registerTypeAdapter(PlayerDataExtension::class.java, PlayerDataExtensionAdapter)
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.create()
|
||||
|
||||
override val classToken = TypeToken.get(GeneralPlayerData::class.java)
|
||||
}
|
||||
@@ -134,12 +134,12 @@ class Toast(
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes [expire] after the specified [seconds].
|
||||
* Invokes [expire] after the specified [ticks].
|
||||
*
|
||||
* @param seconds The number of seconds before the lifecycle of this toast ends.
|
||||
* @param ticks The number of ticks before the lifecycle of this toast ends.
|
||||
*/
|
||||
fun expireAfter(seconds: Float) {
|
||||
afterOnServer(seconds = seconds) {
|
||||
fun expireAfter(ticks: Int) {
|
||||
afterOnServer(ticks = ticks) {
|
||||
this.expire()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
@@ -33,6 +32,7 @@ import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.Iterable
|
||||
import kotlin.collections.MutableSet
|
||||
@@ -61,30 +61,8 @@ object BattleBuilder {
|
||||
healFirst: Boolean = false,
|
||||
partyAccessor: (ServerPlayer) -> PartyStore = { it.party() }
|
||||
): BattleStartResult {
|
||||
|
||||
val adjustLevel = battleFormat.adjustLevel
|
||||
val team1 = partyAccessor(player1).toBattleTeam(clone = cloneParties || adjustLevel > 0, healPokemon = healFirst, leadingPokemonPlayer1).sortedBy { it.health <= 0 }
|
||||
val team2 = partyAccessor(player2).toBattleTeam(clone = cloneParties || adjustLevel > 0, healPokemon = healFirst, leadingPokemonPlayer2).sortedBy { it.health <= 0 }
|
||||
|
||||
val battlePartyStores = mutableListOf<PlayerPartyStore>()
|
||||
|
||||
if (adjustLevel > 0) {
|
||||
val tempStoreP1 = PlayerPartyStore(player1.uuid)
|
||||
team1.forEachIndexed { index, it ->
|
||||
it.effectedPokemon.level = adjustLevel
|
||||
it.effectedPokemon.heal()
|
||||
tempStoreP1.set(index, it.effectedPokemon)
|
||||
}
|
||||
battlePartyStores.add(tempStoreP1)
|
||||
|
||||
val tempStoreP2 = PlayerPartyStore(player2.uuid)
|
||||
team2.forEachIndexed { index, it ->
|
||||
it.effectedPokemon.level = adjustLevel
|
||||
it.effectedPokemon.heal()
|
||||
tempStoreP2.set(index, it.effectedPokemon)
|
||||
}
|
||||
battlePartyStores.add(tempStoreP2)
|
||||
}
|
||||
val team1 = partyAccessor(player1).toBattleTeam(clone = cloneParties, healPokemon = healFirst, leadingPokemonPlayer1).sortedBy { it.health <= 0 }
|
||||
val team2 = partyAccessor(player2).toBattleTeam(clone = cloneParties, healPokemon = healFirst, leadingPokemonPlayer2).sortedBy { it.health <= 0 }
|
||||
|
||||
val player1Actor = PlayerBattleActor(player1.uuid, team1)
|
||||
val player2Actor = PlayerBattleActor(player2.uuid, team2)
|
||||
@@ -114,7 +92,6 @@ object BattleBuilder {
|
||||
).ifSuccessful {
|
||||
player1Actor.battleTheme = player2.getBattleTheme()
|
||||
player2Actor.battleTheme = player1.getBattleTheme()
|
||||
it.battlePartyStores.addAll(battlePartyStores)
|
||||
result = SuccessfulBattleStart(it)
|
||||
}
|
||||
result
|
||||
@@ -125,44 +102,28 @@ object BattleBuilder {
|
||||
|
||||
@JvmOverloads
|
||||
fun pvp2v2(
|
||||
players: List<ServerPlayer> = emptyList(),
|
||||
leadingPokemon: List<UUID> = emptyList(),
|
||||
battleFormat: BattleFormat = BattleFormat.GEN_9_MULTI,
|
||||
cloneParties: Boolean = false,
|
||||
healFirst: Boolean = false,
|
||||
partyAccessor: (ServerPlayer) -> PartyStore = { it.party() }
|
||||
players: List<ServerPlayer> = emptyList(),
|
||||
leadingPokemon: List<UUID> = emptyList(),
|
||||
battleFormat: BattleFormat = BattleFormat.GEN_9_MULTI,
|
||||
cloneParties: Boolean = false,
|
||||
healFirst: Boolean = false,
|
||||
partyAccessor: (ServerPlayer) -> PartyStore = { it.party() }
|
||||
): BattleStartResult {
|
||||
val adjustLevel = battleFormat.adjustLevel
|
||||
val teams = players.mapIndexed { index, it ->
|
||||
partyAccessor(it).toBattleTeam(
|
||||
clone = cloneParties || adjustLevel > 0,
|
||||
clone = cloneParties,
|
||||
healPokemon = healFirst,
|
||||
leadingPokemon[index]
|
||||
).sortedBy { it.health <= 0 }
|
||||
}
|
||||
val playerActors = teams.mapIndexed { index, team -> PlayerBattleActor(players[index].uuid, team)}.toMutableList()
|
||||
|
||||
val battlePartyStores = mutableListOf<PlayerPartyStore>()
|
||||
|
||||
if (adjustLevel > 0) {
|
||||
teams.forEachIndexed { playerIndex, battleTeam ->
|
||||
val tempStore = PlayerPartyStore(players[playerIndex].uuid)
|
||||
battleTeam.forEachIndexed { pokemonIndex, battlePokemon ->
|
||||
battlePokemon.effectedPokemon.level = adjustLevel
|
||||
battlePokemon.effectedPokemon.heal()
|
||||
tempStore.set(pokemonIndex, battlePokemon.effectedPokemon)
|
||||
}
|
||||
battlePartyStores.add(tempStore)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
val errors = ErroredBattleStart()
|
||||
|
||||
if (players.size != TeamManager.MAX_TEAM_MEMBER_COUNT * 2) {
|
||||
if (players.size != BattleRegistry.MAX_TEAM_MEMBER_COUNT * 2) {
|
||||
playerActors.forEach {actor ->
|
||||
errors.participantErrors[actor] += BattleStartError.incorrectActorCount(
|
||||
requiredCount = TeamManager.MAX_TEAM_MEMBER_COUNT * 2,
|
||||
requiredCount = BattleRegistry.MAX_TEAM_MEMBER_COUNT * 2,
|
||||
hadCount = players.size
|
||||
)
|
||||
}
|
||||
@@ -209,8 +170,6 @@ object BattleBuilder {
|
||||
|
||||
playerActors[2].battleTheme = players[0].getBattleTheme()
|
||||
playerActors[3].battleTheme = players[0].getBattleTheme()
|
||||
|
||||
it.battlePartyStores.addAll(battlePartyStores)
|
||||
}
|
||||
errors
|
||||
} else {
|
||||
@@ -314,7 +273,7 @@ object BattleBuilder {
|
||||
): BattleStartResult {
|
||||
val playerTeam = party.toBattleTeam(clone = cloneParties, healPokemon = healFirst, leadingPokemon = leadingPokemon)
|
||||
val playerActor = PlayerBattleActor(player.uuid, playerTeam)
|
||||
val npcParty = npcEntity.getPartyForChallenge(player)
|
||||
val npcParty = npcEntity.party?.getParty(player, npcEntity)
|
||||
val errors = ErroredBattleStart()
|
||||
|
||||
if (playerActor.pokemonList.size < battleFormat.battleType.slotsPerActor) {
|
||||
@@ -334,12 +293,12 @@ object BattleBuilder {
|
||||
return errors
|
||||
}
|
||||
|
||||
val npcActor = NPCBattleActor(npcEntity, npcParty, npcEntity.skill ?: npcEntity.npc.skill)
|
||||
val npcActor = NPCBattleActor(npcEntity, npcParty, npcEntity.skill ?: 0)
|
||||
// if (npcEntity.battleIds.get().isPresent) {
|
||||
// errors.participantErrors[npcActor] += BattleStartError.alreadyInBattle(npcActor)
|
||||
// }
|
||||
|
||||
if (npcActor.pokemonList.filter { it.health > 0 }.size < battleFormat.battleType.slotsPerActor) {
|
||||
if (npcActor.pokemonList.size < battleFormat.battleType.slotsPerActor) {
|
||||
errors.participantErrors[npcActor] += BattleStartError.insufficientPokemon(
|
||||
actorEntity = npcEntity,
|
||||
requiredCount = battleFormat.battleType.slotsPerActor,
|
||||
@@ -497,10 +456,6 @@ open class ErroredBattleStart(
|
||||
return this
|
||||
}
|
||||
|
||||
fun sendTo(entities: Collection<Entity>, transformer: (MutableComponent) -> (MutableComponent) = { it }) {
|
||||
entities.forEach { this.sendTo(it, transformer) }
|
||||
}
|
||||
|
||||
fun sendTo(entity: Entity, transformer: (MutableComponent) -> (MutableComponent) = { it }) {
|
||||
errors.forEach { entity.sendSystemMessage(transformer(it.getMessageFor(entity))) }
|
||||
}
|
||||
|
||||
@@ -24,9 +24,8 @@ import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
data class BattleFormat(
|
||||
val mod: String = "cobblemon",
|
||||
val battleType: BattleType = BattleTypes.SINGLES,
|
||||
var ruleSet: Set<String> = setOf(),
|
||||
val gen: Int = 9,
|
||||
var adjustLevel: Int = -1, // Stop gap rule before a more general system for rules enforced by Cobblemon is implemented.
|
||||
val ruleSet: Set<String> = setOf(),
|
||||
val gen: Int = 9
|
||||
) {
|
||||
companion object {
|
||||
val GEN_9_SINGLES = BattleFormat(
|
||||
@@ -50,8 +49,8 @@ data class BattleFormat(
|
||||
)
|
||||
|
||||
val GEN_9_ROYAL = BattleFormat(
|
||||
battleType = BattleTypes.ROYAL,
|
||||
ruleSet = mutableSetOf(BattleRules.OBTAINABLE, BattleRules.PAST, BattleRules.UNOBTAINABLE)
|
||||
battleType = BattleTypes.ROYAL,
|
||||
ruleSet = mutableSetOf(BattleRules.OBTAINABLE, BattleRules.PAST, BattleRules.UNOBTAINABLE)
|
||||
)
|
||||
|
||||
fun loadFromBuffer(buffer: RegistryFriendlyByteBuf): BattleFormat {
|
||||
@@ -59,12 +58,10 @@ data class BattleFormat(
|
||||
val battleType = BattleType.loadFromBuffer(buffer)
|
||||
val ruleSet = mutableSetOf<String>()
|
||||
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) { ruleSet.add(buffer.readString()) }
|
||||
val adjustLevel = buffer.readSizedInt(IntSize.INT)
|
||||
return BattleFormat(
|
||||
mod = mod,
|
||||
battleType = battleType,
|
||||
ruleSet = ruleSet,
|
||||
adjustLevel = adjustLevel
|
||||
ruleSet = ruleSet
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -74,7 +71,6 @@ data class BattleFormat(
|
||||
battleType.saveToBuffer(buffer)
|
||||
buffer.writeSizedInt(IntSize.U_BYTE, ruleSet.size)
|
||||
ruleSet.forEach(buffer::writeString)
|
||||
buffer.writeSizedInt(IntSize.INT, adjustLevel)
|
||||
return buffer
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleStartedPostEvent
|
||||
@@ -17,26 +19,142 @@ import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleChallengeExpiredPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.TeamMemberRemoveNotificationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.TeamRequestExpiredPacket
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.google.gson.GsonBuilder
|
||||
import java.time.Instant
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
object BattleRegistry {
|
||||
|
||||
const val MAX_TEAM_MEMBER_COUNT = 2
|
||||
const val MAX_BATTLE_RADIUS = 15.0
|
||||
class BattleChallenge(
|
||||
val challengeId: UUID,
|
||||
val challengedPlayerUUID: UUID,
|
||||
val selectedPokemonId: UUID,
|
||||
val battleFormat: BattleFormat,
|
||||
var expiryTimeSeconds: Int = 60
|
||||
) {
|
||||
val challengedTime = Instant.now()
|
||||
fun isExpired() = Instant.now().isAfter(challengedTime.plusSeconds(expiryTimeSeconds.toLong()))
|
||||
}
|
||||
|
||||
|
||||
|
||||
class TeamRequest(
|
||||
val requestID: UUID,
|
||||
val requestedPlayerUUID: UUID,
|
||||
var expiryTimeSeconds: Int = 60
|
||||
) {
|
||||
val challengedTime = Instant.now()
|
||||
fun isExpired() = Instant.now().isAfter(challengedTime.plusSeconds(expiryTimeSeconds.toLong()))
|
||||
}
|
||||
class MultiBattleTeam(
|
||||
val teamID: UUID,
|
||||
val teamPlayersUUID: MutableList<UUID>,
|
||||
)
|
||||
|
||||
val gson = GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(ShowdownMoveset::class.java, ShowdownMovesetAdapter)
|
||||
.create()
|
||||
private val battleMap = ConcurrentHashMap<UUID, PokemonBattle>()
|
||||
// Challenger to challenge
|
||||
val pvpChallenges = mutableMapOf<UUID, BattleChallenge>()
|
||||
|
||||
// Multi-battle teams
|
||||
val playerToTeam = mutableMapOf<UUID, MultiBattleTeam>()
|
||||
val multiBattleTeams = mutableMapOf<UUID, MultiBattleTeam>()
|
||||
val multiBattleTeamRequests = mutableMapOf<UUID, TeamRequest>()
|
||||
|
||||
fun onServerStarted() {
|
||||
battleMap.clear()
|
||||
pvpChallenges.clear()
|
||||
playerToTeam.clear()
|
||||
}
|
||||
|
||||
fun removeChallenge(challengerId: UUID, challengeId: UUID? = null, isMultiBattleTeam: Boolean = false) {
|
||||
val existing = pvpChallenges[challengerId] ?: return
|
||||
if (existing.challengeId != challengeId) {
|
||||
return
|
||||
}
|
||||
pvpChallenges.remove(challengerId)
|
||||
if (isMultiBattleTeam) {
|
||||
multiBattleTeams[existing.challengedPlayerUUID]?.teamPlayersUUID?.let {
|
||||
CobblemonNetwork.sendPacketToPlayers(it.mapNotNull { serverPlayer -> serverPlayer.getPlayer() }, BattleChallengeExpiredPacket(existing.challengeId))
|
||||
}
|
||||
} else {
|
||||
existing.challengedPlayerUUID.getPlayer()?.sendPacket(BattleChallengeExpiredPacket(existing.challengeId))
|
||||
}
|
||||
}
|
||||
|
||||
fun removeTeamUpRequest(requesterId: UUID, requestId: UUID? = null) {
|
||||
val existing = multiBattleTeamRequests[requesterId] ?: return
|
||||
if (existing.requestID != requestId) {
|
||||
return
|
||||
}
|
||||
multiBattleTeamRequests.remove(requesterId)
|
||||
existing.requestedPlayerUUID.getPlayer()?.sendPacket(TeamRequestExpiredPacket(existing.requestID))
|
||||
}
|
||||
|
||||
fun onPlayerDisconnect(player: ServerPlayer) {
|
||||
// Stop battles
|
||||
getBattleByParticipatingPlayer(player)?.stop()
|
||||
// Remove player from any teams they may be a part of
|
||||
removeTeamMember(player)
|
||||
}
|
||||
|
||||
fun removeTeamMember(player: ServerPlayer) {
|
||||
val teamEntry = playerToTeam[player.uuid]
|
||||
if (teamEntry != null) {
|
||||
teamEntry.teamPlayersUUID.remove(player.uuid)
|
||||
playerToTeam.remove(player.uuid)
|
||||
val notificationPacket = TeamMemberRemoveNotificationPacket(player.uuid)
|
||||
CobblemonNetwork.sendPacketToPlayer(player, notificationPacket)
|
||||
|
||||
// Notify remaining members that the player has left the group
|
||||
server()?.let {
|
||||
val teamServerPlayers = teamEntry.teamPlayersUUID.mapNotNull { uuid -> it.playerList.getPlayer(uuid) }
|
||||
CobblemonNetwork.sendPacketToPlayers(teamServerPlayers, notificationPacket)
|
||||
}
|
||||
|
||||
|
||||
if (teamEntry.teamPlayersUUID.count() == 1) {
|
||||
val remainingPlayerUUID = teamEntry.teamPlayersUUID.first()
|
||||
|
||||
// Remove any outstanding battle requests for the team
|
||||
// TODO: Find a way to not do a reverse map look up
|
||||
val battleChallengeEntry = pvpChallenges.entries.firstOrNull { it -> it.value.challengedPlayerUUID == teamEntry.teamID }
|
||||
if (battleChallengeEntry != null) {
|
||||
val battleChallenge = battleChallengeEntry.value
|
||||
removeChallenge(battleChallengeEntry.key, battleChallenge.challengeId, true)
|
||||
|
||||
// Notify opposing team that the challenge was cancelled
|
||||
val opposingTeam = multiBattleTeams[battleChallengeEntry.key]
|
||||
opposingTeam?.teamPlayersUUID?.map { it.getPlayer() }?.forEach { serverPlayerEntity ->
|
||||
serverPlayerEntity?.sendSystemMessage(lang("challenge.multi.decline.disband"))
|
||||
}
|
||||
}
|
||||
|
||||
// Disband the team
|
||||
playerToTeam.remove(remainingPlayerUUID)
|
||||
multiBattleTeams.remove(teamEntry.teamID)
|
||||
|
||||
// Notify remaining player that the team has been disbanded
|
||||
server()?.let {
|
||||
it.playerList.getPlayer(remainingPlayerUUID)?.let { serverPlayerEntity ->
|
||||
CobblemonNetwork.sendPacketToPlayer(serverPlayerEntity, TeamMemberRemoveNotificationPacket(remainingPlayerUUID))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,7 +40,7 @@ class BattleSide(vararg val actors: BattleActor) {
|
||||
activePokemon.forEach {
|
||||
val entity = it.battlePokemon?.entity ?: return@forEach
|
||||
entity.cry()
|
||||
if(entity.pokemon.shiny && entity.pokemon.isWild()) {
|
||||
if(entity.pokemon.shiny) {
|
||||
SpawnSnowstormEntityParticlePacket(cobblemonResource("shiny_ring"), entity.id, listOf("shiny_particles", "middle"))
|
||||
.sendToPlayersAround(entity.x, entity.y, entity.z, 64.0, entity.level().dimension())
|
||||
}
|
||||
|
||||
@@ -1,194 +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.battles
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.interaction.RequestManager
|
||||
import com.cobblemon.mod.common.api.interaction.ServerPlayerActionRequest
|
||||
import com.cobblemon.mod.common.api.interaction.ServerTeamActionRequest
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.text.aqua
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.TeamManager.MAX_TEAM_MEMBER_COUNT
|
||||
import com.cobblemon.mod.common.battles.TeamManager.MultiBattleTeam
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleChallengeExpiredPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleChallengeNotificationPacket
|
||||
import com.cobblemon.mod.common.util.canInteractWith
|
||||
import com.cobblemon.mod.common.util.getBattleTeam
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Responsible for managing [BattleChallenge]s and initiating PVP [PokemonBattle]s.
|
||||
*
|
||||
* @author Segfault Guy, JazzMcNade
|
||||
* @since October 28th, 2024
|
||||
*/
|
||||
object ChallengeManager : RequestManager<ChallengeManager.BattleChallenge>() {
|
||||
|
||||
init {
|
||||
register(this)
|
||||
}
|
||||
|
||||
const val MAX_BATTLE_RADIUS = 15.0
|
||||
|
||||
/** Mapping of players to their lead Pokemon. */
|
||||
private val selectedLead = mutableMapOf<UUID, UUID>()
|
||||
|
||||
/**
|
||||
* Represents an interaction request between players or teams to start a battle.
|
||||
*
|
||||
* @param sender The player sending this request.
|
||||
* @param receiver The player receiving this request.
|
||||
* @param selectedPokemonId
|
||||
* @param battleFormat
|
||||
* @param expiryTime How long (in seconds) this request is active.
|
||||
*/
|
||||
abstract class BattleChallenge : ServerPlayerActionRequest {
|
||||
abstract val selectedPokemonId: UUID
|
||||
abstract val battleFormat: BattleFormat
|
||||
override val requestID: UUID = UUID.randomUUID()
|
||||
}
|
||||
|
||||
/** PLAYER-to-PLAYER BattleChallenge */
|
||||
data class SinglesBattleChallenge(
|
||||
override val sender: ServerPlayer,
|
||||
override val receiver: ServerPlayer,
|
||||
override val selectedPokemonId: UUID,
|
||||
override val battleFormat: BattleFormat,
|
||||
override val expiryTime: Int = 20
|
||||
) : BattleChallenge() {
|
||||
override val key: String = "challenge"
|
||||
}
|
||||
|
||||
/** TEAM-to-TEAM BattleChallenge */
|
||||
data class MultiBattleChallenge(
|
||||
override val sender: ServerPlayer,
|
||||
override val receiver: ServerPlayer,
|
||||
override val selectedPokemonId: UUID,
|
||||
override val battleFormat: BattleFormat, // TODO force this to be multi battletype
|
||||
override val expiryTime: Int = 20
|
||||
) : BattleChallenge(), ServerTeamActionRequest {
|
||||
override val key: String = "challenge.multi"
|
||||
override val senderTeam: MultiBattleTeam = sender.getBattleTeam() ?: throw IllegalArgumentException("Sending player is not part of a team!")
|
||||
override val receiverTeam: MultiBattleTeam = receiver.getBattleTeam() ?: throw IllegalArgumentException("Target player is not part of a team!")
|
||||
}
|
||||
|
||||
override fun expirationPacket(request: BattleChallenge): NetworkPacket<*> = BattleChallengeExpiredPacket(request)
|
||||
|
||||
override fun notificationPacket(request: BattleChallenge): NetworkPacket<*> = BattleChallengeNotificationPacket(request)
|
||||
|
||||
override fun onDecline(request: BattleChallenge) {
|
||||
request.notifySender(true, "decline.sender", request.receiver.name.copy().aqua(), request.battleFormat.battleType.displayName)
|
||||
request.notifyReceiver(false, "decline.receiver", request.sender.name.copy().aqua(), request.battleFormat.battleType.displayName)
|
||||
}
|
||||
|
||||
override fun onSend(request: BattleChallenge) {
|
||||
request.notifySender(false, "sent", request.receiver.name.copy().aqua(), request.battleFormat.battleType.displayName)
|
||||
request.notifyReceiver(false, "received", request.sender.name.copy().aqua(), request.battleFormat.battleType.displayName)
|
||||
}
|
||||
|
||||
override fun onAccept(request: BattleChallenge) {
|
||||
if (request is MultiBattleChallenge) {
|
||||
val players = request.receiverTeam.teamPlayers + request.senderTeam.teamPlayers
|
||||
val leadPokemon = players.mapNotNull { it.party().first().uuid }
|
||||
BattleBuilder.pvp2v2(
|
||||
players,
|
||||
leadPokemon,
|
||||
request.battleFormat
|
||||
).ifErrored {
|
||||
erredStart -> erredStart.sendTo(players) { it.red() }
|
||||
}
|
||||
}
|
||||
else {
|
||||
BattleBuilder.pvp1v1(
|
||||
request.receiver,
|
||||
request.sender,
|
||||
selectedLead.get(request.receiver.uuid),
|
||||
selectedLead.get(request.sender.uuid),
|
||||
request.battleFormat
|
||||
).ifErrored {
|
||||
it.sendTo(request.receiver) { it.red() }
|
||||
it.sendTo(request.sender) { it.red() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isValidInteraction(player: ServerPlayer, target: ServerPlayer) = player.canInteractWith(target, Cobblemon.config.BattlePvPMaxDistance)
|
||||
|
||||
override fun canAccept(request: BattleChallenge): Boolean {
|
||||
if (request is MultiBattleChallenge) {
|
||||
val existingReceiverTeam = TeamManager.getTeam(request.receiverID)
|
||||
val existingSenderTeam = TeamManager.getTeam(request.senderID)
|
||||
// validate teams
|
||||
if (existingReceiverTeam == null || existingSenderTeam == null) {
|
||||
request.notifySender(true, "error.missing_team")
|
||||
request.notifyReceiver(true, "error.missing_team")
|
||||
return false;
|
||||
}
|
||||
|
||||
val players = existingReceiverTeam.teamPlayers + existingSenderTeam.teamPlayers
|
||||
val leads = players.mapNotNull { it.party().first().uuid }
|
||||
val farAwayPlayer = this.validateProximity(players)
|
||||
// validate team sizes
|
||||
if (players.count() != MAX_TEAM_MEMBER_COUNT * 2) {
|
||||
request.notifySender(true, "error.invalid_team_size", MAX_TEAM_MEMBER_COUNT)
|
||||
request.notifyReceiver(true, "error.invalid_team_size", MAX_TEAM_MEMBER_COUNT)
|
||||
}
|
||||
// validate parties
|
||||
else if (leads.count() != players.count()) {
|
||||
request.notifySender(true, "error.insufficient_pokemon")
|
||||
request.notifyReceiver(true, "error.insufficient_pokemon")
|
||||
}
|
||||
// validate dimension
|
||||
else if (this.validateDimension(players)) {
|
||||
request.notifySender(true, "error.player_different_dimension")
|
||||
request.notifyReceiver(true, "error.player_different_dimension")
|
||||
}
|
||||
// validate proximity
|
||||
else if (farAwayPlayer != null) {
|
||||
request.notifySender(true, "error.player_distance", farAwayPlayer.name.copy().aqua())
|
||||
request.notifyReceiver(true, "error.player_distance", farAwayPlayer.name.copy().aqua())
|
||||
}
|
||||
else return true
|
||||
}
|
||||
else {
|
||||
// validate parties
|
||||
if (request.receiver.party().none()) {
|
||||
request.notifySender(true, "error.insufficient_pokemon.other", request.receiver.name.copy().aqua())
|
||||
request.notifyReceiver(true, "error.insufficient_pokemon.self")
|
||||
}
|
||||
else if (request.sender.party().none()) {
|
||||
request.notifySender(true, "error.insufficient_pokemon.self")
|
||||
request.notifyReceiver(true, "error.insufficient_pokemon.other", request.sender.name.copy().aqua())
|
||||
}
|
||||
// TODO worth validating size and party health here? BattleBuilder already does it
|
||||
else return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun validateDimension(players: Collection<ServerPlayer>): Boolean {
|
||||
val dimension = players.first().level().dimension()
|
||||
val playerInWrongDimension = players.firstOrNull { it.level().dimension() != dimension }
|
||||
return playerInWrongDimension != null
|
||||
}
|
||||
|
||||
private fun validateProximity(players: Collection<ServerPlayer>): ServerPlayer? {
|
||||
// Check if all players are nearby
|
||||
var averagePos = Vec3(0.0, 0.0, 0.0)
|
||||
players.forEach { averagePos = averagePos.add(it.position().multiply(1.0 / players.count(), 0.0, 1.0 / players.count())) }
|
||||
val farAwayPlayer = players.firstOrNull { it.position().subtract(0.0, it.position().y, 0.0).distanceToSqr(averagePos) > MAX_BATTLE_RADIUS * MAX_BATTLE_RADIUS }
|
||||
return farAwayPlayer
|
||||
}
|
||||
|
||||
fun setLead(player: ServerPlayer, lead: UUID) = this.selectedLead.put(player.uuid, lead)
|
||||
}
|
||||
@@ -35,12 +35,6 @@ interface Targetable {
|
||||
}
|
||||
}
|
||||
|
||||
fun getOppositeOpponent() : Targetable {
|
||||
val digit = getDigit()
|
||||
val sideSize = getFormat().battleType.pokemonPerSide
|
||||
return getAllActivePokemon().first { pokemon -> !pokemon.isAllied(this) && (sideSize - pokemon.getDigit() + 1) - digit == 0 }
|
||||
}
|
||||
|
||||
fun getAdjacentAllies() = getAdjacent().filter { it.isAllied(this) }
|
||||
fun getAdjacentOpponents() = getAdjacent().filterNot { it.isAllied(this) }
|
||||
|
||||
|
||||
@@ -403,7 +403,6 @@ class ShowdownPokemon {
|
||||
lateinit var ability: String
|
||||
var baseTypes = mutableListOf<String>()
|
||||
var types = mutableListOf<String>()
|
||||
var commanding: Boolean = false
|
||||
var reviving: Boolean = false
|
||||
|
||||
val uuid: UUID by lazy { UUID.fromString(details.split(",")[1].trim()) }
|
||||
@@ -413,7 +412,6 @@ class ShowdownPokemon {
|
||||
buffer.writeString(condition)
|
||||
buffer.writeBoolean(active)
|
||||
buffer.writeBoolean(reviving)
|
||||
buffer.writeBoolean(commanding)
|
||||
buffer.writeSizedInt(IntSize.U_BYTE, moves.size)
|
||||
moves.forEach(buffer::writeString)
|
||||
buffer.writeString(baseAbility)
|
||||
@@ -431,7 +429,6 @@ class ShowdownPokemon {
|
||||
condition = buffer.readString()
|
||||
active = buffer.readBoolean()
|
||||
reviving = buffer.readBoolean()
|
||||
commanding = buffer.readBoolean()
|
||||
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
|
||||
moves.add(buffer.readString())
|
||||
}
|
||||
|
||||
@@ -9,11 +9,14 @@
|
||||
package com.cobblemon.mod.common.battles
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.*
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BasicContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.Effect
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.MissingContext
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
@@ -60,12 +63,12 @@ object ShowdownInterpreter {
|
||||
updateInstructionParser["-ability"] = { _, instructionSet, message, _ -> AbilityInstruction(instructionSet, message) }
|
||||
updateInstructionParser["-activate"] = { _, instructionSet, message, _ -> ActivateInstruction(instructionSet, message) }
|
||||
updateInstructionParser["bagitem"] = { _, _, message, _ -> BagItemInstruction(message) }
|
||||
updateInstructionParser["-boost"] = { battle, _, message, _ -> BoostInstruction(battle, message, true) }
|
||||
updateInstructionParser["-boost"] = { _, instructionSet, message, remainingLines -> BoostInstruction(instructionSet, message, remainingLines, true) }
|
||||
updateInstructionParser["-block"] = { _, _, message, _ -> BlockInstruction(message) }
|
||||
updateInstructionParser["cant"] = { _, _, message, _ -> CantInstruction(message) }
|
||||
updateInstructionParser["-clearallboost"] = { _, _, message, _ -> ClearAllBoostInstruction(message) }
|
||||
updateInstructionParser["-clearnegativeboost"] = { _, _, message, _ -> ClearNegativeBoostInstruction(message) }
|
||||
updateInstructionParser["-clearboost"] = { _, _, message, _ -> ClearBoostInstruction(message) }
|
||||
updateInstructionParser["-clearboost"] = { _, _, message, _ -> ClearBoostInstruction(message) }
|
||||
updateInstructionParser["-copyboost"] = { _, _, message, _ -> CopyBoostInstruction(message) }
|
||||
updateInstructionParser["-crit"] = { _, instructionSet, message, _ -> CritInstruction(message, instructionSet) }
|
||||
updateInstructionParser["-curestatus"] = { _, _, message, _ -> CureStatusInstruction(message) }
|
||||
@@ -105,7 +108,7 @@ object ShowdownInterpreter {
|
||||
updateInstructionParser["-terastallize"] = { _, _, message, _ -> TerastallizeInstruction(message) }
|
||||
updateInstructionParser["-transform"] = { battle, _, message, _ -> TransformInstruction(battle, message) }
|
||||
updateInstructionParser["turn"] = { _, _, message, _ -> TurnInstruction(message) }
|
||||
updateInstructionParser["-unboost"] = { battle, _, message, _ -> BoostInstruction(battle, message, false) }
|
||||
updateInstructionParser["-unboost"] = { _, instructionSet, message, remainingLines -> BoostInstruction(instructionSet, message, remainingLines, false) }
|
||||
updateInstructionParser["upkeep"] = { _, _, _, _ -> UpkeepInstruction() }
|
||||
updateInstructionParser["-weather"] = { _, _, message, _ -> WeatherInstruction(message) }
|
||||
updateInstructionParser["win"] = { _, _, message, _ -> WinInstruction(message) }
|
||||
@@ -145,74 +148,59 @@ object ShowdownInterpreter {
|
||||
*/
|
||||
fun getSendoutPosition(battle: PokemonBattle, activePokemon: ActiveBattlePokemon, battleActor: BattleActor): Vec3? {
|
||||
val pnx = activePokemon.getPNX()
|
||||
val actorEntityPosList = battleActor.getSide().actors.mapNotNull { if (it is EntityBackedBattleActor<*>) it.initialPos else null }
|
||||
val actorEntityPos = if (actorEntityPosList.size == 1)
|
||||
actorEntityPosList[0]
|
||||
else if (actorEntityPosList.size > 1)
|
||||
actorEntityPosList.fold(Vec3(0.0, 0.0, 0.0)) { acc, vec3 -> acc.add(vec3.scale(1.0 / actorEntityPosList.size)) }
|
||||
val entityPosList = battleActor.getSide().actors.mapNotNull { if (it is EntityBackedBattleActor<*>) it.initialPos else null }
|
||||
var entityPos = if (entityPosList.size == 1)
|
||||
entityPosList[0]
|
||||
else if (entityPosList.size > 1)
|
||||
entityPosList.fold(Vec3(0.0, 0.0, 0.0)) { acc, vec3 -> acc.add(vec3.scale(1.0 / entityPosList.size)) }
|
||||
else
|
||||
null
|
||||
val opposingActorEntityList = battleActor.getSide().getOppositeSide().actors.mapNotNull { if (it is EntityBackedBattleActor<*>) it.initialPos else null }
|
||||
val opposingEntityPos = if (opposingActorEntityList.size == 1)
|
||||
opposingActorEntityList[0]
|
||||
else if (opposingActorEntityList.size > 1) {
|
||||
// If multiple actors per side, avg their position
|
||||
opposingActorEntityList.fold(Vec3(0.0, 0.0, 0.0)) { acc, vec3 -> acc.add(vec3.scale(1.0 / opposingActorEntityList.size)) }
|
||||
}
|
||||
val opposingEntityList = battleActor.getSide().getOppositeSide().actors.mapNotNull { if (it is EntityBackedBattleActor<*>) it.initialPos else null }
|
||||
val opposingEntityPos = if (opposingEntityList.size == 1)
|
||||
opposingEntityList[0]
|
||||
else if (opposingEntityList.size > 1)
|
||||
opposingEntityList.fold(Vec3(0.0, 0.0, 0.0)) { acc, vec3 -> acc.add(vec3.scale(1.0 / opposingEntityList.size)) }
|
||||
else null
|
||||
|
||||
var actorOffset = actorEntityPos?.let { opposingEntityPos?.subtract(it) }
|
||||
var result = actorEntityPos
|
||||
if (actorOffset != null) {
|
||||
var widthSum = 4.0 // Leave a constant to allow double/triples alignment to be consistent
|
||||
if (battle.format.battleType.pokemonPerSide == 1) {
|
||||
activePokemon.battlePokemon?.let { battlePokemon ->
|
||||
// sum of the hitbox widths of both pokemon
|
||||
val opposingActivePokemon = (activePokemon.getOppositeOpponent() as ActiveBattlePokemon)
|
||||
val pokemonWidth = battlePokemon.originalPokemon.form.hitbox.width * battlePokemon.originalPokemon.form.baseScale
|
||||
val opposingPokemonWidth = opposingActivePokemon.battlePokemon?.let {
|
||||
it.originalPokemon.form.hitbox.width * it.originalPokemon.form.baseScale
|
||||
} ?: pokemonWidth
|
||||
widthSum = (pokemonWidth + opposingPokemonWidth) / 2.0 // Only care about the front half of each hitbox
|
||||
}
|
||||
}
|
||||
var baseOffset = entityPos?.let { opposingEntityPos?.subtract(it) }
|
||||
|
||||
val minDistance = 4.0 + widthSum
|
||||
val actorDistance = actorOffset.length()
|
||||
|
||||
if (actorDistance < minDistance) {
|
||||
val temp = actorOffset.scale(minDistance / actorDistance) ?: actorOffset
|
||||
result = actorEntityPos?.subtract(temp.subtract(actorOffset))
|
||||
actorOffset = temp
|
||||
if (baseOffset != null) {
|
||||
val minDistance = if (battle.isPvW) 8.0 else 5.0
|
||||
val length = baseOffset.length()
|
||||
if (length < minDistance) {
|
||||
val temp = baseOffset.scale(minDistance / length) ?: baseOffset
|
||||
entityPos = entityPos?.subtract(temp.subtract(baseOffset))
|
||||
baseOffset = temp
|
||||
}
|
||||
var vector = Vec3(actorOffset.x, 0.0, actorOffset.z).normalize()
|
||||
var vector = Vec3(baseOffset.x, 0.0, baseOffset.z).normalize()
|
||||
vector = vector.cross(Vec3(0.0, 1.0, 0.0))
|
||||
|
||||
if (battle.format.battleType.pokemonPerSide == 1) { // Singles
|
||||
result = result?.add(actorOffset.scale(if (battle.isPvW) 0.4 else 0.3))
|
||||
entityPos = entityPos?.add(baseOffset.scale(if (battle.isPvW) 0.4 else 0.3))
|
||||
activePokemon.battlePokemon?.let { battlePokemon ->
|
||||
val hitbox = battlePokemon.originalPokemon.form.hitbox
|
||||
val scale = battlePokemon.originalPokemon.form.baseScale
|
||||
activePokemon.getAdjacentOpponents()
|
||||
result = result?.add(vector.scale(-0.3 - hitbox.width * scale ))
|
||||
if (hitbox.height * scale > 1.1) {
|
||||
entityPos = entityPos?.add(vector.scale(-1.5 - hitbox.width * scale / 2))
|
||||
}
|
||||
}
|
||||
} else if (battle.format.battleType.pokemonPerSide == 2) { // Doubles/Multi
|
||||
} else if (battle.format.battleType.pokemonPerSide == 2) { // Doubles
|
||||
if (battle.actors.first() !== battle.actors.last()) {
|
||||
val offsetB = if (pnx[2] == 'a') vector.scale(-1.0) else vector
|
||||
result = result?.add(actorOffset.scale(0.33))?.add(offsetB.scale(2.5))
|
||||
entityPos = entityPos?.add(baseOffset.scale(0.33))?.add(offsetB.scale(2.5))
|
||||
}
|
||||
} else if (battle.format.battleType.pokemonPerSide == 3) { // Triples
|
||||
if (battle.actors.first() !== battle.actors.last()) {
|
||||
result = when (pnx[2]) {
|
||||
'a' -> result?.add(actorOffset.scale(0.15))?.add(vector.scale(-3.5))
|
||||
'b' -> result?.add(actorOffset.scale(0.3))
|
||||
'c' -> result?.add(actorOffset.scale(0.15))?.add(vector.scale(3.5))
|
||||
else -> result
|
||||
entityPos = when (pnx[2]) {
|
||||
'a' -> entityPos?.add(baseOffset.scale(0.15))?.add(vector.scale(-3.5))
|
||||
'b' -> entityPos?.add(baseOffset.scale(0.3))
|
||||
'c' -> entityPos?.add(baseOffset.scale(0.15))?.add(vector.scale(3.5))
|
||||
else -> entityPos
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
return entityPos
|
||||
}
|
||||
|
||||
|
||||
@@ -276,16 +264,8 @@ object ShowdownInterpreter {
|
||||
}
|
||||
instructionSet.execute(battle)
|
||||
}
|
||||
catch (e: InvalidInstructionException) {
|
||||
e.message?.let {
|
||||
battle.broadcastChatMessage(it.red())
|
||||
LOGGER.error(it)
|
||||
}
|
||||
|
||||
}
|
||||
catch (e: Exception) {
|
||||
battle.broadcastChatMessage("A fatal error occurred. Please report to developers.".red())
|
||||
LOGGER.error("Caught exception interpreting battle instructions.", e)
|
||||
LOGGER.error("Caught exception interpreting {}", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,201 +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.battles
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.api.interaction.RequestManager
|
||||
import com.cobblemon.mod.common.api.interaction.ServerPlayerActionRequest
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.text.aqua
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.*
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Responsible for managing active [MultiBattleTeam]s and the respective [TeamRequest]s that form them.
|
||||
*
|
||||
* @author Segfault Guy, JazzMcNade
|
||||
* @since October 26th, 2024
|
||||
*/
|
||||
object TeamManager : RequestManager<TeamManager.TeamRequest>() {
|
||||
|
||||
init {
|
||||
register(this)
|
||||
}
|
||||
|
||||
const val MAX_TEAM_MEMBER_COUNT = 2
|
||||
|
||||
/**
|
||||
* Represents an interaction request between players to form a team.
|
||||
*
|
||||
* @param sender The player sending this request.
|
||||
* @param receiver The player receiving this request.
|
||||
* @param expiryTime How long (in seconds) this request is active.
|
||||
*/
|
||||
data class TeamRequest(
|
||||
override val sender: ServerPlayer,
|
||||
override val receiver: ServerPlayer,
|
||||
override val expiryTime: Int = 60
|
||||
) : ServerPlayerActionRequest
|
||||
{
|
||||
override val key: String = "team"
|
||||
override val requestID: UUID = UUID.randomUUID()
|
||||
}
|
||||
// TODO right now team requests are sent PLAYER-to-PLAYER. May want to redesign once larger teams are implemented? Or have a 'team lead' who is responsible for inviting?
|
||||
|
||||
/**
|
||||
* Represents a team for multi battles.
|
||||
*
|
||||
* @param teamPlayers The players that form this team.
|
||||
*/
|
||||
data class MultiBattleTeam(val teamPlayers: MutableList<ServerPlayer>)
|
||||
{
|
||||
constructor(vararg teamPlayers: ServerPlayer) : this(teamPlayers.toMutableList())
|
||||
val teamID: UUID = UUID.randomUUID()
|
||||
val teamPlayersUUID get() = this.teamPlayers.map { it.uuid }
|
||||
}
|
||||
|
||||
// Multi-battle teams
|
||||
private val playerToTeam = mutableMapOf<UUID, MultiBattleTeam>()
|
||||
private val multiBattleTeams = mutableMapOf<UUID, MultiBattleTeam>()
|
||||
|
||||
fun getTeam(player: ServerPlayer) = playerToTeam.get(player.uuid)
|
||||
|
||||
fun getTeam(teamID: UUID) = multiBattleTeams.get(teamID)
|
||||
|
||||
override fun notificationPacket(request: TeamRequest): NetworkPacket<*> = TeamRequestNotificationPacket(request)
|
||||
|
||||
override fun expirationPacket(request: TeamRequest): NetworkPacket<*> = TeamRequestExpiredPacket(request)
|
||||
|
||||
/** Removes the [teamEntry]. */
|
||||
fun disbandTeam(teamEntry: MultiBattleTeam) {
|
||||
// Remove any outstanding team-up requests from the team members
|
||||
val pendingTeamRequests = teamEntry.teamPlayersUUID.mapNotNull { member -> this.getOutboundRequest(member) } // team requests are by player
|
||||
pendingTeamRequests.forEach { this.cancelRequest(it) }
|
||||
|
||||
// Decline any inbound requests for the team
|
||||
ChallengeManager.getInboundRequests(teamEntry.teamID)?.forEach { ChallengeManager.declineRequest(it) } // multi challenges are by team
|
||||
|
||||
// Remove any outstanding multibattle challenges from the team
|
||||
ChallengeManager.getOutboundRequest(teamEntry.teamID)?.let { ChallengeManager.cancelRequest(it) } // multi challenges are by team
|
||||
|
||||
// TODO have a way to get all types of requests targeting a team instead of just querying the ChallengeManger
|
||||
|
||||
// Notify remaining players that the team has been disbanded
|
||||
teamEntry.teamPlayersUUID.forEach { member ->
|
||||
playerToTeam.remove(member)
|
||||
member.getPlayer()?.let { CobblemonNetwork.sendPacketToPlayer(it, TeamMemberRemoveNotificationPacket(it.uuid)) }
|
||||
}
|
||||
|
||||
// Disband the team
|
||||
multiBattleTeams.remove(teamEntry.teamID)
|
||||
}
|
||||
|
||||
/** Removes a [player] from their [MultiBattleTeam]. Will disband the team if the last remaining member. */
|
||||
fun removeTeamMember(player: ServerPlayer) {
|
||||
val teamEntry = playerToTeam.get(player.uuid) ?: return
|
||||
teamEntry.teamPlayers.remove(player)
|
||||
playerToTeam.remove(player.uuid)
|
||||
|
||||
val notificationPacket = TeamMemberRemoveNotificationPacket(player.uuid)
|
||||
CobblemonNetwork.sendPacketToPlayer(player, notificationPacket)
|
||||
// Notify remaining members that the player has left the group
|
||||
val teamServerPlayers = teamEntry.teamPlayersUUID.mapNotNull { it.getPlayer() }
|
||||
CobblemonNetwork.sendPacketToPlayers(teamServerPlayers, notificationPacket)
|
||||
|
||||
if (teamEntry.teamPlayersUUID.count() == 1) this.disbandTeam(teamEntry)
|
||||
}
|
||||
|
||||
/** Adds [player] as a member of existing [team]. */
|
||||
fun joinTeam(player: ServerPlayer, team: MultiBattleTeam) {
|
||||
// decline any other incoming requests to team up
|
||||
this.getInboundRequests(player.uuid)?.forEach { this.cancelRequest(it) } // this is guaranteed to not contain the request currently being accepted
|
||||
|
||||
// notify the team
|
||||
val teamNotifyPacket = TeamMemberAddNotificationPacket(
|
||||
player.uuid,
|
||||
player.name.copy()
|
||||
)
|
||||
CobblemonNetwork.sendPacketToPlayers(team.teamPlayers, teamNotifyPacket)
|
||||
|
||||
team.teamPlayers.add(player)
|
||||
playerToTeam.put(player.uuid, team)
|
||||
|
||||
// notify the joiner
|
||||
val joinerPacket = TeamJoinNotificationPacket(
|
||||
team.teamPlayersUUID,
|
||||
team.teamPlayers.mapNotNull { it.name.plainCopy() }
|
||||
)
|
||||
CobblemonNetwork.sendPacketToPlayer(player, joinerPacket)
|
||||
}
|
||||
|
||||
/** Creates and registers a [MultiBattleTeam] from [players]. */
|
||||
fun createTeam(vararg players: ServerPlayer): MultiBattleTeam {
|
||||
// decline any other incoming requests to team up
|
||||
players.forEach {
|
||||
this.getInboundRequests(it.uuid)?.forEach { this.cancelRequest(it) } // this is guaranteed to not contain the request currently being accepted
|
||||
}
|
||||
|
||||
val team = MultiBattleTeam(*players)
|
||||
this.multiBattleTeams.put(team.teamID, team)
|
||||
|
||||
// Notify the team
|
||||
val joinerPacket = TeamJoinNotificationPacket(
|
||||
team.teamPlayersUUID,
|
||||
players.mapNotNull { it.name.plainCopy() }
|
||||
)
|
||||
|
||||
players.forEach {
|
||||
this.playerToTeam.put(it.uuid, team)
|
||||
CobblemonNetwork.sendPacketToPlayer(it, joinerPacket)
|
||||
}
|
||||
|
||||
return team
|
||||
}
|
||||
|
||||
override fun onAccept(request: TeamRequest) {
|
||||
super.onAccept(request) // notification
|
||||
val existingTeam = this.getTeam(request.sender)
|
||||
if (existingTeam == null)
|
||||
this.createTeam(request.receiver, request.sender)
|
||||
else
|
||||
this.joinTeam(request.receiver, existingTeam)
|
||||
}
|
||||
|
||||
override fun canAccept(request: TeamRequest): Boolean {
|
||||
val existingReceiverTeam = this.getTeam(request.receiverID)
|
||||
val existingSenderTeam = this.getTeam(request.senderID)
|
||||
if (request.sender.party().none()) {
|
||||
request.notifySender(true, "error.no_pokemon")
|
||||
request.notifyReceiver(true, "error.no_pokemon")
|
||||
}
|
||||
else if (existingReceiverTeam != null) {
|
||||
request.notifySender(true, "error.existing_team.other", request.receiver.name.copy().aqua())
|
||||
request.notifyReceiver(true, "error.existing_team.self")
|
||||
}
|
||||
else if (existingSenderTeam != null && existingSenderTeam.teamPlayersUUID.count() >= MAX_TEAM_MEMBER_COUNT) {
|
||||
request.notifySender(true, "error.max_team_size.other", request.receiver.name.copy().aqua())
|
||||
request.notifyReceiver(true, "error.max_team_size.self")
|
||||
}
|
||||
else {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isValidInteraction(player: ServerPlayer, target: ServerPlayer): Boolean = player.canInteractWith(target, Cobblemon.config.tradeMaxDistance)
|
||||
|
||||
override fun onLogoff(sender: ServerPlayer) {
|
||||
super.onLogoff(sender)
|
||||
// Remove player from any teams they may be a part of
|
||||
this.removeTeamMember(sender)
|
||||
}
|
||||
}
|
||||
+7
-9
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.InvalidInstructionException
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
@@ -20,6 +19,7 @@ import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -33,25 +33,19 @@ import java.util.concurrent.CompletableFuture
|
||||
* @author Hiroku
|
||||
* @since August 20th, 2022
|
||||
*/
|
||||
class BoostInstruction(battle: PokemonBattle, val message: BattleMessage, val isBoost: Boolean = true): ActionEffectInstruction {
|
||||
class BoostInstruction(val instructionSet: InstructionSet, val message: BattleMessage, val remainingLines: Iterator<BattleMessage>, val isBoost: Boolean = true): ActionEffectInstruction {
|
||||
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
|
||||
override var holds = mutableSetOf<String>()
|
||||
override val id = cobblemonResource("boost")
|
||||
|
||||
val pokemon = message.battlePokemon(0, battle) ?: throw InvalidInstructionException(message)
|
||||
val statKey = message.argumentAt(1) ?: throw InvalidInstructionException(message)
|
||||
val stages = message.argumentAt(2)?.toInt() ?: throw InvalidInstructionException(message)
|
||||
val stat = Stats.getStat(statKey).displayName
|
||||
|
||||
override fun preActionEffect(battle: PokemonBattle) {
|
||||
|
||||
}
|
||||
|
||||
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
|
||||
if (stages == 0) return // only play effect if there was a stat change
|
||||
battle.dispatch {
|
||||
val actionEffect = if (isBoost) BOOST_EFFECT else UNBOOST_EFFECT
|
||||
val providers = mutableListOf<Any>(battle)
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
|
||||
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
@@ -66,6 +60,10 @@ class BoostInstruction(battle: PokemonBattle, val message: BattleMessage, val is
|
||||
}
|
||||
|
||||
override fun postActionEffect(battle: PokemonBattle) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return
|
||||
val statKey = message.argumentAt(1) ?: return
|
||||
val stages = message.argumentAt(2)?.toInt() ?: return
|
||||
val stat = Stats.getStat(statKey).displayName
|
||||
val severity = Stats.getSeverity(stages)
|
||||
val rootKey = if (isBoost) "boost" else "unboost"
|
||||
|
||||
|
||||
+3
-3
@@ -150,12 +150,12 @@ class DamageInstruction(
|
||||
|
||||
if (effect != null) {
|
||||
val lang = when (effect.id) {
|
||||
"blacksludge", "stickybarb", "jabocaberry", "rowapberry" -> battleLang("damage.item", pokemonName, effect.typelessData)
|
||||
"blacksludge", "stickybarb" -> battleLang("damage.item", pokemonName, effect.typelessData)
|
||||
"brn", "psn", "tox" -> {
|
||||
val status = Statuses.getStatus(effect.id)?.name?.path ?: return@dispatch GO
|
||||
lang("status.$status.hurt", pokemonName)
|
||||
}
|
||||
"aftermath", "innardsout" -> battleLang("damage.generic", pokemonName)
|
||||
"aftermath" -> battleLang("damage.generic", pokemonName)
|
||||
"chloroblast", "steelbeam" -> battleLang("damage.mindblown", pokemonName)
|
||||
"jumpkick" -> battleLang("damage.highjumpkick", pokemonName)
|
||||
else -> battleLang("damage.${effect.id}", pokemonName, source?.getName() ?: Component.literal("UNKOWN"))
|
||||
@@ -209,7 +209,7 @@ class DamageInstruction(
|
||||
} else if (causedFaint) {
|
||||
GO
|
||||
} else {
|
||||
UntilDispatch { "effects" !in holds}
|
||||
UntilDispatch {"effects" !in holds}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ class FailInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
val of = message.battlePokemonFromOptional(battle)
|
||||
|
||||
val lang = when (effectID) {
|
||||
null, "burnup", "doubleshock", "allyswitch" -> battleLang("fail") // Moves that use default fail lang. (Null included for moves that fail with no effect, for example: Baton Pass.)
|
||||
null, "burnup", "doubleshock" -> battleLang("fail") // Moves that use default fail lang. (Null included for moves that fail with no effect, for example: Baton Pass.)
|
||||
"shedtail" -> battleLang("fail.substitute", pokemonName)
|
||||
"hyperspacefury", "aurawheel" -> battleLang("fail.darkvoid", pokemonName) // Moves that can only be used by one species and fail when any others try
|
||||
"corrosivegas" -> battleLang("fail.healblock", pokemonName)
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ class InitializeInstruction(val instructionSet: InstructionSet, val message: Bat
|
||||
pokemon.entity?.let {
|
||||
// If a Pokémon entity is being recalled with an animation,
|
||||
// wrap up the animation and recall the Pokémon immediately.
|
||||
if (it.beamMode == 3 && battle.battlePartyStores.isEmpty()) {
|
||||
if (it.beamMode == 3) {
|
||||
pokemon.effectedPokemon.recall()
|
||||
}
|
||||
// If already on the field, initialize for the BattleInitializePacket
|
||||
|
||||
-1
@@ -41,7 +41,6 @@ class StatusInstruction( val message: BattleMessage): InterpreterInstruction {
|
||||
if (status is PersistentStatus) {
|
||||
pokemon.effectedPokemon.applyStatus(status)
|
||||
battle.sendUpdate(BattlePersistentStatusPacket(pnx, status))
|
||||
pokemon.sendUpdate()
|
||||
}
|
||||
|
||||
battle.broadcastChatMessage(status.applyMessage.asTranslated(pokemon.getName()))
|
||||
|
||||
-24
@@ -25,8 +25,6 @@ import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler.SEND_OUT_STAGGER_BASE_DURATION
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler.SEND_OUT_STAGGER_RANDOM_MAX_DURATION
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.swap
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.random.Random
|
||||
@@ -91,28 +89,6 @@ class SwitchInstruction(val instructionSet: InstructionSet, val battleActor: Bat
|
||||
}
|
||||
GO
|
||||
}
|
||||
|
||||
val futureSwitches = instructionSet.getSubsequentInstructions(this).filterIsInstance<SwitchInstruction>()
|
||||
if (futureSwitches.isEmpty()) {
|
||||
if (battle.format.adjustLevel > 0) {
|
||||
// means battle is using clone teams, recall the "real" pokemon before the sendouts occur
|
||||
var waitOnRecall = false
|
||||
battle.actors.forEach { it ->
|
||||
val playerUUIDS = it.getPlayerUUIDs()
|
||||
playerUUIDS.forEach { uuid ->
|
||||
uuid.getPlayer()?.party()?.forEach { pokemon ->
|
||||
if (pokemon.entity != null) {
|
||||
waitOnRecall = true
|
||||
pokemon.entity!!.recallWithAnimation()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (waitOnRecall) {
|
||||
battle.dispatchWaitingToFront(SEND_OUT_DURATION) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
battle.dispatchInsert {
|
||||
|
||||
@@ -22,8 +22,6 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleUpdateTeamPokem
|
||||
import com.cobblemon.mod.common.pokemon.IVs
|
||||
import com.cobblemon.mod.common.pokemon.Nature
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import java.util.UUID
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
@@ -35,16 +33,11 @@ open class BattlePokemon(
|
||||
) {
|
||||
lateinit var actor: BattleActor
|
||||
companion object {
|
||||
fun safeCopyOf(pokemon: Pokemon): BattlePokemon {
|
||||
val effectedPokemon = pokemon.clone()
|
||||
BattleCloneProperty.isBattleClone().apply(effectedPokemon)
|
||||
UncatchableProperty.uncatchable().apply(effectedPokemon)
|
||||
return BattlePokemon(
|
||||
originalPokemon = pokemon,
|
||||
effectedPokemon = effectedPokemon,
|
||||
postBattleEntityOperation = { it.recallWithAnimation() }
|
||||
)
|
||||
}
|
||||
fun safeCopyOf(pokemon: Pokemon): BattlePokemon = BattlePokemon(
|
||||
originalPokemon = pokemon,
|
||||
effectedPokemon = pokemon.clone(),
|
||||
postBattleEntityOperation = { entity -> entity.discard() }
|
||||
)
|
||||
|
||||
fun playerOwned(pokemon: Pokemon): BattlePokemon = BattlePokemon(
|
||||
originalPokemon = pokemon,
|
||||
|
||||
@@ -23,7 +23,6 @@ import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.CropBlock
|
||||
import net.minecraft.world.level.block.FarmBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
@@ -99,61 +98,12 @@ class RevivalHerbBlock(settings: Properties) : CropBlock(settings), Mulchable {
|
||||
if (world.getRawBrightness(pos, 0) < 9 || this.isMaxAge(state)) {
|
||||
return
|
||||
}
|
||||
val currentMoisture = getMoistureAmount(this, world, pos)
|
||||
val currentMoisture = getGrowthSpeed(this, world, pos)
|
||||
if (random.nextInt((25F / currentMoisture).toInt() + 1) == 0) {
|
||||
this.growCrops(world, pos, state, false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is a copy pasta of [CropBlock.getGrowthSpeed]. Why? Because neoforge changes the signature of this
|
||||
* vanilla method, breaking us at runtime. To prevent this, we simply avoid using the vanilla method and create our own,
|
||||
* while not ideal, it works....
|
||||
*/
|
||||
fun getMoistureAmount(block: Block, level: BlockGetter, pos: BlockPos): Float {
|
||||
var growthSpeed = 1.0F
|
||||
var ground = pos.below()
|
||||
|
||||
for(x in -1..1) {
|
||||
for(z in -1..1) {
|
||||
var nearbyFarmLandBoost = 0.0F
|
||||
var blockState = level.getBlockState(ground.offset(x, 0, z))
|
||||
if (blockState.`is`(Blocks.FARMLAND)) {
|
||||
nearbyFarmLandBoost = 1.0F
|
||||
if (blockState.getValue(FarmBlock.MOISTURE) > 0) {
|
||||
nearbyFarmLandBoost = 3.0F
|
||||
}
|
||||
}
|
||||
|
||||
if (x != 0 || z != 0) {
|
||||
nearbyFarmLandBoost /= 4.0F
|
||||
}
|
||||
|
||||
growthSpeed += nearbyFarmLandBoost
|
||||
}
|
||||
}
|
||||
|
||||
var north = pos.north()
|
||||
var south = pos.south()
|
||||
var west = pos.west()
|
||||
var east = pos.east()
|
||||
var sameBlockDirectNeighborXAxis = level.getBlockState(west).`is`(block) || level.getBlockState(east).`is`(block)
|
||||
var sameBlockDirectNeighborZAxis = level.getBlockState(north).`is`(block) || level.getBlockState(south).`is`(block)
|
||||
if (sameBlockDirectNeighborZAxis && sameBlockDirectNeighborXAxis) {
|
||||
growthSpeed /= 2.0F
|
||||
} else {
|
||||
var sameBlockDiagonallyPresent = level.getBlockState(west.north()).`is`(block) ||
|
||||
level.getBlockState(east.north()).`is`(block) ||
|
||||
level.getBlockState(east.south()).`is`(block) ||
|
||||
level.getBlockState(west.south()).`is`(block)
|
||||
if (sameBlockDiagonallyPresent) {
|
||||
growthSpeed /= 2.0F
|
||||
}
|
||||
}
|
||||
|
||||
return growthSpeed
|
||||
}
|
||||
|
||||
override fun growCrops(world: Level, pos: BlockPos, state: BlockState) {
|
||||
this.growCrops(world, pos, state, true)
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ class GildedChestBlock(settings: Properties, val type: Type = Type.RED) : BaseEn
|
||||
world.addFreshEntity(entity)
|
||||
|
||||
world.removeBlock(pos, false)
|
||||
afterOnServer(seconds = 0.1F) {
|
||||
afterOnServer(ticks = 2) {
|
||||
if (player !in player.level().players()) {
|
||||
return@afterOnServer
|
||||
}
|
||||
|
||||
+1
-1
@@ -137,7 +137,7 @@ class HealingMachineBlockEntity(
|
||||
val npc = level
|
||||
?.getEntities(null, AABB.ofSize(blockPos.toVec3d(), 10.0, 10.0, 10.0)) { it.uuid == currentUser && it is NPCEntity }
|
||||
?.firstOrNull() as? NPCEntity
|
||||
val party = npc?.party
|
||||
val party = npc?.staticParty
|
||||
if (party != null) {
|
||||
party.heal()
|
||||
npc.sendSystemMessage(lang("healingmachine.healed").green()) // An NPC can read text, right?
|
||||
|
||||
+1
-1
@@ -126,7 +126,7 @@ class FossilMultiblockStructure (
|
||||
stack?.shrink(1)
|
||||
}
|
||||
|
||||
val pokemon = this.resultingFossil?.result?.create(player)
|
||||
val pokemon = this.resultingFossil?.result?.create()
|
||||
|
||||
if(pokemon != null) {
|
||||
pokemon.caughtBall = ballType
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client
|
||||
|
||||
import com.cobblemon.mod.common.client.battle.ClientBattleChallenge
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
import com.cobblemon.mod.common.client.trade.ClientTradeOffer
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import java.util.UUID
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class ClientPlayerActionRequests {
|
||||
val battleChallenges = mutableListOf<ClientBattleChallenge>()
|
||||
val multiBattleTeamRequests = mutableListOf<ClientBattleChallenge>()
|
||||
val tradeOffers = mutableListOf<ClientTradeOffer>()
|
||||
|
||||
val trackedPlayers = mutableMapOf<UUID, MatrixWrapper>()
|
||||
|
||||
fun onMultiChallengeExpired(challengeId: UUID) {
|
||||
val players = multiBattleTeamRequests.filter { it.challengeId == challengeId }.flatMap { it.challengerIds }
|
||||
players.forEach(trackedPlayers::remove)
|
||||
multiBattleTeamRequests.removeIf { it.challengeId == challengeId }
|
||||
}
|
||||
|
||||
fun onChallengeExpired(challengeId: UUID) {
|
||||
val players = battleChallenges.filter { it.challengeId == challengeId }.flatMap { it.challengerIds }
|
||||
players.forEach(trackedPlayers::remove)
|
||||
battleChallenges.removeIf { it.challengeId == challengeId }
|
||||
}
|
||||
|
||||
fun onTradeOfferExpired(uuid: UUID) {
|
||||
val tradeOffers = tradeOffers.filter { it.tradeOfferId == uuid }
|
||||
val players = tradeOffers.map { it.traderId }
|
||||
players.forEach(trackedPlayers::remove)
|
||||
this.tradeOffers.removeAll(tradeOffers)
|
||||
}
|
||||
|
||||
fun onRenderPlayer(player: Player, partialTicks: Float) {
|
||||
val matrixWrapper = trackedPlayers[player.uuid] ?: return
|
||||
val position = player.getEyePosition(partialTicks)
|
||||
matrixWrapper.updatePosition(
|
||||
Vec3(
|
||||
position.x,
|
||||
position.y + 1.1,
|
||||
position.z
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun addParticleEffect(challenge: ClientBattleChallenge) {
|
||||
challenge.challengerIds.forEach { playerId ->
|
||||
val player = Minecraft.getInstance().level?.getPlayerByUUID(playerId) ?: return@forEach
|
||||
val wrapper = trackedPlayers.getOrPut(player.uuid) { MatrixWrapper() }
|
||||
wrapper.updatePosition(player.getEyePosition(0F).add(0.0, 1.1, 0.0))
|
||||
val effect = BedrockParticleOptionsRepository.getEffect(cobblemonResource("particle_exclamation"))
|
||||
if (effect != null) {
|
||||
val particleStorm = ParticleStorm(
|
||||
effect = effect,
|
||||
matrixWrapper = wrapper,
|
||||
sourceAlive = { !player.isRemoved && trackedPlayers.containsKey(playerId) },
|
||||
world = Minecraft.getInstance().level ?: return,
|
||||
entity = player,
|
||||
)
|
||||
particleStorm.spawn()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,16 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.client
|
||||
|
||||
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.UUID
|
||||
|
||||
class ClientPlayerTeamData {
|
||||
var multiBattleTeamMembers = mutableListOf<ClientMultiBattleTeamMember>()
|
||||
}
|
||||
|
||||
class ClientMultiBattleTeamMember(val uuid: UUID, val name: MutableComponent) : ClientPlayerIcon(null) {
|
||||
override val texture: ResourceLocation = cobblemonResource("textures/particle/request/icon_partner.png")
|
||||
class ClientMultiBattleTeamMember(val uuid: UUID, val name: MutableComponent) {
|
||||
}
|
||||
@@ -19,8 +19,6 @@ import com.cobblemon.mod.common.client.battle.ClientBattle
|
||||
import com.cobblemon.mod.common.client.gui.PartyOverlay
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
|
||||
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
|
||||
import com.cobblemon.mod.common.client.render.DeferredRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.*
|
||||
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
|
||||
import com.cobblemon.mod.common.client.render.entity.PokeBobberEntityRenderer
|
||||
@@ -33,7 +31,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.repository.*
|
||||
import com.cobblemon.mod.common.client.render.npc.NPCRenderer
|
||||
import com.cobblemon.mod.common.client.render.pokeball.PokeBallRenderer
|
||||
import com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer
|
||||
import com.cobblemon.mod.common.client.requests.ClientPlayerActionRequests
|
||||
import com.cobblemon.mod.common.client.sound.BattleMusicController
|
||||
import com.cobblemon.mod.common.client.sound.EntitySoundTracker
|
||||
import com.cobblemon.mod.common.client.storage.ClientStorageManager
|
||||
@@ -107,8 +104,6 @@ object CobblemonClient {
|
||||
ClientTaskTracker.clear()
|
||||
checkedStarterScreen = false
|
||||
CobblemonDataProvider.canReload = true
|
||||
DeferredRenderer.clearAll()
|
||||
ClientPlayerIcon.clear()
|
||||
}
|
||||
|
||||
fun initialize(implementation: CobblemonClientImplementation) {
|
||||
@@ -161,7 +156,6 @@ object CobblemonClient {
|
||||
it.delegate.spawnShinyParticle(player!!)
|
||||
}
|
||||
}
|
||||
ClientPlayerIcon.onTick()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-24
@@ -8,29 +8,11 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.battle
|
||||
|
||||
import com.cobblemon.mod.common.api.interaction.PlayerActionRequest
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.client.requests.ClientPlayerActionRequest
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Clientside representation of a [PlayerActionRequest] for a battle.
|
||||
*
|
||||
* @param requestID The unique identifier of this challenge.
|
||||
* @param senderID The unique identifier of the player (or team) that sent this challenge.
|
||||
* @param expiryTime The amount of time (in seconds) this challenge is active.
|
||||
* @param battleFormat The
|
||||
*
|
||||
* @author JazzMcNade
|
||||
* @since July 22nd, 2024
|
||||
*/
|
||||
data class ClientBattleChallenge(
|
||||
override val requestID: UUID,
|
||||
override val senderID: UUID,
|
||||
override val expiryTime: Int,
|
||||
val battleFormat: BattleFormat
|
||||
) : ClientPlayerActionRequest(expiryTime) {
|
||||
override val texture: ResourceLocation = cobblemonResource("textures/particle/request/icon_challenge.png")
|
||||
}
|
||||
class ClientBattleChallenge(
|
||||
val challengeId: UUID,
|
||||
val challengerIds: List<UUID>,
|
||||
val battleFormat: BattleFormat? = null
|
||||
)
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.battle
|
||||
|
||||
import com.cobblemon.mod.common.client.requests.ClientPlayerActionRequest
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.*
|
||||
|
||||
data class ClientTeamRequest(
|
||||
override val requestID: UUID,
|
||||
override val senderID: UUID,
|
||||
override val expiryTime: Int
|
||||
) : ClientPlayerActionRequest(expiryTime) {
|
||||
override val texture: ResourceLocation = cobblemonResource("textures/particle/request/icon_team.png")
|
||||
}
|
||||
+11
-6
@@ -15,7 +15,6 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
|
||||
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
|
||||
@@ -32,7 +31,10 @@ import com.cobblemon.mod.common.client.render.models.blockbench.repository.Pokem
|
||||
import com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer.Companion.ease
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.MovingSoundInstance
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.syncher.EntityDataAccessor
|
||||
@@ -104,10 +106,6 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
|
||||
} else if (data == PokemonEntity.ASPECTS) {
|
||||
currentAspects = currentEntity.entityData.get(PokemonEntity.ASPECTS)
|
||||
currentEntity.pokemon.shiny = currentAspects.contains("shiny")
|
||||
} else if (data == PokemonEntity.CAUGHT_BALL) {
|
||||
PokeBalls.getPokeBall(currentEntity.entityData.get(PokemonEntity.CAUGHT_BALL).asIdentifierDefaultingNamespace())?.let {
|
||||
currentEntity.pokemon.caughtBall = it
|
||||
}
|
||||
} else if (data == PokemonEntity.DYING_EFFECTS_STARTED) {
|
||||
val isDying = currentEntity.entityData.get(PokemonEntity.DYING_EFFECTS_STARTED)
|
||||
if (isDying) {
|
||||
@@ -232,6 +230,13 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
|
||||
world
|
||||
).spawn()
|
||||
}
|
||||
currentEntity.after(seconds = 0.1f) {
|
||||
//This is only for when the player is sending out the Pokemon into the world and not a battle.
|
||||
if (currentEntity.pokemon.shiny && !currentEntity.isBattling) {
|
||||
playShinyEffect("cobblemon:shiny_ring")
|
||||
lastShinyParticle = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ class BattleGUI : Screen(battleLang("gui.title")) {
|
||||
// Known quirk of Showdown. It'll ask for actions on fainted slots
|
||||
// Also during a forced switch in doubles/triples it'll ask for actions on non-switching slots
|
||||
val pokemon = request.side?.pokemon?.firstOrNull { it.uuid == request.activePokemon.battlePokemon?.uuid }
|
||||
if (pokemon == null || pokemon.condition.contains("fnt") || pokemon.commanding || request.moveSet == null) {
|
||||
if(pokemon == null || pokemon.condition.contains("fnt") || request.moveSet == null) {
|
||||
this.selectAction(request, PassActionResponse)
|
||||
null
|
||||
} else {
|
||||
|
||||
@@ -312,7 +312,6 @@ class BattleOverlay : Gui(Minecraft.getInstance()), Schedulable {
|
||||
drawPokeBall(
|
||||
state = ballState,
|
||||
matrixStack = matrixStack,
|
||||
reversed = reversed,
|
||||
partialTicks = partialTicks
|
||||
)
|
||||
} else {
|
||||
|
||||
+5
-52
@@ -28,8 +28,8 @@ class BattleMessagePane(
|
||||
messageQueue: ClientBattleMessageQueue
|
||||
): ObjectSelectionList<BattleMessagePane.BattleMessageLine>(
|
||||
Minecraft.getInstance(),
|
||||
FRAME_WIDTH, // width
|
||||
FRAME_HEIGHT, // height
|
||||
TEXT_BOX_WIDTH, // width
|
||||
TEXT_BOX_HEIGHT, // height
|
||||
1, // top
|
||||
LINE_HEIGHT
|
||||
) {
|
||||
@@ -62,7 +62,7 @@ class BattleMessagePane(
|
||||
|
||||
companion object {
|
||||
const val LINE_HEIGHT = 10
|
||||
const val LINE_WIDTH = 146
|
||||
const val LINE_WIDTH = 142
|
||||
const val FRAME_WIDTH = 169
|
||||
const val FRAME_HEIGHT = 55
|
||||
const val FRAME_EXPANDED_HEIGHT = 101
|
||||
@@ -72,7 +72,6 @@ class BattleMessagePane(
|
||||
|
||||
private val battleMessagePaneFrameResource = cobblemonResource("textures/gui/battle/battle_log.png")
|
||||
private val battleMessagePaneFrameExpandedResource = cobblemonResource("textures/gui/battle/battle_log_expanded.png")
|
||||
private val battleMessageHighlight = cobblemonResource("textures/gui/battle/battle_log_row_selected_color.png")
|
||||
private var expanded = false
|
||||
}
|
||||
|
||||
@@ -80,54 +79,8 @@ class BattleMessagePane(
|
||||
return super.addEntry(entry)
|
||||
}
|
||||
|
||||
override fun getRowLeft(): Int {
|
||||
return super.getRowLeft() + 4
|
||||
}
|
||||
|
||||
override fun renderSelection(guiGraphics: GuiGraphics, top: Int, width: Int, height: Int, outerColor: Int, innerColor: Int) {
|
||||
blitk(
|
||||
matrixStack = guiGraphics.pose(),
|
||||
texture = battleMessageHighlight,
|
||||
x = x + 6,
|
||||
y = top - 2,
|
||||
height = 10,
|
||||
width = 1,
|
||||
alpha = opacity
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = guiGraphics.pose(),
|
||||
texture = battleMessageHighlight,
|
||||
x = x + 6,
|
||||
y = top - 2,
|
||||
height = 1,
|
||||
width = LINE_WIDTH,
|
||||
alpha = opacity
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = guiGraphics.pose(),
|
||||
texture = battleMessageHighlight,
|
||||
x = x + 6,
|
||||
y = top + 7,
|
||||
height = 1,
|
||||
width = LINE_WIDTH,
|
||||
alpha = opacity
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = guiGraphics.pose(),
|
||||
texture = battleMessageHighlight,
|
||||
x = x + 6 + LINE_WIDTH - 1,
|
||||
y = top - 2,
|
||||
height = 10,
|
||||
width = 1,
|
||||
alpha = opacity
|
||||
)
|
||||
}
|
||||
|
||||
override fun getRowWidth(): Int {
|
||||
return LINE_WIDTH
|
||||
return 80
|
||||
}
|
||||
|
||||
override fun getScrollbarPosition(): Int {
|
||||
@@ -209,7 +162,7 @@ class BattleMessagePane(
|
||||
drawScaledText(
|
||||
context,
|
||||
line,
|
||||
rowLeft,
|
||||
rowLeft - 29,
|
||||
rowTop - 2,
|
||||
opacity = pane.opacity
|
||||
)
|
||||
|
||||
+57
-141
@@ -8,21 +8,15 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.interact.battleRequest
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
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.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
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.render.drawScaledText
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.*
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamLeavePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamRequestPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamResponsePacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.Minecraft
|
||||
@@ -31,20 +25,17 @@ import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
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")) {
|
||||
private val activeTeamRequest: ClientBattleChallenge? = null
|
||||
) : Screen(lang("challenge.request_battle_title")) {
|
||||
companion object {
|
||||
const val SIZE = 113
|
||||
private val backgroundResource = cobblemonResource("textures/gui/interact/request/battle_request.png")
|
||||
private val battleArrowsResource = cobblemonResource("textures/gui/interact/request/battle_request_arrows.png")
|
||||
private var requestButton: BattleRequestButton? = null
|
||||
var autoLevelNavButtonLeft: BattleRequestNavigationButton? = null
|
||||
var autoLevelNavButtonRight: BattleRequestNavigationButton? = null
|
||||
|
||||
val battleRequestMap = mutableMapOf(
|
||||
Pair(
|
||||
@@ -54,11 +45,11 @@ class BattleConfigureGUI(
|
||||
battleFormat = BattleFormat.GEN_9_SINGLES,
|
||||
tileTexture = cobblemonResource("textures/gui/interact/request/battle_request_single.png"),
|
||||
overlayTexture = cobblemonResource("textures/gui/interact/request/battle_request_overlay.png"),
|
||||
title = lang("ui.challenge.challenge_title").bold(),
|
||||
title = lang("challenge.request_battle_title").bold(),
|
||||
subTitle = lang("battle.types.singles").bold(),
|
||||
buttonText = lang("ui.challenge.challenge").bold(),
|
||||
buttonText = lang("challenge.challenge").bold(),
|
||||
onRequest = { packet, battleFormat -> sendBattleRequest(battleFormat, packet) },
|
||||
onResponse = { packet, requestID, accept -> sendBattleResponse(packet, requestID, accept) }
|
||||
onResponse = { packet, accept -> sendBattleResponse(packet, accept) }
|
||||
)
|
||||
),
|
||||
Pair(
|
||||
@@ -68,11 +59,11 @@ class BattleConfigureGUI(
|
||||
battleFormat = BattleFormat.GEN_9_DOUBLES,
|
||||
tileTexture = cobblemonResource("textures/gui/interact/request/battle_request_double.png"),
|
||||
overlayTexture = cobblemonResource("textures/gui/interact/request/battle_request_overlay.png"),
|
||||
title = lang("ui.challenge.challenge_title").bold(),
|
||||
title = lang("challenge.request_battle_title").bold(),
|
||||
subTitle = lang("battle.types.doubles").bold(),
|
||||
buttonText = lang("ui.challenge.challenge").bold(),
|
||||
buttonText = lang("challenge.challenge").bold(),
|
||||
onRequest = { packet, battleFormat -> sendBattleRequest(battleFormat, packet) },
|
||||
onResponse = { packet, requestID, accept -> sendBattleResponse(packet, requestID, accept) }
|
||||
onResponse = { packet, accept -> sendBattleResponse(packet, accept) }
|
||||
),
|
||||
),
|
||||
Pair(
|
||||
@@ -82,11 +73,11 @@ class BattleConfigureGUI(
|
||||
battleFormat = BattleFormat.GEN_9_TRIPLES,
|
||||
tileTexture = cobblemonResource("textures/gui/interact/request/battle_request_triple.png"),
|
||||
overlayTexture = cobblemonResource("textures/gui/interact/request/battle_request_overlay.png"),
|
||||
title = lang("ui.challenge.challenge_title").bold(),
|
||||
title = lang("challenge.request_battle_title").bold(),
|
||||
subTitle = lang("battle.types.triples").bold(),
|
||||
buttonText = lang("ui.challenge.challenge").bold(),
|
||||
buttonText = lang("challenge.challenge").bold(),
|
||||
onRequest = { packet, battleFormat -> sendBattleRequest(battleFormat, packet) },
|
||||
onResponse = { packet, requestID, accept -> sendBattleResponse(packet, requestID, accept) }
|
||||
onResponse = { packet, accept -> sendBattleResponse(packet, accept) }
|
||||
),
|
||||
),
|
||||
Pair(
|
||||
@@ -96,12 +87,12 @@ class BattleConfigureGUI(
|
||||
battleFormat = BattleFormat.GEN_9_MULTI,
|
||||
tileTexture = cobblemonResource("textures/gui/interact/request/battle_request_multi.png"),
|
||||
overlayTexture = cobblemonResource("textures/gui/interact/request/battle_request_multi_overlay_partner.png"),
|
||||
title = lang("ui.team.invite_title").bold(),
|
||||
title = lang("challenge.request_team_join_title").bold(),
|
||||
subTitle = lang("battle.types.multi").bold(),
|
||||
buttonText = lang("ui.team.invite").bold(),
|
||||
buttonText = lang("challenge.multi.invite").bold(),
|
||||
color = ColourLibrary.SIDE_1_ALLY_BATTLE_COLOUR,
|
||||
onRequest = { packet, battleFormat -> BattleTeamRequestPacket(packet.numericTargetId).sendToServer() },
|
||||
onResponse = { packet, requestID, accept -> sendTeamResponse(packet, requestID, accept) }
|
||||
onResponse = { packet, accept -> BattleTeamResponsePacket(packet.numericTargetId, accept).sendToServer() }
|
||||
),
|
||||
),
|
||||
Pair(
|
||||
@@ -111,12 +102,12 @@ class BattleConfigureGUI(
|
||||
battleFormat = BattleFormat.GEN_9_MULTI,
|
||||
tileTexture = cobblemonResource("textures/gui/interact/request/battle_request_multi.png"),
|
||||
overlayTexture = cobblemonResource("textures/gui/interact/request/battle_request_multi_overlay_partner_leave.png"),
|
||||
title = lang("ui.team.leave_title").bold(),
|
||||
title = lang("challenge.request_team_leave_title").bold(),
|
||||
subTitle = lang("battle.types.multi").bold(),
|
||||
buttonText = lang("ui.team.leave").bold(),
|
||||
buttonText = lang("challenge.multi.leave").bold(),
|
||||
color = ColourLibrary.SIDE_1_ALLY_BATTLE_COLOUR,
|
||||
onRequest = { _, _ -> BattleTeamLeavePacket().sendToServer() },
|
||||
onResponse = { _, _, _ -> Unit }
|
||||
onResponse = { _, _ -> Unit }
|
||||
),
|
||||
),
|
||||
Pair(
|
||||
@@ -126,11 +117,11 @@ class BattleConfigureGUI(
|
||||
battleFormat = BattleFormat.GEN_9_MULTI,
|
||||
tileTexture = cobblemonResource("textures/gui/interact/request/battle_request_multi.png"),
|
||||
overlayTexture = cobblemonResource("textures/gui/interact/request/battle_request_multi_overlay_opponent.png"),
|
||||
title = lang("ui.challenge.challenge_title").bold(),
|
||||
title = lang("challenge.request_battle_title").bold(),
|
||||
subTitle = lang("battle.types.multi").bold(),
|
||||
buttonText = lang("ui.challenge.challenge").bold(),
|
||||
buttonText = lang("challenge.challenge").bold(),
|
||||
onRequest = { packet, battleFormat -> sendBattleRequest(battleFormat, packet) },
|
||||
onResponse = { packet, requestID, accept -> sendBattleResponse(packet, requestID, accept) }
|
||||
onResponse = { packet, accept -> sendBattleResponse(packet, accept) }
|
||||
),
|
||||
),
|
||||
Pair(
|
||||
@@ -140,41 +131,36 @@ class BattleConfigureGUI(
|
||||
battleFormat = BattleFormat.GEN_9_ROYAL,
|
||||
tileTexture = cobblemonResource("textures/gui/interact/request/battle_request_royal.png"),
|
||||
overlayTexture = cobblemonResource("textures/gui/interact/request/battle_request_royal_overlay.png"),
|
||||
title = lang("ui.challenge.challenge_title").bold(),
|
||||
title = lang("challenge.request_battle_title").bold(),
|
||||
subTitle = lang("battle.types.freeforall").bold(),
|
||||
buttonText = lang("ui.challenge.challenge").bold(),
|
||||
buttonText = lang("challenge.challenge").bold(),
|
||||
onRequest = { packet, battleFormat -> sendBattleRequest(battleFormat, packet) },
|
||||
onResponse = { packet, requestID, accept -> sendBattleResponse(packet, requestID, accept) }
|
||||
onResponse = { packet, accept -> sendBattleResponse(packet, accept) }
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val levelRulesetOption = mutableListOf(-1, 50, 100, 5)
|
||||
|
||||
private fun sendTeamResponse(packet: PlayerInteractOptionsPacket, requestID: UUID, accept: Boolean) {
|
||||
BattleTeamResponsePacket(packet.numericTargetId, requestID, accept).sendToServer()
|
||||
}
|
||||
fun sendBattleRequest(battleFormat: BattleFormat, packet: PlayerInteractOptionsPacket) {
|
||||
BattleChallengePacket(packet.numericTargetId, packet.selectedPokemonId, battleFormat).sendToServer()
|
||||
}
|
||||
private fun sendBattleResponse(packet: PlayerInteractOptionsPacket, requestID: UUID, accept: Boolean) {
|
||||
BattleChallengeResponsePacket(packet.numericTargetId, requestID, packet.selectedPokemonId, accept).sendToServer()
|
||||
private fun sendBattleResponse(packet: PlayerInteractOptionsPacket, accept: Boolean) {
|
||||
BattleChallengeResponsePacket(packet.numericTargetId, packet.selectedPokemonId, accept).sendToServer()
|
||||
}
|
||||
private var options: List<PlayerInteractOptionsPacket.Options> = emptyList()
|
||||
private val blinkRate = 35
|
||||
}
|
||||
|
||||
class BattleTypeTile(
|
||||
val option: PlayerInteractOptionsPacket.Options,
|
||||
val battleFormat: BattleFormat,
|
||||
val tileTexture: ResourceLocation?,
|
||||
val overlayTexture: ResourceLocation?,
|
||||
val title: MutableComponent,
|
||||
val subTitle: MutableComponent,
|
||||
val buttonText: MutableComponent,
|
||||
val color: Int = ColourLibrary.SIDE_1_BATTLE_COLOUR,
|
||||
val onRequest: (packet : PlayerInteractOptionsPacket, battleFormat: BattleFormat) -> Unit,
|
||||
val onResponse: (packet: PlayerInteractOptionsPacket, requestID: UUID, accept: Boolean) -> Unit,
|
||||
val option: PlayerInteractOptionsPacket.Options,
|
||||
val battleFormat: BattleFormat,
|
||||
val tileTexture: ResourceLocation?,
|
||||
val overlayTexture: ResourceLocation?,
|
||||
val title: MutableComponent,
|
||||
val subTitle: MutableComponent,
|
||||
val buttonText: MutableComponent,
|
||||
val color: Int = ColourLibrary.SIDE_1_BATTLE_COLOUR,
|
||||
val onRequest: (packet : PlayerInteractOptionsPacket, battleFormat: BattleFormat) -> Unit,
|
||||
val onResponse: (packet: PlayerInteractOptionsPacket, accept: Boolean) -> Unit,
|
||||
)
|
||||
|
||||
private var currentPage = 0
|
||||
@@ -187,16 +173,6 @@ class BattleConfigureGUI(
|
||||
else 0
|
||||
}
|
||||
|
||||
private var levelRulesetOptionIndex = 0
|
||||
set(value) {
|
||||
// If value is within min and max
|
||||
field = if (value > 0 && value < levelRulesetOption.count()) value
|
||||
// If value is less than zero, wrap around to end
|
||||
else if (value < 0) levelRulesetOption.count() - 1
|
||||
// Else it's greater than max, wrap around to start
|
||||
else 0
|
||||
}
|
||||
|
||||
private var targetName = Component.literal("Target Name").bold()
|
||||
private var hasRequest = false
|
||||
private var ticksPassed = 0F
|
||||
@@ -206,20 +182,20 @@ class BattleConfigureGUI(
|
||||
|
||||
|
||||
override fun init() {
|
||||
val challenge = activeRequest
|
||||
targetName = Minecraft.getInstance().player?.level()?.getPlayerByUUID(packet.targetId)?.name?.plainCopy()?.bold() ?: targetName
|
||||
val pendingRequest = activeTeamRequest ?: activeRequest
|
||||
hasRequest = challenge != null || activeTeamRequest != null
|
||||
if (activeTeamRequest != null) {
|
||||
options = listOf(PlayerInteractOptionsPacket.Options.TEAM_REQUEST)
|
||||
} else if (activeRequest != null) {
|
||||
options = packet.options.keys.filter { packet.options[it] === PlayerInteractOptionsPacket.OptionStatus.AVAILABLE && battleRequestMap[it]?.battleFormat?.battleType?.name == activeRequest.battleFormat.battleType.name }
|
||||
val level = activeRequest.battleFormat.adjustLevel
|
||||
levelRulesetOptionIndex = maxOf(0, levelRulesetOption.indexOf(level))
|
||||
} else if (challenge != null) {
|
||||
options = packet.options.keys.filter { packet.options[it] === PlayerInteractOptionsPacket.OptionStatus.AVAILABLE && battleRequestMap[it]?.battleFormat?.battleType?.name == challenge.battleFormat?.battleType?.name }
|
||||
} else {
|
||||
options = packet.options.keys.filter { packet.options[it] === PlayerInteractOptionsPacket.OptionStatus.AVAILABLE && battleRequestMap.containsKey(it) }.toList()
|
||||
}
|
||||
val (x, y) = getDimensions()
|
||||
|
||||
if(pendingRequest != null) {
|
||||
|
||||
if(hasRequest) {
|
||||
// Draw Accept/Decline buttons
|
||||
this.addRenderableWidget(
|
||||
BattleResponseButton(
|
||||
@@ -227,7 +203,7 @@ class BattleConfigureGUI(
|
||||
y + 99,
|
||||
true
|
||||
) {
|
||||
battleRequestMap[options[currentPage]]?.onResponse?.let { it1 -> it1(packet, pendingRequest.requestID, true) }
|
||||
battleRequestMap[options[currentPage]]?.onResponse?.let { it1 -> it1(packet, true) }
|
||||
closeGUI()
|
||||
}
|
||||
)
|
||||
@@ -238,39 +214,35 @@ class BattleConfigureGUI(
|
||||
y + 99,
|
||||
false
|
||||
) {
|
||||
battleRequestMap[options[currentPage]]?.onResponse?.let { it1 -> it1(packet, pendingRequest.requestID, false) }
|
||||
battleRequestMap[options[currentPage]]?.onResponse?.let { it1 -> it1(packet, false) }
|
||||
closeGUI()
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Draw Challenge button
|
||||
requestButton = BattleRequestButton(
|
||||
x + 22,
|
||||
y + 99,
|
||||
lang("ui.challenge.challenge"),
|
||||
lang("challenge.challenge"),
|
||||
) {
|
||||
//TODO: add additional battle rules, otherwise this call feels pretty silly
|
||||
battleRequestMap[options[currentPage]]?.onRequest?.let { it1 ->
|
||||
val battleFormat = battleRequestMap[options[currentPage]]?.battleFormat ?: BattleFormat.GEN_9_SINGLES
|
||||
val level = levelRulesetOption[levelRulesetOptionIndex]
|
||||
battleFormat.adjustLevel = level
|
||||
it1(packet, battleFormat)
|
||||
}
|
||||
it1(packet, battleRequestMap[options[currentPage]]?.battleFormat ?: BattleFormat.GEN_9_SINGLES) }
|
||||
closeGUI()
|
||||
}
|
||||
requestButton?.let {
|
||||
this.addRenderableWidget(it)
|
||||
this.addRenderableWidget(
|
||||
it
|
||||
)
|
||||
}
|
||||
|
||||
// BattleFormat Selection buttons
|
||||
// Selection buttons
|
||||
this.addRenderableWidget(
|
||||
BattleRequestNavigationButton(
|
||||
pX = x + 2,
|
||||
pY = y + 30,
|
||||
clickHeight = BattleRequestNavigationButton.HEIGHT * 6,
|
||||
forward = false
|
||||
pX = x + 2,
|
||||
pY = y + 30,
|
||||
forward = false
|
||||
) { currentPage = (currentPage - 1) % options.count()
|
||||
updateRequestButtonText()
|
||||
}
|
||||
@@ -278,40 +250,20 @@ class BattleConfigureGUI(
|
||||
|
||||
this.addRenderableWidget(
|
||||
BattleRequestNavigationButton(
|
||||
pX = x + 106,
|
||||
pY = y + 30,
|
||||
clickHeight = BattleRequestNavigationButton.HEIGHT * 6,
|
||||
forward = true
|
||||
pX = x + 106,
|
||||
pY = y + 30,
|
||||
forward = true
|
||||
) { currentPage = (currentPage + 1) % options.count()
|
||||
updateRequestButtonText()
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
if (battleRequestMap[options[currentPage]]?.option != PlayerInteractOptionsPacket.Options.TEAM_REQUEST) {
|
||||
|
||||
// Level Selection buttons
|
||||
autoLevelNavButtonLeft = BattleRequestNavigationButton(
|
||||
pX = x + 15,
|
||||
pY = y + 75,
|
||||
forward = false
|
||||
) { levelRulesetOptionIndex -= 1 }
|
||||
this.addRenderableWidget(autoLevelNavButtonLeft)
|
||||
|
||||
autoLevelNavButtonRight = BattleRequestNavigationButton(
|
||||
pX = x + 93,
|
||||
pY = y + 75,
|
||||
forward = true
|
||||
) {levelRulesetOptionIndex += 1 }
|
||||
this.addRenderableWidget(autoLevelNavButtonRight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateRequestButtonText() {
|
||||
requestButton.let {
|
||||
if (it != null) {
|
||||
it.text = battleRequestMap[options[currentPage]]?.buttonText ?: lang("ui.challenge.challenge")
|
||||
it.text = battleRequestMap[options[currentPage]]?.buttonText ?: lang("challenge.challenge")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -376,42 +328,6 @@ class BattleConfigureGUI(
|
||||
shadow = true
|
||||
)
|
||||
|
||||
if (battleRequestMap[options[currentPage]]?.option != PlayerInteractOptionsPacket.Options.TEAM_REQUEST) {
|
||||
// Level Rule Display Text
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = if (levelRulesetOption[levelRulesetOptionIndex] < 0) lang("challenge.rule.anything_goes").bold()
|
||||
else lang("challenge.rule.level", levelRulesetOption[levelRulesetOptionIndex]).bold(),
|
||||
x = x + 55,
|
||||
y = y + 75,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
autoLevelNavButtonLeft.let {
|
||||
if (it != null) {
|
||||
it.active = true
|
||||
}
|
||||
}
|
||||
autoLevelNavButtonRight.let {
|
||||
if (it != null) {
|
||||
it.active = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
autoLevelNavButtonLeft.let {
|
||||
if (it != null) {
|
||||
it.active = false
|
||||
}
|
||||
}
|
||||
autoLevelNavButtonRight.let {
|
||||
if (it != null) {
|
||||
it.active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Opponent display name
|
||||
drawScaledText(
|
||||
context = context,
|
||||
|
||||
+7
-10
@@ -19,28 +19,25 @@ import net.minecraft.network.chat.Component
|
||||
|
||||
class BattleRequestNavigationButton(
|
||||
pX: Int, pY: Int,
|
||||
private val clickHeight: Float = HEIGHT,
|
||||
private val forward: Boolean,
|
||||
onPress: OnPress
|
||||
): Button(pX, pY, (WIDTH * SCALE).toInt(), (clickHeight * SCALE).toInt(), Component.literal("Navigation"), onPress, DEFAULT_NARRATION) {
|
||||
): Button(pX, pY, (WIDTH * SCALE).toInt(), (CLICK_HEIGHT * SCALE).toInt(), Component.literal("Navigation"), onPress, DEFAULT_NARRATION) {
|
||||
|
||||
companion object {
|
||||
const val WIDTH = 9F
|
||||
const val HEIGHT = 16F
|
||||
const val SCALE = 0.5F
|
||||
private const val WIDTH = 9F
|
||||
private const val HEIGHT = 16F
|
||||
private const val CLICK_HEIGHT = HEIGHT * 6
|
||||
private const val SCALE = 0.5F
|
||||
private val forwardButtonResource = cobblemonResource("textures/gui/interact/request/arrow_right.png")
|
||||
private val backwardsButtonResource = cobblemonResource("textures/gui/interact/request/arrow_left.png")
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
if (!isActive) {
|
||||
return
|
||||
}
|
||||
val hovered = (isHovered(pMouseX.toDouble(), pMouseY.toDouble()))
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
x = x / SCALE,
|
||||
y = (y + (clickHeight - HEIGHT) / 4) / SCALE,
|
||||
y = (y + (CLICK_HEIGHT - HEIGHT) / 4) / SCALE,
|
||||
texture = if (forward) forwardButtonResource else backwardsButtonResource,
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
@@ -54,5 +51,5 @@ class BattleRequestNavigationButton(
|
||||
soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.PC_CLICK, 1.0F))
|
||||
}
|
||||
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + (WIDTH * SCALE))) && mouseY.toFloat() in ((y.toFloat())..(y.toFloat() + (clickHeight * SCALE)))
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + (WIDTH * SCALE))) && mouseY.toFloat() in ((y.toFloat())..(y.toFloat() + (CLICK_HEIGHT * SCALE)))
|
||||
}
|
||||
+9
-11
@@ -8,13 +8,11 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.interact.wheel
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.pokemon.interaction.PokemonInteractionGUICreationEvent
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.gui.interact.battleRequest.BattleConfigureGUI
|
||||
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
|
||||
@@ -59,24 +57,24 @@ fun createPokemonInteractGui(pokemonID: UUID, canMountShoulder: Boolean): Intera
|
||||
fun createPlayerInteractGui(optionsPacket: PlayerInteractOptionsPacket): InteractWheelGUI {
|
||||
val trade = InteractWheelOption(
|
||||
iconResource = cobblemonResource("textures/gui/interact/icon_trade.png"),
|
||||
secondaryIconResource = if (CobblemonClient.requests.tradeOffers[optionsPacket.targetId] != null)
|
||||
secondaryIconResource = if (CobblemonClient.requests.tradeOffers.any { it.traderId == optionsPacket.targetId })
|
||||
cobblemonResource("textures/gui/interact/icon_exclamation.png")
|
||||
else null,
|
||||
colour = { null },
|
||||
tooltipText = "cobblemon.ui.interact.trade",
|
||||
onPress = {
|
||||
val tradeOffer = CobblemonClient.requests.tradeOffers[optionsPacket.targetId]
|
||||
val tradeOffer = CobblemonClient.requests.tradeOffers.find { it.traderId == optionsPacket.targetId }
|
||||
if (tradeOffer == null) {
|
||||
CobblemonNetwork.sendToServer(OfferTradePacket(optionsPacket.targetId))
|
||||
} else {
|
||||
CobblemonClient.requests.tradeOffers.remove(optionsPacket.targetId)
|
||||
CobblemonNetwork.sendToServer(AcceptTradeRequestPacket(tradeOffer.requestID))
|
||||
CobblemonClient.requests.tradeOffers -= tradeOffer
|
||||
CobblemonNetwork.sendToServer(AcceptTradeRequestPacket(tradeOffer.tradeOfferId))
|
||||
}
|
||||
closeGUI()
|
||||
}
|
||||
)
|
||||
val activeBattleRequest = CobblemonClient.requests.battleChallenges[optionsPacket.targetId]
|
||||
val activeTeamRequest = CobblemonClient.requests.multiBattleTeamRequests[optionsPacket.targetId]
|
||||
val activeBattleRequest = CobblemonClient.requests.battleChallenges.firstOrNull { it.challengerIds.contains(optionsPacket.targetId) }
|
||||
val activeTeamRequest = CobblemonClient.requests.multiBattleTeamRequests.firstOrNull { it.challengerIds.contains(optionsPacket.targetId) }
|
||||
val battle = InteractWheelOption(
|
||||
iconResource = cobblemonResource("textures/gui/interact/icon_battle.png"),
|
||||
secondaryIconResource = if(activeBattleRequest != null|| activeTeamRequest != null)
|
||||
@@ -91,7 +89,7 @@ fun createPlayerInteractGui(optionsPacket: PlayerInteractOptionsPacket): Interac
|
||||
|
||||
val spectate = InteractWheelOption(
|
||||
iconResource = cobblemonResource("textures/gui/interact/icon_spectate_battle.png"),
|
||||
colour = { if (CobblemonClient.requests.battleChallenges[optionsPacket.targetId] != null) Vector3f(0F, 0.6F, 0F) else null },
|
||||
colour = { if (CobblemonClient.requests.battleChallenges.any { it.challengerIds.contains(optionsPacket.targetId) }) Vector3f(0F, 0.6F, 0F) else null },
|
||||
onPress = {
|
||||
SpectateBattlePacket(optionsPacket.targetId).sendToServer()
|
||||
closeGUI()
|
||||
@@ -100,8 +98,8 @@ fun createPlayerInteractGui(optionsPacket: PlayerInteractOptionsPacket): Interac
|
||||
)
|
||||
val options: Multimap<Orientation, InteractWheelOption> = ArrayListMultimap.create()
|
||||
//TODO: hasChallenge and hasTeamRequest get calculated a bunch of times. Might consider having the server just passing it over.
|
||||
val hasChallenge = CobblemonClient.requests.battleChallenges[optionsPacket.targetId] != null
|
||||
val hasTeamRequest = CobblemonClient.requests.multiBattleTeamRequests[optionsPacket.targetId] != null
|
||||
val hasChallenge = CobblemonClient.requests.battleChallenges.any { it.challengerIds.contains(optionsPacket.targetId) }
|
||||
val hasTeamRequest = CobblemonClient.requests.multiBattleTeamRequests.any { it.challengerIds.contains(optionsPacket.targetId) }
|
||||
//The way things are positioned should probably be more thought out if more options are added
|
||||
var addBattleOption = false
|
||||
optionsPacket.options.forEach {
|
||||
|
||||
@@ -348,10 +348,7 @@ class PokedexGUI private constructor(
|
||||
|
||||
if (entries.isNotEmpty()) {
|
||||
if (init && initSpecies != null) {
|
||||
var entry = entries.firstOrNull { it.speciesId == initSpecies }
|
||||
if (entry == null) {
|
||||
entry = entries.first()
|
||||
}
|
||||
val entry = entries.first { it.speciesId == initSpecies }
|
||||
setSelectedEntry(entry)
|
||||
scrollScreen.scrollAmount = (entries.indexOf(entry).toDouble() / entries.size.toDouble()) * scrollScreen.maxScroll
|
||||
} else {
|
||||
@@ -448,7 +445,7 @@ class PokedexGUI private constructor(
|
||||
val form = species.forms.find { it.name.equals(formName, ignoreCase = true) } ?: species.standardForm
|
||||
when (tabInfoIndex) {
|
||||
TAB_DESCRIPTION -> {
|
||||
textToShowInDescription.addAll(form.pokedex)
|
||||
textToShowInDescription.addAll(species.pokedex)
|
||||
(tabInfoElement as DescriptionWidget).showPlaceholder = false
|
||||
}
|
||||
TAB_ABILITIES -> {
|
||||
|
||||
+10
-12
@@ -174,7 +174,6 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
}
|
||||
|
||||
val possibleVariationButtonPositions = mutableListOf<Pair<Float, Float>>(
|
||||
126F to 27F,
|
||||
114F to 27F,
|
||||
102F to 27F,
|
||||
90F to 27F,
|
||||
@@ -470,6 +469,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Cry
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
@@ -550,12 +550,6 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
variationButtons.forEach { removeWidget(it.getWidget()) }
|
||||
variationButtons.clear()
|
||||
|
||||
val seenShinyStates = CobblemonClient.clientPokedexData.getSeenShinyStates(pokedexEntry, pokedexForm)
|
||||
shiny = seenShinyStates.count() == 1 && seenShinyStates.first() == "shiny"
|
||||
shinyButton.resource = if (shiny) buttonShiny else buttonNone
|
||||
shinyButton.visible = shiny || seenShinyStates.size > 1
|
||||
shinyButton.active = seenShinyStates.size > 1
|
||||
|
||||
val form = species.forms.find { it.name.equals(pokedexForm.displayForm, ignoreCase = true) } ?: species.standardForm
|
||||
maleRatio = form.maleRatio
|
||||
|
||||
@@ -568,13 +562,12 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
genderButton.visible = true
|
||||
genderButton.active = seenGenders.size > 1
|
||||
}
|
||||
genderButton.buttonX = pX + (if (shinyButton.visible) 114F else 126F)
|
||||
|
||||
if (CobblemonClient.clientPokedexData.getHighestKnowledgeFor(pokedexEntry) == PokedexEntryProgress.NONE) return
|
||||
|
||||
var startPosition = if (shinyButton.visible) 1 else 0
|
||||
startPosition += if (genderButton.visible || (species.maleRatio == -1F)) 1 else 0
|
||||
if (CobblemonClient.clientPokedexData.getHighestKnowledgeFor(pokedexEntry) == PokedexEntryProgress.NONE) {
|
||||
return
|
||||
}
|
||||
|
||||
val startPosition = if (species.maleRatio == -1F) 0 else 1
|
||||
pokedexEntry.variations.forEachIndexed { index, variation ->
|
||||
val pos = possibleVariationButtonPositions[index + startPosition]
|
||||
val button = VariationButtonWrapper(this, pX + pos.first, pY + pos.second)
|
||||
@@ -582,6 +575,11 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
addWidget(button.getWidget())
|
||||
variationButtons.add(button)
|
||||
}
|
||||
|
||||
val seenShinyStates = CobblemonClient.clientPokedexData.getSeenShinyStates(pokedexEntry, pokedexForm)
|
||||
shiny = seenShinyStates.count() == 1 && seenShinyStates.first() == "shiny"
|
||||
shinyButton.resource = if (shiny) buttonShiny else buttonNone
|
||||
shinyButton.active = seenShinyStates.size > 1
|
||||
}
|
||||
|
||||
private fun updateType(species: Species, form: FormData) {
|
||||
|
||||
@@ -314,26 +314,26 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
when (screen) {
|
||||
INFO -> {
|
||||
mainScreen = InfoWidget(
|
||||
pX = x + 77,
|
||||
pY = y + 12,
|
||||
pokemon = this.selectedPokemon
|
||||
pX = x + 77,
|
||||
pY = y + 12,
|
||||
pokemon = this.selectedPokemon
|
||||
)
|
||||
}
|
||||
|
||||
MOVES -> {
|
||||
mainScreen = MovesWidget(
|
||||
pX = x + 77,
|
||||
pY = y + 12,
|
||||
summary = this
|
||||
pX = x + 77,
|
||||
pY = y + 12,
|
||||
summary = this
|
||||
)
|
||||
}
|
||||
|
||||
STATS -> {
|
||||
mainScreen = StatWidget(
|
||||
pX = x + 77,
|
||||
pY = y + 12,
|
||||
pokemon = this.selectedPokemon,
|
||||
tabIndex = subIndex
|
||||
pX = x + 77,
|
||||
pY = y + 12,
|
||||
pokemon = this.selectedPokemon,
|
||||
tabIndex = subIndex
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -365,16 +365,19 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
val movesWidget = mainScreen
|
||||
if (movesWidget is MovesWidget) {
|
||||
sideScreen = MoveSwapScreen(
|
||||
x + 216,
|
||||
y + 23,
|
||||
movesWidget = movesWidget,
|
||||
replacedMove = move
|
||||
x + 216,
|
||||
y + 24,
|
||||
movesWidget = movesWidget,
|
||||
replacedMove = move
|
||||
).also { switchPane ->
|
||||
val pokemon = selectedPokemon
|
||||
var moveSlotList = pokemon.relearnableMoves.map { template ->
|
||||
val benched = pokemon.benchedMoves.find { it.moveTemplate == template }
|
||||
MoveSwapScreen.MoveSlot(switchPane, template, benched?.ppRaisedStages ?: 0, pokemon)
|
||||
}
|
||||
var moveSlotList = (pokemon.allAccessibleMoves
|
||||
.filter { template -> pokemon.moveSet.none { it.template == template } }
|
||||
.map { template ->
|
||||
val benched = pokemon.benchedMoves.find { it.moveTemplate == template }
|
||||
MoveSwapScreen.MoveSlot(switchPane, template, benched?.ppRaisedStages
|
||||
?: 0, pokemon)
|
||||
})
|
||||
if (pokemon.moveSet.getMoves().size > 1 && move != null) {
|
||||
// Adds the "Forget" slot
|
||||
moveSlotList += MoveSwapScreen.MoveSlot(switchPane, null, 0, pokemon)
|
||||
@@ -387,7 +390,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
EVOLVE -> {
|
||||
sideScreen = EvolutionSelectScreen(
|
||||
x + 216,
|
||||
y + 23,
|
||||
y + 22,
|
||||
pokemon = selectedPokemon
|
||||
)
|
||||
}
|
||||
@@ -592,7 +595,6 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
|
||||
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
|
||||
if (sideScreenIndex == MOVE_SWAP || sideScreenIndex == EVOLVE) sideScreen.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
if (mainScreenIndex == MOVES) mainScreen.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler.MAX_NAME_LENGTH
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.mojang.blaze3d.platform.InputConstants
|
||||
import net.minecraft.client.Minecraft
|
||||
@@ -29,6 +28,9 @@ class NicknameEntryWidget(
|
||||
Minecraft.getInstance().font,
|
||||
x, y, width, height, text
|
||||
) {
|
||||
companion object {
|
||||
private const val MAX_NAME_LENGTH = 12
|
||||
}
|
||||
|
||||
var pokemonName = ""
|
||||
|
||||
|
||||
+2
-2
@@ -32,7 +32,7 @@ abstract class SummaryScrollList<T : ObjectSelectionList.Entry<T>>(
|
||||
) {
|
||||
companion object {
|
||||
const val WIDTH = 108
|
||||
const val HEIGHT = 112
|
||||
const val HEIGHT = 111
|
||||
const val SLOT_WIDTH = 91
|
||||
|
||||
private val backgroundResource = cobblemonResource("textures/gui/summary/summary_scroll_background.png")
|
||||
@@ -71,7 +71,7 @@ abstract class SummaryScrollList<T : ObjectSelectionList.Entry<T>>(
|
||||
)
|
||||
context.enableScissor(
|
||||
x,
|
||||
y,
|
||||
y - 1,
|
||||
x + width,
|
||||
y + height
|
||||
)
|
||||
|
||||
+1
@@ -25,6 +25,7 @@ import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
|
||||
|
||||
class InfoWidget(
|
||||
pX: Int,
|
||||
pY: Int,
|
||||
|
||||
+8
-31
@@ -21,11 +21,14 @@ class MoveDescriptionScrollList(
|
||||
) : ObjectSelectionList<MoveDescriptionEntry>(
|
||||
Minecraft.getInstance(),
|
||||
60, // width
|
||||
30, // height
|
||||
28, // height
|
||||
0, // top
|
||||
slotHeight
|
||||
) {
|
||||
private var scrolling = false
|
||||
companion object {
|
||||
const val WIDTH = 108
|
||||
const val HEIGHT = 111
|
||||
}
|
||||
|
||||
init {
|
||||
this.y = this.listY
|
||||
@@ -41,7 +44,7 @@ class MoveDescriptionScrollList(
|
||||
isHovered = pMouseX >= x && pMouseY >= y && pMouseX < x + width && pMouseY < y + height
|
||||
context.enableScissor(
|
||||
x,
|
||||
y,
|
||||
y - 1,
|
||||
x + width,
|
||||
y + height
|
||||
)
|
||||
@@ -49,33 +52,7 @@ class MoveDescriptionScrollList(
|
||||
context.disableScissor()
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
updateScrollingState(mouseX, mouseY)
|
||||
if (scrolling) isDragging = true
|
||||
|
||||
return isDragging
|
||||
}
|
||||
|
||||
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
|
||||
if (scrolling) {
|
||||
if (mouseY < y) {
|
||||
scrollAmount = 0.0
|
||||
} else if (mouseY > bottom) {
|
||||
scrollAmount = maxScroll.toDouble()
|
||||
} else {
|
||||
scrollAmount += deltaY
|
||||
}
|
||||
}
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
}
|
||||
|
||||
override fun renderListBackground(guiGraphics: GuiGraphics) {}
|
||||
|
||||
private fun updateScrollingState(mouseX: Double, mouseY: Double) {
|
||||
scrolling = mouseX >= getScrollbarPosition().toDouble()
|
||||
&& mouseX < (getScrollbarPosition() + 3).toDouble()
|
||||
&& mouseY >= y
|
||||
&& mouseY < bottom
|
||||
override fun renderListBackground(guiGraphics: GuiGraphics) {
|
||||
}
|
||||
|
||||
fun setMoveDescription(moveDescription: MutableComponent) {
|
||||
@@ -105,4 +82,4 @@ class MoveDescriptionScrollList(
|
||||
addEntry(MoveDescriptionEntry(Component.literal(part)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -111,9 +111,9 @@ class MoveSlotWidget(
|
||||
|
||||
var movePPText = Component.literal("${move.currentPp}/${move.maxPp}").bold()
|
||||
|
||||
if (move.currentPp <= Mth.floor(move.maxPp / 2F)) {
|
||||
movePPText = if (move.currentPp == 0) movePPText.red() else movePPText.gold()
|
||||
}
|
||||
if (move.currentPp <= Mth.floor(move.maxPp / 2F)) {
|
||||
movePPText = if (move.currentPp == 0) movePPText.red() else movePPText.gold()
|
||||
}
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
|
||||
+5
-13
@@ -49,7 +49,7 @@ class MovesWidget(
|
||||
|
||||
private var index = -1
|
||||
private val moves = (
|
||||
if (summary.selectedPokemon.relearnableMoves.count() > 0)
|
||||
if (summary.selectedPokemon.benchedMoves.count() > 0)
|
||||
summary.selectedPokemon.moveSet.getMovesWithNulls()
|
||||
else summary.selectedPokemon.moveSet
|
||||
).map { move ->
|
||||
@@ -67,7 +67,7 @@ class MovesWidget(
|
||||
|
||||
private var descriptionScrollList = MoveDescriptionScrollList(
|
||||
x + 69,
|
||||
y + 113,
|
||||
y + 114,
|
||||
5
|
||||
)
|
||||
|
||||
@@ -218,18 +218,10 @@ class MovesWidget(
|
||||
descriptionScrollList.setMoveDescription(selectedMove?.description ?: "".text())
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (descriptionScrollList.isHovered) descriptionScrollList.mouseClicked(mouseX, mouseY, button)
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
override fun mouseScrolled(mouseX: Double, mouseY: Double, horizontalAmount: Double, verticalAmount: Double): Boolean {
|
||||
if (descriptionScrollList.isHovered) descriptionScrollList.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount)
|
||||
if (descriptionScrollList.isHovered) {
|
||||
descriptionScrollList.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount)
|
||||
}
|
||||
return super.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount)
|
||||
}
|
||||
|
||||
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
|
||||
if (descriptionScrollList.isHovered) descriptionScrollList.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -49,7 +49,7 @@ class SwapMoveButton(
|
||||
val selected = if (swapScreen is MoveSwapScreen) swapScreen.replacedMove?.template == move else false
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = if (move == null) addMoveButtonResource else switchMoveButtonResource,
|
||||
texture = if(move == null) addMoveButtonResource else switchMoveButtonResource,
|
||||
x = (pX + OFFSET_X) / SCALE,
|
||||
y = (pY + OFFSET_Y) / SCALE,
|
||||
width = WIDTH,
|
||||
|
||||
@@ -22,7 +22,7 @@ class ModelWidget(
|
||||
pX: Int, pY: Int,
|
||||
pWidth: Int, pHeight: Int,
|
||||
var pokemon: RenderablePokemon,
|
||||
var baseScale: Float = 2.7F,
|
||||
val baseScale: Float = 2.7F,
|
||||
var rotationY: Float = 35F,
|
||||
var offsetY: Double = 0.0
|
||||
): SoundlessWidget(pX, pY, pWidth, pHeight, Component.literal("Trade - ModelWidget")) {
|
||||
|
||||
@@ -60,7 +60,13 @@ open class PartySlot(
|
||||
}
|
||||
|
||||
if (pokemon != null) {
|
||||
// context.enableScissor(x - 2, y + 2, x + SIZE + 4, y + SIZE + 4)
|
||||
context.enableScissor(
|
||||
x - 2,
|
||||
y + 2,
|
||||
x + SIZE + 4,
|
||||
y + SIZE + 4
|
||||
)
|
||||
|
||||
// Render Pokémon
|
||||
matrices.pushPose()
|
||||
matrices.translate(x + (SIZE / 2.0), y + 1.0, 0.0)
|
||||
@@ -75,7 +81,7 @@ open class PartySlot(
|
||||
)
|
||||
matrices.popPose()
|
||||
|
||||
// context.disableScissor()
|
||||
context.disableScissor()
|
||||
|
||||
// Ensure elements are not hidden behind Pokémon render
|
||||
matrices.pushPose()
|
||||
|
||||
@@ -36,7 +36,7 @@ class TradeButton(
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
val enabled = parent.offeredPokemon != null && parent.opposingOfferedPokemon != null && parent.protectiveTicks <= 0 && !parent.tradeProcessing
|
||||
val enabled = parent.offeredPokemon != null && parent.opposingOfferedPokemon != null && parent.protectiveTicks <= 0
|
||||
val active = parent.trade.acceptedOppositeOffer && !parent.trade.oppositeAcceptedMyOffer.get()
|
||||
|
||||
val texture = if (!enabled) buttonDisabledResource
|
||||
@@ -52,13 +52,14 @@ class TradeButton(
|
||||
textureHeight = HEIGHT * 2
|
||||
)
|
||||
|
||||
|
||||
val label = if (active) ".".repeat(parent.readyProgress).text() else lang("ui.trade")
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = label.bold(),
|
||||
x = x + (WIDTH / 2),
|
||||
y = y + (if (active) 1 else 3),
|
||||
y = y + 2.5,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
@@ -21,10 +21,11 @@ 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.reformatNatureTextIfMinted
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
|
||||
import com.cobblemon.mod.common.client.trade.ClientTrade
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket.TradeablePokemon
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.*
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.CancelTradePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.ChangeTradeAcceptancePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.UpdateTradeOfferPacket
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
@@ -35,7 +36,6 @@ 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.client.resources.sounds.SoundInstance
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import java.util.*
|
||||
@@ -61,25 +61,12 @@ class TradeGUI(
|
||||
const val BASE_BACKGROUND_HEIGHT = 85
|
||||
const val PARTY_SLOT_PADDING = 4
|
||||
const val PORTRAIT_SIZE = 78
|
||||
const val PORTRAIT_SCALE = 2F
|
||||
const val PORTRAIT_SCALE_MULTIPLIER = 0.4F
|
||||
const val PORTRAIT_MODEL_OFFSET_Y = -8.0
|
||||
const val TYPE_SPACER_WIDTH = 134
|
||||
const val TYPE_SPACER_HEIGHT = 12
|
||||
const val TRADE_READY_WIDTH = 28
|
||||
const val TRADE_READY_HEIGHT = 6
|
||||
const val TRADE_READY_TOP_HEIGHT = 5
|
||||
const val READY_PROGRESS_LIMIT = 6
|
||||
const val TRADE_ANIMATION_WIDTH = 114
|
||||
const val TRADE_ANIMATION_HEIGHT = 133
|
||||
const val TRADE_ANIMATION_ARROWS_WIDTH = 69
|
||||
const val TRADE_ANIMATION_FRAMES = 13
|
||||
const val TRADE_ANIMATION_ARROWS_FRAMES = 18
|
||||
const val MODEL_SCALE_BUFFER_FRAMES = 3
|
||||
const val REVERSE_ANIMATION_TICK = TRADE_ANIMATION_FRAMES + MODEL_SCALE_BUFFER_FRAMES + TRADE_ANIMATION_ARROWS_FRAMES
|
||||
const val MAX_TRADE_PROGRESS = REVERSE_ANIMATION_TICK + TRADE_ANIMATION_FRAMES + MODEL_SCALE_BUFFER_FRAMES
|
||||
const val CRY_BUFFER = 7
|
||||
|
||||
const val SCALE = 0.5F
|
||||
|
||||
private val baseResource = cobblemonResource("textures/gui/trade/trade_base.png")
|
||||
@@ -91,8 +78,6 @@ class TradeGUI(
|
||||
private val tradeReadyTopResource = cobblemonResource("textures/gui/trade/trade_ready_top.png")
|
||||
private val opposingTradeReadyResource = cobblemonResource("textures/gui/trade/trade_ready_opposing.png")
|
||||
private val opposingTradeReadyTopResource = cobblemonResource("textures/gui/trade/trade_ready_top_opposing.png")
|
||||
private val tradeAnimationResource = cobblemonResource("textures/gui/trade/trade_animation.png")
|
||||
private val tradeAnimationArrowsResource = cobblemonResource("textures/gui/trade/trade_animation_arrows.png")
|
||||
}
|
||||
|
||||
private var offeredPokemonModel: ModelWidget? = null
|
||||
@@ -102,20 +87,15 @@ class TradeGUI(
|
||||
var opposingOfferedPokemon: Pokemon? = null
|
||||
|
||||
var ticksElapsed = 0
|
||||
var protectiveTicks = 0
|
||||
var selectPointerOffsetY = 0
|
||||
var readyProgress = 0
|
||||
var tradeAnimationProgress = 0
|
||||
var selectPointerOffsetIncrement = false
|
||||
var tradeProcessing = false
|
||||
var isTradeInitiator = false
|
||||
|
||||
var tradeSoundInstance: SoundInstance? = null
|
||||
var protectiveTicks = 0
|
||||
|
||||
init {
|
||||
trade.cancelEmitter.subscribe {
|
||||
cancelTradeSound()
|
||||
super.onClose()
|
||||
// Maybe a sound
|
||||
}
|
||||
|
||||
trade.completedEmitter.subscribe {
|
||||
@@ -126,36 +106,30 @@ class TradeGUI(
|
||||
CobblemonNetwork.sendToServer(CancelTradePacket())
|
||||
return@subscribe onClose()
|
||||
}
|
||||
val tradedSlot = party.indexOf(myTradedPokemon)
|
||||
val opposingTradedSlot = traderParty.indexOf(theirTradedPokemon)
|
||||
party[tradedSlot] = theirTradedPokemon
|
||||
traderParty[opposingTradedSlot] = myTradedPokemon
|
||||
|
||||
val i1 = party.indexOf(myTradedPokemon)
|
||||
val i2 = traderParty.indexOf(theirTradedPokemon)
|
||||
party[i1] = theirTradedPokemon
|
||||
traderParty[i2] = myTradedPokemon
|
||||
offeredPokemon = null
|
||||
opposingOfferedPokemon = null
|
||||
ticksElapsed = 0
|
||||
readyProgress = 0
|
||||
trade.oppositeAcceptedMyOffer.set(false)
|
||||
CobblemonNetwork.sendToServer(UpdateTradeOfferPacket(Pair(theirTradedPokemon.pokemonId, PartyPosition(tradedSlot))))
|
||||
|
||||
setOfferedPokemon(pokemon = null, isOpposing = true)
|
||||
setOfferedPokemon(pokemon = null, isOpposing = false)
|
||||
rebuildWidgets()
|
||||
// Make a sound maybe
|
||||
}
|
||||
|
||||
trade.oppositeOffer.subscribe { newOffer: Pokemon? ->
|
||||
setOfferedPokemon(pokemon = newOffer, isOpposing = true)
|
||||
}
|
||||
|
||||
trade.myOffer.subscribe { myOffer: Pokemon? ->
|
||||
setOfferedPokemon(pokemon = myOffer)
|
||||
}
|
||||
|
||||
trade.oppositeAcceptedMyOffer.subscribe {
|
||||
ticksElapsed = 0
|
||||
readyProgress = 0
|
||||
}
|
||||
|
||||
trade.tradeProcessing.subscribe {
|
||||
val (processStatus, isInitiator) = it
|
||||
tradeProcessing = processStatus
|
||||
isTradeInitiator = isInitiator
|
||||
tradeAnimationProgress = 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
@@ -178,11 +152,17 @@ class TradeGUI(
|
||||
y = y + 119,
|
||||
parent = this,
|
||||
onPress = {
|
||||
if (offeredPokemon != null && opposingOfferedPokemon != null && protectiveTicks <= 0 && !tradeProcessing) {
|
||||
if (offeredPokemon != null && opposingOfferedPokemon != null && protectiveTicks <= 0) {
|
||||
ticksElapsed = 0
|
||||
readyProgress = 0
|
||||
CobblemonNetwork.sendToServer(ChangeTradeAcceptancePacket(opposingOfferedPokemon!!.uuid, !trade.acceptedOppositeOffer))
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
if (trade.acceptedOppositeOffer) {
|
||||
// trade.acceptedOppositeOffer = false;
|
||||
readyProgress = 0
|
||||
CobblemonNetwork.sendToServer(ChangeTradeAcceptancePacket(opposingOfferedPokemon!!.uuid, false))
|
||||
} else {
|
||||
// trade.acceptedOppositeOffer = true;
|
||||
readyProgress = 0
|
||||
CobblemonNetwork.sendToServer(ChangeTradeAcceptancePacket(opposingOfferedPokemon!!.uuid, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -210,10 +190,9 @@ class TradeGUI(
|
||||
pokemon = pokemon,
|
||||
parent = this,
|
||||
onPress = {
|
||||
if (!trade.acceptedOppositeOffer && !tradeProcessing) {
|
||||
if (!trade.acceptedOppositeOffer) {
|
||||
val pk = if (offeredPokemon?.uuid == pokemon?.pokemonId) null else pokemon
|
||||
CobblemonNetwork.sendToServer(UpdateTradeOfferPacket(pk?.let { it.pokemonId to PartyPosition(partyIndex) }))
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
}
|
||||
}
|
||||
).also { widget -> addRenderableWidget(widget) }
|
||||
@@ -265,59 +244,16 @@ class TradeGUI(
|
||||
height = BASE_BACKGROUND_HEIGHT
|
||||
)
|
||||
|
||||
if (
|
||||
(tradeAnimationProgress in MODEL_SCALE_BUFFER_FRAMES until(TRADE_ANIMATION_FRAMES + MODEL_SCALE_BUFFER_FRAMES)) ||
|
||||
(tradeAnimationProgress in (REVERSE_ANIMATION_TICK..(REVERSE_ANIMATION_TICK + TRADE_ANIMATION_FRAMES)).drop(1))
|
||||
) {
|
||||
val frameOffset = (
|
||||
if (tradeAnimationProgress > REVERSE_ANIMATION_TICK)
|
||||
(REVERSE_ANIMATION_TICK + TRADE_ANIMATION_FRAMES - tradeAnimationProgress)
|
||||
else (tradeAnimationProgress - MODEL_SCALE_BUFFER_FRAMES)
|
||||
) * TRADE_ANIMATION_HEIGHT
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = tradeAnimationResource,
|
||||
x = (x + 80.5) / SCALE,
|
||||
y = (y + 31) / SCALE,
|
||||
width = TRADE_ANIMATION_WIDTH,
|
||||
height = TRADE_ANIMATION_HEIGHT,
|
||||
vOffset = frameOffset,
|
||||
textureHeight = TRADE_ANIMATION_HEIGHT * TRADE_ANIMATION_FRAMES,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = tradeAnimationResource,
|
||||
x = (x + 155.5) / SCALE,
|
||||
y = (y + 31) / SCALE,
|
||||
width = TRADE_ANIMATION_WIDTH,
|
||||
height = TRADE_ANIMATION_HEIGHT,
|
||||
vOffset = frameOffset,
|
||||
textureHeight = TRADE_ANIMATION_HEIGHT * TRADE_ANIMATION_FRAMES,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
|
||||
// Render Model Portraits
|
||||
var scale = PORTRAIT_SCALE
|
||||
var scaleOffsetY = PORTRAIT_MODEL_OFFSET_Y
|
||||
if (tradeProcessing) {
|
||||
val scaleInterval = if (tradeAnimationProgress > REVERSE_ANIMATION_TICK) (MAX_TRADE_PROGRESS - tradeAnimationProgress) else tradeAnimationProgress
|
||||
scale = (PORTRAIT_SCALE - (scaleInterval * PORTRAIT_SCALE_MULTIPLIER)).coerceIn(0F, PORTRAIT_SCALE)
|
||||
scaleOffsetY = (45.0 - (45.0 / PORTRAIT_SCALE) * scale) + PORTRAIT_MODEL_OFFSET_Y
|
||||
}
|
||||
if (scale > 0F) {
|
||||
context.enableScissor(backgroundX, backgroundY, backgroundX + BASE_BACKGROUND_WIDTH, backgroundY + BASE_BACKGROUND_HEIGHT)
|
||||
offeredPokemonModel?.baseScale = scale
|
||||
offeredPokemonModel?.offsetY = scaleOffsetY
|
||||
offeredPokemonModel?.render(context, mouseX, mouseY, delta)
|
||||
|
||||
opposingOfferedPokemonModel?.baseScale = scale
|
||||
opposingOfferedPokemonModel?.offsetY = scaleOffsetY
|
||||
opposingOfferedPokemonModel?.render(context, mouseX, mouseY, delta)
|
||||
context.disableScissor()
|
||||
}
|
||||
context.enableScissor(
|
||||
backgroundX,
|
||||
backgroundY,
|
||||
backgroundX + BASE_BACKGROUND_WIDTH,
|
||||
backgroundY + BASE_BACKGROUND_HEIGHT
|
||||
)
|
||||
offeredPokemonModel?.render(context, mouseX, mouseY, delta)
|
||||
opposingOfferedPokemonModel?.render(context, mouseX, mouseY, delta)
|
||||
context.disableScissor()
|
||||
|
||||
// Render Base Resource
|
||||
blitk(
|
||||
@@ -345,18 +281,16 @@ class TradeGUI(
|
||||
textureHeight = TRADE_READY_HEIGHT * READY_PROGRESS_LIMIT
|
||||
)
|
||||
|
||||
if (!tradeProcessing) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = tradeReadyTopResource,
|
||||
x = x + 112,
|
||||
y = y + 2,
|
||||
width = TRADE_READY_WIDTH,
|
||||
height = TRADE_READY_TOP_HEIGHT,
|
||||
vOffset = TRADE_READY_TOP_HEIGHT * readyProgress,
|
||||
textureHeight = TRADE_READY_TOP_HEIGHT * READY_PROGRESS_LIMIT
|
||||
)
|
||||
}
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = tradeReadyTopResource,
|
||||
x = x + 112,
|
||||
y = y + 2,
|
||||
width = TRADE_READY_WIDTH,
|
||||
height = TRADE_READY_TOP_HEIGHT,
|
||||
vOffset = TRADE_READY_TOP_HEIGHT * readyProgress,
|
||||
textureHeight = TRADE_READY_TOP_HEIGHT * READY_PROGRESS_LIMIT
|
||||
)
|
||||
}
|
||||
|
||||
if (trade.oppositeAcceptedMyOffer.get()) {
|
||||
@@ -371,31 +305,15 @@ class TradeGUI(
|
||||
textureHeight = TRADE_READY_HEIGHT * READY_PROGRESS_LIMIT
|
||||
)
|
||||
|
||||
if (!tradeProcessing) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = opposingTradeReadyTopResource,
|
||||
x = x + 153,
|
||||
y = y + 2,
|
||||
width = TRADE_READY_WIDTH,
|
||||
height = TRADE_READY_TOP_HEIGHT,
|
||||
vOffset = TRADE_READY_TOP_HEIGHT * readyProgress,
|
||||
textureHeight = TRADE_READY_TOP_HEIGHT * READY_PROGRESS_LIMIT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Trade animation arrows
|
||||
if (tradeAnimationProgress >= (TRADE_ANIMATION_FRAMES + MODEL_SCALE_BUFFER_FRAMES) && tradeAnimationProgress < REVERSE_ANIMATION_TICK) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = tradeAnimationArrowsResource,
|
||||
x = x + 112,
|
||||
texture = opposingTradeReadyTopResource,
|
||||
x = x + 153,
|
||||
y = y + 2,
|
||||
width = TRADE_ANIMATION_ARROWS_WIDTH,
|
||||
width = TRADE_READY_WIDTH,
|
||||
height = TRADE_READY_TOP_HEIGHT,
|
||||
vOffset = (((tradeAnimationProgress - MODEL_SCALE_BUFFER_FRAMES) % TRADE_ANIMATION_ARROWS_FRAMES) - MODEL_SCALE_BUFFER_FRAMES) * TRADE_READY_TOP_HEIGHT,
|
||||
textureHeight = TRADE_READY_TOP_HEIGHT * TRADE_ANIMATION_ARROWS_FRAMES
|
||||
vOffset = TRADE_READY_TOP_HEIGHT * readyProgress,
|
||||
textureHeight = TRADE_READY_TOP_HEIGHT * READY_PROGRESS_LIMIT
|
||||
)
|
||||
}
|
||||
|
||||
@@ -404,17 +322,19 @@ class TradeGUI(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = Minecraft.getInstance().user.name.text().bold(),
|
||||
x = x + 13,
|
||||
x = x + 57,
|
||||
y = y - 10.5,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
drawScaledTextJustifiedRight(
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = traderName.bold(),
|
||||
x = x + 280,
|
||||
x = x + 237,
|
||||
y = y - 10.5,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
@@ -439,15 +359,14 @@ class TradeGUI(
|
||||
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
|
||||
if (minecraft?.options?.keyInventory?.matches(keyCode, scanCode) == true) {
|
||||
CancelTradePacket().sendToServer()
|
||||
cancelTradeSound()
|
||||
Minecraft.getInstance().setScreen(null)
|
||||
return true
|
||||
}
|
||||
|
||||
when (keyCode) {
|
||||
InputConstants.KEY_ESCAPE -> {
|
||||
// playSound(CobblemonSounds.PC_OFF)
|
||||
CancelTradePacket().sendToServer()
|
||||
cancelTradeSound()
|
||||
}
|
||||
}
|
||||
return super.keyPressed(keyCode, scanCode, modifiers)
|
||||
@@ -455,35 +374,8 @@ class TradeGUI(
|
||||
|
||||
override fun tick() {
|
||||
ticksElapsed++
|
||||
if (protectiveTicks > 0) protectiveTicks--
|
||||
if (tradeProcessing) {
|
||||
if (tradeAnimationProgress == 0) tradeSoundInstance = playSound(CobblemonSounds.GUI_TRADE)
|
||||
|
||||
tradeAnimationProgress++
|
||||
|
||||
if (offeredPokemon != null && opposingOfferedPokemon != null
|
||||
&& tradeAnimationProgress == REVERSE_ANIMATION_TICK
|
||||
&& isTradeInitiator
|
||||
) {
|
||||
CobblemonNetwork.sendToServer(PerformTradePacket(opposingOfferedPokemon!!.uuid))
|
||||
}
|
||||
|
||||
if (tradeAnimationProgress == (MAX_TRADE_PROGRESS + CRY_BUFFER)) {
|
||||
if (offeredPokemonModel !== null && opposingOfferedPokemonModel !== null) {
|
||||
offeredPokemonModel!!.state.activeAnimations.clear()
|
||||
offeredPokemonModel!!.state.addFirstAnimation(setOf("cry"))
|
||||
opposingOfferedPokemonModel!!.state.activeAnimations.clear()
|
||||
opposingOfferedPokemonModel!!.state.addFirstAnimation(setOf("cry"))
|
||||
}
|
||||
}
|
||||
|
||||
if (tradeAnimationProgress > (MAX_TRADE_PROGRESS + CRY_BUFFER)) {
|
||||
trade.tradeProcessing.set(Pair(false, false))
|
||||
ticksElapsed = 0
|
||||
readyProgress = 0
|
||||
tradeAnimationProgress = 0
|
||||
tradeSoundInstance = null
|
||||
}
|
||||
if (protectiveTicks > 0) {
|
||||
protectiveTicks--
|
||||
}
|
||||
|
||||
// Calculate select pointer offset
|
||||
@@ -496,7 +388,6 @@ class TradeGUI(
|
||||
|
||||
override fun onClose() {
|
||||
CobblemonNetwork.sendToServer(CancelTradePacket())
|
||||
cancelTradeSound()
|
||||
super.onClose()
|
||||
}
|
||||
|
||||
@@ -512,39 +403,33 @@ class TradeGUI(
|
||||
if (isOpposing) {
|
||||
opposingOfferedPokemon = pokemon
|
||||
opposingOfferedPokemonModel = if (pokemon != null) ModelWidget(
|
||||
pX = x + 145,
|
||||
pX = x + 147,
|
||||
pY = y + 30,
|
||||
pWidth = PORTRAIT_SIZE,
|
||||
pHeight = PORTRAIT_SIZE,
|
||||
pokemon = pokemon.asRenderablePokemon(),
|
||||
baseScale = PORTRAIT_SCALE,
|
||||
baseScale = 2F,
|
||||
rotationY = 35F,
|
||||
offsetY = PORTRAIT_MODEL_OFFSET_Y
|
||||
offsetY = -10.0
|
||||
) else null
|
||||
trade.acceptedOppositeOffer = false
|
||||
} else {
|
||||
offeredPokemon = pokemon
|
||||
offeredPokemonModel = if (pokemon != null) ModelWidget(
|
||||
pX = x + 70,
|
||||
pX = x + 68,
|
||||
pY = y + 30,
|
||||
pWidth = PORTRAIT_SIZE,
|
||||
pHeight = PORTRAIT_SIZE,
|
||||
pokemon = pokemon.asRenderablePokemon(),
|
||||
baseScale = PORTRAIT_SCALE,
|
||||
baseScale = 2F,
|
||||
rotationY = 325F,
|
||||
offsetY = PORTRAIT_MODEL_OFFSET_Y
|
||||
offsetY = -10.0
|
||||
) else null
|
||||
}
|
||||
}
|
||||
|
||||
private fun playSound(soundEvent: SoundEvent): SoundInstance {
|
||||
val soundInstance = SimpleSoundInstance.forUI(soundEvent, 1.0F)
|
||||
Minecraft.getInstance().soundManager.play(soundInstance)
|
||||
return soundInstance
|
||||
}
|
||||
|
||||
private fun cancelTradeSound() {
|
||||
if (tradeSoundInstance !== null) Minecraft.getInstance().soundManager.stop(tradeSoundInstance)
|
||||
private fun playSound(soundEvent: SoundEvent) {
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
|
||||
}
|
||||
|
||||
private fun renderPokemonInfo(pokemon: Pokemon?, isOpposing: Boolean, context: GuiGraphics, x: Int, y: Int, mouseX: Int, mouseY: Int) {
|
||||
@@ -621,7 +506,7 @@ class TradeGUI(
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = Summary.iconShinyResource,
|
||||
x = (x + (if (isOpposing) 213.5 else 71.5)) / SCALE,
|
||||
x = (x + (if (isOpposing) 214.5 else 71.5)) / SCALE,
|
||||
y = (y + 33.5) / SCALE,
|
||||
width = 16,
|
||||
height = 16,
|
||||
|
||||
+6
-7
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.keybind.keybinds
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendToServer
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
|
||||
import com.cobblemon.mod.common.client.keybind.CobblemonBlockingKeyBinding
|
||||
@@ -78,12 +77,12 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
|
||||
|
||||
if (CobblemonClient.storage.selectedSlot != -1 && Minecraft.getInstance().screen == null) {
|
||||
val pokemon = CobblemonClient.storage.myParty.get(CobblemonClient.storage.selectedSlot)
|
||||
if (pokemon != null) {
|
||||
if (pokemon != null && pokemon.currentHealth > 0) {
|
||||
val targetEntity = player.traceFirstEntityCollision(
|
||||
entityClass = LivingEntity::class.java,
|
||||
ignoreEntity = player,
|
||||
maxDistance = Cobblemon.config.battleSpectateMaxDistance,
|
||||
collideBlock = ClipContext.Fluid.NONE)
|
||||
entityClass = LivingEntity::class.java,
|
||||
ignoreEntity = player,
|
||||
maxDistance = Cobblemon.config.battleSpectateMaxDistance,
|
||||
collideBlock = ClipContext.Fluid.NONE)
|
||||
if (targetEntity == null || (targetEntity is PokemonEntity && targetEntity.ownerUUID == player.uuid)) {
|
||||
sendToServer(SendOutPokemonPacket(CobblemonClient.storage.selectedSlot))
|
||||
}
|
||||
@@ -103,7 +102,7 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
|
||||
}
|
||||
is PokemonEntity -> {
|
||||
if (!entity.canBattle(player) || entity.position().distanceToSqr(player.position()) > Cobblemon.config.battleWildMaxDistance.pow(2)) return
|
||||
sendToServer(BattleChallengePacket(entity.id, pokemon.uuid, BattleFormat.GEN_9_SINGLES))
|
||||
sendToServer(BattleChallengePacket(entity.id, pokemon.uuid))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user