Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da76b2fb94 | ||
|
|
87d8197dd0 | ||
|
|
2ed6f89d09 | ||
|
|
5947468cd9 | ||
|
|
7ba1507ac8 | ||
|
|
9fdffdc392 | ||
|
|
6a01fe3863 | ||
|
|
9b5a153e3f | ||
|
|
def975c03f | ||
|
|
e67567e8dc | ||
|
|
23a42a29b3 | ||
|
|
2e88d971f7 | ||
|
|
bfc1c66097 | ||
|
|
13d9f84d8d | ||
|
|
148eb130d9 | ||
|
|
eb07b0e03f | ||
|
|
1b8d416962 | ||
|
|
3030b544b5 | ||
|
|
b890d662f6 | ||
|
|
2a9bbc1d68 | ||
|
|
735817869a | ||
|
|
488e6dcf7a | ||
|
|
a0f32ea444 | ||
|
|
182e06205d | ||
|
|
4c391645fe | ||
|
|
79bf50004a | ||
|
|
396ab13164 | ||
|
|
c10c91d86a | ||
|
|
d5fd2e9a07 | ||
|
|
45cc0ba579 | ||
|
|
1880a0a24e | ||
|
|
5f3a0b96cc | ||
|
|
7ab1610700 | ||
|
|
daad9f6c00 | ||
|
|
2b234b740e | ||
|
|
39e3983020 | ||
|
|
9d58cdb21c | ||
|
|
f05e35e792 | ||
|
|
a333e68620 | ||
|
|
2c2791ec69 | ||
|
|
635b09d82f | ||
|
|
46c3ff9db4 | ||
|
|
8bfc95c23a | ||
|
|
99d05b6ef7 | ||
|
|
1c0a14c819 | ||
|
|
fe5cd58801 | ||
|
|
c3bd007f5a | ||
|
|
dba24e104e | ||
|
|
35815e371b | ||
|
|
d0f8ae0565 | ||
|
|
b2e21969ca | ||
|
|
ea57c233a3 | ||
|
|
0d39b47f22 | ||
|
|
9f3a279b03 |
No files matched your search
@@ -1,173 +1,35 @@
|
||||
# Changelog
|
||||
## [1.8.5 (MONTH xth, 2026)](#1-8-5)
|
||||
## [1.9.0 (MONTH xth, 2026)](#1-9-0)
|
||||
|
||||
### Additions
|
||||
- Added trades for Saccharine Saplings, Hearty Grains, Chipped Pot, and Masterpiece Teacup to the Wandering Trader.
|
||||
- Added brewing recipe for Throat Spray.
|
||||
- Added Berry Tree scanning for Pokedex
|
||||
- Added Datapackable Pokedex Scanning for Block Entities
|
||||
- Added config options to award experience to fainted Pokémon for opponents they helped defeat (`awardExperienceToFaintedPokemon`) and to still award experience for defeated enemies even after battle loss/forfeit (`awardExperienceOnBattleLoss`)
|
||||
- Party Overlay Exp Gained Animation will now display the amount of EXP the Pokémon gained
|
||||
- Added a toast to notify players of how they can scroll through their party. Did you know you can hold R and scroll instead of using arrow keys??!
|
||||
- Added a new layer property that makes a layer scroll in a direction. "scrolling": {"speedU": 0.1, "speedV": 0.1}
|
||||
- Added missing crops to Botany Pots integration
|
||||
- Added a way to obtain Porygon by inserting the Upgrade or Dubious Disc into a data monitor
|
||||
- Added a new `cobblemon:structure`, which is a feature without any restrictions allowing the Builders to place certain structures in much more specific manner.
|
||||
- Added Configs for Infinite Use TMs and Unlocking all TMs
|
||||
- Added Type Gem blocks
|
||||
- Added Type Gem Clusters
|
||||
- Added TMs
|
||||
- Added TM Machine
|
||||
- Added Deepslate Core Block
|
||||
- Added /givetm Command
|
||||
- Added advancement triggers for learning TMs: `cobblemon:has_learn_specific_tm` (per-TM), `cobblemon:has_learn_all_tm` (all TMs learned), and `cobblemon:tms_learned` (count-based), backed by a new `totalTMLearnedCount` on player advancement data.
|
||||
- Added `cobblemon:catch_alpha_pokemon` advancement trigger and `totalAlphaCaptureCount` tracking for capturing Alpha Pokémon.
|
||||
- Added Move Dex to Pokédex
|
||||
- Added the Habitat Block, a spawner block for servers and adventure maps that controls spawning in an area.
|
||||
- Added Disk Rack for storing disks and being an audio sequencer for Noteblocks
|
||||
- Added intrinsic scaling to all mons (+-5%). Some rendering cases, such as shoulder mounting, revert to 1.0 scaling to preserve animation accuracy.
|
||||
- Added scaling to "baby" Pokémon (smaller (-20%) at level 1 → normally sized at level 10)
|
||||
- Added conditional seats for riding. Seats can be gated by molang such as "is_alpha".
|
||||
- Added Alpha Pokémon
|
||||
- Added alpha scaling (alphas are larger than their normal counterparts)
|
||||
- Alphas will always lead herds and will spawn in herds
|
||||
- Alphas spawn with two random TM moves from their learnset
|
||||
- Alpha Pokémon will always defend themselves from players that attack them
|
||||
- Added alpha level matching and stat boost (based on level)
|
||||
- Added pitch-down and reverb to alpha cries
|
||||
- Added alpha Pokémon bait effect
|
||||
- Added alpha eye rendering including eye overlay, bloom, eye trail, and particles.
|
||||
- Added cobweb block slowness immunity for spider Pokémon
|
||||
- Added 49 new habitat structures. 34 as typical Minecraft structures and 15 as structures that get placed as biome features to allow for more natural placement in generation. Structures placed as biome features will not be able to be located with the `/locate structure` command.
|
||||
- Vanilla village house template pools have been adjusted to account for the new abandoned village structure habitat. Weights have changed within the JSON file for every piece to ensure too many habitats don't generate in a single village.
|
||||
- Added 10 new ruin structures.
|
||||
- rooted_arch_ruins & crumbling_arch_ruins have been transferred over to biome features, allowing for better placement in generation.
|
||||
- All ruin structures that did not generate with a gimmighoul/gilded chest before will now generate with one.
|
||||
- All ruin structures gilded chest loot tables have been updated to include a guaranteed TM not learned naturally by any Pokémon through their level-up moveset. Each ruin gets its own specific TM, so you can now go hunting ruins to complete your move dex!
|
||||
- All fossil structures have been transferred over to biome features, allowing for better placement in generation.
|
||||
- The prehistoric_underwater_fissure & prehistoric_submerged_impact fossil structures now use the biome tag `#cobblemon:is_temperate_ocean`.
|
||||
- A new Biome tag called `#cobblemon:is_stony_beach` has been added.
|
||||
- Gimmighoul towers now generate at ground level rather than sinking one block into the ground.
|
||||
- 1 New shipwreck cove structure called the "magma_shipwreck_cove" has been added.
|
||||
- Shipwreck coves now generate slightly more frequently.
|
||||
- Added equipment loot tables for shipwreck cove enemies.
|
||||
- Maps from abandoned fishing boats leading to shipwreck coves now will skip explored chunks, allowing the player to find more shipwreck coves easier.
|
||||
- The lush shipwreck cove's phantom mob spawner has been replaced with breezes.
|
||||
- The lush shipwreck cove structure itself has been slightly cleaned up to be less noisy with the blocks it is built out of.
|
||||
- All shipwreck coves' spawners have been adjusted to give compatible mobs enchanted iron or diamond armour and weapons while in its ominous state. The armour given to the mobs will also use the automaton trim. We upped the difficulty because it was too easy now that your Pokémon can fight for you.
|
||||
- One TM of the elemental hyper beams has been added as guaranteed loot to the main treasure chest in every shipwreck cove. (Magma cove gets blast burn, lush cove gets frenzy plant, and submerged cove gets hydro cannon)
|
||||
- Added 7 new advancements
|
||||
|
||||
### New rideable Pokémon
|
||||
- Drampa
|
||||
- Archeops
|
||||
|
||||
### Pokémon Added
|
||||
- Pancham (+ Cosmetic with Black Glasses)
|
||||
- Pangoro (+ Cosmetic with Black Glasses)
|
||||
- Skitty
|
||||
- Delcatty
|
||||
- Wurmple
|
||||
- Silcoon
|
||||
- Beautifly
|
||||
- Cascoon
|
||||
- Dustox
|
||||
- Audino
|
||||
- Rockruff
|
||||
- Lycanroc
|
||||
- Snom
|
||||
- Frosmoth
|
||||
- Applin
|
||||
- Flapple
|
||||
- Appletun
|
||||
- Dipplin
|
||||
- Hydrapple
|
||||
- Sandshrew (Alolan)
|
||||
- Sandslash (Alolan)
|
||||
- Seviper
|
||||
- Zangoose
|
||||
- Stunky
|
||||
- Skuntank
|
||||
- Pawniard
|
||||
- Bisharp
|
||||
- Kingambit
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Goomy
|
||||
- Sliggoo
|
||||
- Goodra
|
||||
- Porygon
|
||||
- Porygon2
|
||||
- Porygon-Z
|
||||
- Zubat
|
||||
- Golbat
|
||||
- Crobat
|
||||
- Pidgey
|
||||
- Pidgeotto
|
||||
- Pidgeot
|
||||
- Sandslash
|
||||
- Nidoking
|
||||
- Sableye
|
||||
- Mawile
|
||||
- Nacli
|
||||
- Naclestack
|
||||
- Garganacl
|
||||
- Skarmory
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
- Drampa
|
||||
- Archeops
|
||||
- Skarmory
|
||||
- Riolu
|
||||
- Lucario
|
||||
- Shellos
|
||||
- Skitty
|
||||
- Joltik
|
||||
- Purrloin
|
||||
- Liepard
|
||||
- Flygon
|
||||
|
||||
#### Added sounds to the following moves
|
||||
- Absorb
|
||||
- Aurora Beam
|
||||
- Double Team
|
||||
- Fire Punch
|
||||
- Fire Spin
|
||||
- Flame Charge
|
||||
- Flame Wheel
|
||||
- Giga Drain
|
||||
- Ice Punch
|
||||
- Leech Life
|
||||
- Magical Leaf
|
||||
- Mega Drain
|
||||
- Minimize
|
||||
- Poison Gas
|
||||
- Rock Throw
|
||||
- Scary Face
|
||||
- Seed bomb
|
||||
- Smokescreen
|
||||
- String Shot
|
||||
- Tail Whip
|
||||
- Added the `healersSetSpawn` gamerule. When enabled, using a healing machine will set your spawnpoint.
|
||||
- A Pokémon holding a Power item now gains 1 point in the corresponding riding stat every two minutes while being ridden. The interval is configurable, and can be disabled with `-1`.
|
||||
- Added sounds to Gigaton Hammer and Pyro Ball
|
||||
- Added stepsounds to Venusaur, Blastoise, Dewgong, Rhydon, Rhyhorn, Parasect and Mr. Mime.
|
||||
- Trainer bags and packs got added
|
||||
- You can craft and equip those to store additional items
|
||||
|
||||
### Changes
|
||||
- Amethyst now grows from Typegem core blocks like typegems do
|
||||
- Updated the recipe category for the Create compatibility EXP Candy recipe
|
||||
- Added No Man's Lands' Honeyed Apple to the Leaves Leftovers tag
|
||||
- Trading has been overhauled to allow offering multiple Pokémon, up to the entire party, in a single trade
|
||||
- Selected Pokémon are now highlighted in both parties with new party slot assets
|
||||
- Hovering an offered Pokémon now shows its summary, and clicking the active Pokémon no longer exits the summary view
|
||||
- The trade animation now moves the offered Pokémon towards the Poké Ball
|
||||
- Parties are compacted after a trade so no empty slots are left between Pokémon
|
||||
|
||||
### Fixes
|
||||
- Fixed Ponigiri food values for AppleSkin integration
|
||||
- Fixed errors emitted by typos in Parched Peat bog structure files
|
||||
|
||||
### Developer
|
||||
- Trade network packets (`TradeUpdatedPacket`, `UpdateTradeOfferPacket`, and `TradeCompletedPacket`) now receive an array of Pokémon instead of a single Pokémon to execute trades
|
||||
- `TradeEvent.Pre` and `TradeEvent.Post` now expose every Pokémon each participant receives (`tradeParticipant1Pokemon` and `tradeParticipant2Pokemon` are now lists), and their Molang context includes `trade_participant_1_pokemon_list` and `trade_participant_2_pokemon_list`
|
||||
|
||||
### Molang & Datapacks
|
||||
- `q.is_standing_on_blocks` now properly checks for non-full blocks like slabs, Mud or Soul Sand
|
||||
- Added Molang functions to play footstep sounds based on the corresponding surface. These are intended to be used by Pokémon animations.
|
||||
- Pokémon can now have their Minecraft footstep sounds silenced in their poser. This doesn't affect emitting game events.
|
||||
- `bag_categories` got added which control what kind of tabs and capabilities the Trainerbags have
|
||||
```json
|
||||
{
|
||||
"order": 1,
|
||||
"icon": "cobblemon:textures/item/medicine/potion.png",
|
||||
"whiteListTag": "cobblemon:bag/whitelist_consumables",
|
||||
"blackListTag": "cobblemon:bag/blacklist_consumables",
|
||||
"maxCount": 512
|
||||
}
|
||||
```
|
||||
- Bunch of default tags as white/blacklist got added to control what can be inserted the corresponding bag categories
|
||||
|
||||
### Particles
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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.bag;
|
||||
|
||||
import com.cobblemon.mod.common.item.bag.CobblemonBagContainer;
|
||||
|
||||
public interface BagEntity {
|
||||
CobblemonBagContainer cobblemon$getBagContainer();
|
||||
void cobblemon$setBagContainer(CobblemonBagContainer bagContainer);
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import com.cobblemon.mod.common.api.tags.CobblemonItemTags;
|
||||
import com.cobblemon.mod.common.duck.PlayerDuck;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.bag.BagEntity;
|
||||
import com.cobblemon.mod.common.item.bag.CobblemonBagContainer;
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData;
|
||||
import com.cobblemon.mod.common.pokedex.scanner.ScannableEntity;
|
||||
import com.cobblemon.mod.common.pokemon.FormData;
|
||||
@@ -56,6 +58,7 @@ import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector3f;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
@@ -73,7 +76,7 @@ import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(Player.class)
|
||||
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable, PlayerDuck, RidePassenger {
|
||||
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable, PlayerDuck, RidePassenger, BagEntity {
|
||||
|
||||
@Unique
|
||||
private Vector3f cobblemon$driverInput;
|
||||
@@ -107,6 +110,8 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
|
||||
|
||||
@Unique private Vec3 cobblemon$rideEyePos = Vec3.ZERO;
|
||||
|
||||
@Unique private CobblemonBagContainer cobblemon$cobblemonBagContainer = new CobblemonBagContainer();
|
||||
|
||||
protected PlayerMixin(EntityType<? extends LivingEntity> p_20966_, Level p_20967_) {
|
||||
super(p_20966_, p_20967_);
|
||||
}
|
||||
@@ -404,4 +409,37 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
|
||||
original.call(target, strength, x, z);
|
||||
}
|
||||
|
||||
@Override @NotNull
|
||||
public CobblemonBagContainer cobblemon$getBagContainer() { return cobblemon$cobblemonBagContainer; }
|
||||
|
||||
@Override
|
||||
public void cobblemon$setBagContainer(CobblemonBagContainer bagContainer) { cobblemon$cobblemonBagContainer = bagContainer; }
|
||||
|
||||
@Inject(
|
||||
method = "readAdditionalSaveData(Lnet/minecraft/nbt/CompoundTag;)V",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
private void cobblemon$readAdditionalSaveData(CompoundTag compound, CallbackInfo ci) {
|
||||
if (compound.contains("CobblemonBagData", 9)) {
|
||||
this.cobblemon$cobblemonBagContainer.fromTag(compound.getList("CobblemonBagData", 10), this.registryAccess());
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "addAdditionalSaveData(Lnet/minecraft/nbt/CompoundTag;)V",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
private void cobblemon$addAdditionalSaveData(CompoundTag compound, CallbackInfo ci) {
|
||||
compound.put("CobblemonBagData", this.cobblemon$cobblemonBagContainer.createTag(this.registryAccess()));
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "dropEquipment()V",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
private void cobblemon$dropCobblemonBag(CallbackInfo ci) {
|
||||
if (level().getGameRules().getBoolean(CobblemonGameRules.BAG_DROP_ITEMS)) {
|
||||
this.cobblemon$cobblemonBagContainer.dropAll((Player)(Object)this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.api.stats.CobblemonStats;
|
||||
import com.cobblemon.mod.common.block.HealingMachineBlock;
|
||||
import com.cobblemon.mod.common.duck.ServerLevelDuck;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.bag.BagEntity;
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
@@ -163,4 +164,12 @@ public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
|
||||
cir.setReturnValue(HealingMachineBlock.Companion.findStandUpPosition(EntityType.PLAYER, level, pos).map((vec3) -> ServerPlayer.RespawnPosAngle.of(vec3, pos)));
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "restoreFrom",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
private void cobblemon$restoreCobblemonBag(ServerPlayer that, boolean keepEverything, CallbackInfo ci) {
|
||||
((BagEntity)(Object)this).cobblemon$setBagContainer(((BagEntity)that).cobblemon$getBagContainer());
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import com.cobblemon.mod.common.client.pokedex.PokedexType
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
import com.cobblemon.mod.common.item.*
|
||||
import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
|
||||
import com.cobblemon.mod.common.item.bag.CobblemonBagItem
|
||||
import com.cobblemon.mod.common.item.battle.DireHitItem
|
||||
import com.cobblemon.mod.common.item.battle.GuardSpecItem
|
||||
import com.cobblemon.mod.common.item.battle.XStatItem
|
||||
@@ -1577,6 +1578,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val LUCIBELL = compostableBlockItem("lucibell", CobblemonBlocks.LUCIBELL, 0.85f)
|
||||
|
||||
//Bags
|
||||
@JvmField
|
||||
val TRAINER_BAG = this.create("trainer_bag", CobblemonBagItem())
|
||||
@JvmField
|
||||
val TRAINER_PACK = this.create("trainer_pack", CobblemonBagItem())
|
||||
|
||||
private fun blockItem(name: String, block: Block, rarity: Rarity = Rarity.COMMON): BlockItem = this.create(name, BlockItem(block, Item.Properties().rarity(rarity)))
|
||||
|
||||
private fun itemNameBlockItem(name: String, block: Block, rarity: Rarity = Rarity.COMMON): ItemNameBlockItem = this.create(name, ItemNameBlockItem(block, Item.Properties().rarity(rarity)))
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common
|
||||
|
||||
|
||||
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
|
||||
import com.cobblemon.mod.common.item.bag.CobblemonBagMenu
|
||||
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import net.minecraft.core.Registry
|
||||
@@ -20,8 +21,9 @@ import net.minecraft.world.inventory.MenuType
|
||||
|
||||
object CobblemonMenuType : PlatformRegistry<Registry<MenuType<*>>, ResourceKey<Registry<MenuType<*>>>, MenuType<*>>() {
|
||||
|
||||
val COOKING_POT = MenuType.register("cooking_pot_menu", ::CookingPotMenu)
|
||||
val COOKING_POT = MenuType.register("cooking_pot", ::CookingPotMenu)
|
||||
val TM_MACHINE = MenuType.register("tm_machine_menu", ::TMMachineMenu)
|
||||
val COBBLEMON_BAG = MenuType.register("cobblemon_bag", ::CobblemonBagMenu)
|
||||
|
||||
override val registry: Registry<MenuType<*>>
|
||||
get() = BuiltInRegistries.MENU
|
||||
|
||||
@@ -13,6 +13,8 @@ import com.cobblemon.mod.common.client.net.OpenBehaviourEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.PlayerInteractOptionsHandler
|
||||
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
|
||||
import com.cobblemon.mod.common.client.net.animation.PlayPosableAnimationHandler
|
||||
import com.cobblemon.mod.common.client.net.bag.UpdateBagMenuDataHandler
|
||||
import com.cobblemon.mod.common.client.net.bag.UseItemOnPokemonFromBagHandler
|
||||
import com.cobblemon.mod.common.client.net.battle.*
|
||||
import com.cobblemon.mod.common.client.net.callback.move.OpenMoveCallbackHandler
|
||||
import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandler
|
||||
@@ -76,6 +78,8 @@ import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.bag.UpdateBagMenuDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.bag.UseItemOnPokemonFromBagPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.*
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
|
||||
@@ -444,6 +448,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideSettingsSyncPacket.ID, RideSettingsSyncPacket::decode, RideSettingsSyncHandler))
|
||||
list.add(PacketRegisterInfo(BagCategoriesSyncPacket.ID, BagCategoriesSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(TechnicalMachineRegistrySyncPacket.ID, TechnicalMachineRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
|
||||
// Effects
|
||||
@@ -640,6 +645,10 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(SetActiveTMPacket.ID, SetActiveTMPacket::decode, SetActiveTMPacketHandler))
|
||||
list.add(PacketRegisterInfo(SetTMMachineContainerDataPacket.ID, SetTMMachineContainerDataPacket::decode, SetTMMachineContainerDataPacketHandler))
|
||||
|
||||
// Bag
|
||||
list.add(PacketRegisterInfo(UpdateBagMenuDataPacket.ID, UpdateBagMenuDataPacket::decode, UpdateBagMenuDataHandler))
|
||||
list.add(PacketRegisterInfo(UseItemOnPokemonFromBagPacket.ID, UseItemOnPokemonFromBagPacket::decode, UseItemOnPokemonFromBagHandler))
|
||||
|
||||
// Behaviour Packets
|
||||
list.add(PacketRegisterInfo(SetEntityBehaviourPacket.ID, SetEntityBehaviourPacket::decode, SetEntityBehaviourHandler))
|
||||
list.add(PacketRegisterInfo(DamageOnCollisionPacket.ID, DamageOnCollisionPacket::decode, DamageOnCollisionPacketHandler))
|
||||
@@ -647,7 +656,7 @@ object CobblemonNetwork {
|
||||
// Habitat blocks
|
||||
list.add(PacketRegisterInfo(SaveHabitatBlockSettingsPacket.ID, SaveHabitatBlockSettingsPacket::decode, SaveHabitatBlockSettingsHandler))
|
||||
|
||||
// Debug / cheats
|
||||
// Debug
|
||||
list.add(PacketRegisterInfo(RequestOpenRidingStatsDebugGUIPacket.ID, RequestOpenRidingStatsDebugGUIPacket::decode, RequestOpenRidingStatsDebugGUIHandler))
|
||||
|
||||
|
||||
|
||||
@@ -321,6 +321,19 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK_PLACE = this.create("block.medicinal_leek.plant")
|
||||
|
||||
@JvmField
|
||||
val ITEM_BAG_OPEN = this.create("item.bag.open")
|
||||
@JvmField
|
||||
val ITEM_BAG_CLOSE = this.create("item.bag.close")
|
||||
@JvmField
|
||||
val ITEM_BAG_CLICK = this.create("item.bag.click")
|
||||
@JvmField
|
||||
val ITEM_BAG_ITEM_IN = this.create("item.bag.item_in")
|
||||
@JvmField
|
||||
val ITEM_BAG_ITEM_OUT = this.create("item.bag.item_out")
|
||||
@JvmField
|
||||
val ITEM_BAG_EQUIP = this.create("item.bag.equip")
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST_OPEN = this.create("block.gilded_chest.open")
|
||||
@JvmField
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.bag
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.BagCategoriesSyncPacket
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.TagKeyAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.item.Item
|
||||
|
||||
object BagCategories: JsonDataRegistry<BagCategory> {
|
||||
|
||||
override val id: ResourceLocation = cobblemonResource("bag_categories")
|
||||
override val type: PackType = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<BagCategories>()
|
||||
|
||||
override val gson = GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(TagKey::class.java, Item::class.java).type, TagKeyAdapter(Registries.ITEM))
|
||||
.create()
|
||||
|
||||
override val typeToken: TypeToken<BagCategory> = TypeToken.get(BagCategory::class.java)
|
||||
override val resourcePath = "bag_categories"
|
||||
|
||||
private val bagCategories = hashMapOf<ResourceLocation, BagCategory>()
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, BagCategory>) {
|
||||
bagCategories.clear()
|
||||
data.forEach { identifier, category ->
|
||||
try {
|
||||
category.identifier = identifier
|
||||
bagCategories[identifier] = category
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("Skipped loading the {} category", identifier, e)
|
||||
}
|
||||
}
|
||||
Cobblemon.LOGGER.info("Loaded {} bag categories", bagCategories.size)
|
||||
this.observable.emit(this)
|
||||
}
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
BagCategoriesSyncPacket(bagCategories.values.toList())
|
||||
}
|
||||
|
||||
fun getOrderedCategoriesList(): List<BagCategory> {
|
||||
return bagCategories.values.toList().sortedBy { it.order }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.bag
|
||||
|
||||
import com.cobblemon.mod.common.util.readIdentifier
|
||||
import com.cobblemon.mod.common.util.writeIdentifier
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
class BagCategory(
|
||||
identifier: ResourceLocation,
|
||||
val order: Int,
|
||||
val icon: ResourceLocation,
|
||||
val whiteListTag: TagKey<Item>,
|
||||
val blackListTag: TagKey<Item>,
|
||||
val maxCount: Int
|
||||
): StringRepresentable {
|
||||
@Transient
|
||||
var identifier: ResourceLocation = identifier
|
||||
internal set
|
||||
|
||||
override fun getSerializedName(): String = identifier.toString()
|
||||
|
||||
fun getName(): MutableComponent = Component.translatable("${identifier.namespace}.bag_category.${identifier.path}")
|
||||
|
||||
fun itemIsValid(itemStack: ItemStack): Boolean {
|
||||
if (itemStack.`is`(blackListTag)) return false
|
||||
if (itemStack.`is`(whiteListTag)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeIdentifier(identifier)
|
||||
buffer.writeInt(order)
|
||||
buffer.writeIdentifier(icon)
|
||||
buffer.writeIdentifier(whiteListTag.location)
|
||||
buffer.writeIdentifier(blackListTag.location)
|
||||
buffer.writeInt(maxCount)
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal fun decode(buffer: RegistryFriendlyByteBuf): BagCategory {
|
||||
return BagCategory(
|
||||
buffer.readIdentifier(),
|
||||
buffer.readInt(),
|
||||
buffer.readIdentifier(),
|
||||
TagKey.create(Registries.ITEM, buffer.readIdentifier()),
|
||||
TagKey.create(Registries.ITEM, buffer.readIdentifier()),
|
||||
buffer.readInt()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,14 @@
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.trade.TradeParticipant
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
|
||||
/**
|
||||
@@ -31,9 +33,9 @@ interface TradeEvent {
|
||||
val tradeParticipant1: TradeParticipant
|
||||
|
||||
/**
|
||||
* The [Pokemon] being traded by the first participant.
|
||||
* All the [Pokemon] the first participant receives in the trade.
|
||||
*/
|
||||
val tradeParticipant1Pokemon: Pokemon
|
||||
val tradeParticipant1Pokemon: List<Pokemon>
|
||||
|
||||
/**
|
||||
* The second [TradeParticipant].
|
||||
@@ -41,9 +43,19 @@ interface TradeEvent {
|
||||
val tradeParticipant2: TradeParticipant
|
||||
|
||||
/**
|
||||
* The [Pokemon] being traded by the second participant.
|
||||
* All the [Pokemon] the second participant receives in the trade.
|
||||
*/
|
||||
val tradeParticipant2Pokemon: Pokemon
|
||||
val tradeParticipant2Pokemon: List<Pokemon>
|
||||
|
||||
fun createContext(): MutableMap<String, MoValue> = mutableMapOf(
|
||||
"trade_participant_1" to (tradeParticipant1.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"trade_participant_2" to (tradeParticipant2.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
// Kept for backwards compatibility, this is the first Pokémon of each side.
|
||||
"trade_participant_1_pokemon" to tradeParticipant1Pokemon.first().struct,
|
||||
"trade_participant_2_pokemon" to tradeParticipant2Pokemon.first().struct,
|
||||
"trade_participant_1_pokemon_list" to tradeParticipant1Pokemon.asArrayValue { it.struct },
|
||||
"trade_participant_2_pokemon_list" to tradeParticipant2Pokemon.asArrayValue { it.struct }
|
||||
)
|
||||
|
||||
/**
|
||||
* Event fired when a trade is about to happen. Cancelling this event prevents the trade from occurring.
|
||||
@@ -53,16 +65,11 @@ interface TradeEvent {
|
||||
*/
|
||||
data class Pre(
|
||||
override val tradeParticipant1: TradeParticipant,
|
||||
override val tradeParticipant1Pokemon: Pokemon,
|
||||
override val tradeParticipant1Pokemon: List<Pokemon>,
|
||||
override val tradeParticipant2: TradeParticipant,
|
||||
override val tradeParticipant2Pokemon: Pokemon
|
||||
override val tradeParticipant2Pokemon: List<Pokemon>
|
||||
) : TradeEvent, Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"trade_participant_1" to (tradeParticipant1.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"trade_participant_2" to (tradeParticipant2.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"trade_participant_1_pokemon" to tradeParticipant1Pokemon.struct,
|
||||
"trade_participant_2_pokemon" to tradeParticipant2Pokemon.struct
|
||||
)
|
||||
val context = createContext()
|
||||
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
@@ -77,15 +84,10 @@ interface TradeEvent {
|
||||
*/
|
||||
data class Post(
|
||||
override val tradeParticipant1: TradeParticipant,
|
||||
override val tradeParticipant1Pokemon: Pokemon,
|
||||
override val tradeParticipant1Pokemon: List<Pokemon>,
|
||||
override val tradeParticipant2: TradeParticipant,
|
||||
override val tradeParticipant2Pokemon: Pokemon
|
||||
override val tradeParticipant2Pokemon: List<Pokemon>
|
||||
) : TradeEvent {
|
||||
val context = mutableMapOf(
|
||||
"trade_participant_1" to (tradeParticipant1.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"trade_participant_2" to (tradeParticipant2.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"trade_participant_1_pokemon" to tradeParticipant1Pokemon.struct,
|
||||
"trade_participant_2_pokemon" to tradeParticipant2Pokemon.struct
|
||||
)
|
||||
val context = createContext()
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,9 @@
|
||||
package com.cobblemon.mod.common.api.riding.stats
|
||||
|
||||
import com.cobblemon.mod.common.api.cooking.Flavour
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.item.Item
|
||||
|
||||
/**
|
||||
* A stat that is used for riding Pokémon. The exact implementation of each of these depends on the [com.cobblemon.mod.common.api.riding.controller.RideController].
|
||||
@@ -23,14 +20,13 @@ import net.minecraft.world.item.Item
|
||||
* @since February 17th, 2025
|
||||
*/
|
||||
enum class RidingStat(
|
||||
val flavour: Flavour,
|
||||
val powerItem: TagKey<Item>
|
||||
val flavour: Flavour
|
||||
) {
|
||||
ACCELERATION(Flavour.SPICY, CobblemonItemTags.POWER_BRACER),
|
||||
SKILL(Flavour.DRY, CobblemonItemTags.POWER_BELT),
|
||||
SPEED(Flavour.SWEET, CobblemonItemTags.POWER_ANKLET),
|
||||
STAMINA(Flavour.SOUR, CobblemonItemTags.POWER_WEIGHT),
|
||||
JUMP(Flavour.BITTER, CobblemonItemTags.POWER_BAND);
|
||||
ACCELERATION(Flavour.SPICY),
|
||||
SKILL(Flavour.DRY),
|
||||
SPEED(Flavour.SWEET),
|
||||
STAMINA(Flavour.SOUR),
|
||||
JUMP(Flavour.BITTER);
|
||||
|
||||
companion object {
|
||||
fun getByName(name: String) = RidingStat.entries.firstOrNull { it.name.equals(name, ignoreCase = true) }
|
||||
|
||||
@@ -140,6 +140,20 @@ object CobblemonItemTags {
|
||||
@JvmField val WEARABLE_HAT_ITEMS = create("held/visibility/hat")
|
||||
@JvmField val HIDDEN_ITEMS = create("held/visibility/hidden")
|
||||
|
||||
//Bag Categories Tags
|
||||
@JvmField val BAG_WHITELIST_POKE_BALLS = create("bag/whitelist_poke_balls")
|
||||
@JvmField val BAG_BLACKLIST_POKE_BALLS = create("bag/blacklist_poke_balls")
|
||||
@JvmField val BAG_WHITELIST_CONSUMABLES = create("bag/whitelist_consumables")
|
||||
@JvmField val BAG_BLACKLIST_CONSUMABLES = create("bag/blacklist_consumables")
|
||||
@JvmField val BAG_WHITELIST_BERRIES = create("bag/whitelist_berries")
|
||||
@JvmField val BAG_BLACKLIST_BERRIES = create("bag/blacklist_berries")
|
||||
@JvmField val BAG_WHITELIST_HELD_EVO = create("bag/whitelist_held_evo")
|
||||
@JvmField val BAG_BLACKLIST_HELD_EVO = create("bag/blacklist_held_evo")
|
||||
@JvmField val BAG_WHITELIST_CURRENCY = create("bag/whitelist_currency")
|
||||
@JvmField val BAG_BLACKLIST_CURRENCY = create("bag/blacklist_currency")
|
||||
@JvmField val BAG_WHITELIST_TMS = create("bag/whitelist_tms")
|
||||
@JvmField val BAG_BLACKLIST_TMS = create("bag/blacklist_tms")
|
||||
|
||||
//Empty Tag
|
||||
@JvmField val EMPTY = create("empty")
|
||||
|
||||
|
||||
@@ -15,19 +15,13 @@ import com.cobblemon.mod.common.api.berry.GrowthPoint
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryHarvestEvent
|
||||
import com.cobblemon.mod.common.api.mulch.MulchVariant
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.pokedex.scanner.BlockScanData
|
||||
import com.cobblemon.mod.common.pokedex.scanner.Frame
|
||||
import com.cobblemon.mod.common.pokedex.scanner.ScannableBlockEntity
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.ResourceLocationException
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.protocol.Packet
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||
@@ -41,10 +35,11 @@ import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.CropBlock.UPDATE_CLIENTS
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
|
||||
class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.BERRY, pos, state), ScannableBlockEntity {
|
||||
class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.BERRY, pos, state) {
|
||||
lateinit var berryIdentifier: ResourceLocation
|
||||
private val ticksPerMinute = 1200
|
||||
var renderState: RenderState? = null
|
||||
@@ -380,22 +375,6 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
return stageTimer <= 0
|
||||
}
|
||||
|
||||
override fun resolveBlockScan(): BlockScanData {
|
||||
val berryName = berry()?.item()?.let { ItemStack(it).hoverName.copy() }
|
||||
?: berryIdentifier.toString().text()
|
||||
|
||||
val frames = mutableSetOf<Frame>(
|
||||
Frame(1, lang("ui.pokedex.scan.berry.stage", blockState.getValue(BerryBlock.AGE))),
|
||||
Frame(2, berryName)
|
||||
)
|
||||
|
||||
if (mulchVariant != MulchVariant.NONE) {
|
||||
frames.add(Frame(3, Component.translatable("item.cobblemon.${mulchVariant.serializedName}_mulch")))
|
||||
}
|
||||
|
||||
return BlockScanData(frames)
|
||||
}
|
||||
|
||||
//Using a similar approach to ComputerCraft, implement this client side
|
||||
interface RenderState : AutoCloseable {
|
||||
var needsRebuild: Boolean
|
||||
|
||||
@@ -9,10 +9,13 @@
|
||||
package com.cobblemon.mod.common.client
|
||||
|
||||
import com.cobblemon.mod.common.BakingOverride
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.item.bag.CobblemonBagItem
|
||||
import com.cobblemon.mod.common.util.cobblemonModel
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.item.Item
|
||||
|
||||
/**
|
||||
* The purpose of this class is to hold models that we want baked, but aren't associated with
|
||||
@@ -103,9 +106,33 @@ object CobblemonBakingOverrides {
|
||||
cobblemonModel("surprise_mulch", "none")
|
||||
)
|
||||
|
||||
// Bag
|
||||
val bagModels = mutableMapOf<CobblemonBagItem, BakingOverride>()
|
||||
val TRAINER_PACK = registerBagOverride(
|
||||
CobblemonItems.TRAINER_PACK,
|
||||
cobblemonResource("item/bag/trainer_pack"),
|
||||
cobblemonModel("trainer_pack", "none")
|
||||
)
|
||||
val TRAINER_BAG = registerBagOverride(
|
||||
CobblemonItems.TRAINER_BAG,
|
||||
cobblemonResource("item/bag/trainer_bag"),
|
||||
cobblemonModel("trainer_bag", "none")
|
||||
)
|
||||
/*val BAG_PENNY = registerBagOverride(
|
||||
CobblemonItems.TRAINER_PACK,
|
||||
cobblemonResource("item/bag/bag_penny"),
|
||||
cobblemonModel("bag_penny", "penny")
|
||||
)*/
|
||||
|
||||
fun registerOverride(modelLocation: ResourceLocation, modelIdentifier: ModelResourceLocation): BakingOverride {
|
||||
val result = BakingOverride(modelLocation, modelIdentifier)
|
||||
models.add(result)
|
||||
return result
|
||||
}
|
||||
|
||||
fun registerBagOverride(associatedItem: CobblemonBagItem, modelLocation: ResourceLocation, modelIdentifier: ModelResourceLocation): BakingOverride {
|
||||
val result = registerOverride(modelLocation, modelIdentifier)
|
||||
bagModels[associatedItem] = result
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import com.cobblemon.mod.common.client.battle.ClientBattle
|
||||
import com.cobblemon.mod.common.client.gui.PartyOverlay
|
||||
import com.cobblemon.mod.common.client.gui.PartyOverlayDataControl
|
||||
import com.cobblemon.mod.common.client.gui.RideControlsOverlay
|
||||
import com.cobblemon.mod.common.client.gui.bag.CobblemonBagScreen
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotScreen
|
||||
import com.cobblemon.mod.common.client.gui.party.PartyTutorialToasts
|
||||
@@ -46,6 +47,7 @@ import com.cobblemon.mod.common.client.render.generic.GenericBedrockRenderer
|
||||
import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererRegistry
|
||||
import com.cobblemon.mod.common.client.render.item.PokemonItemRenderer
|
||||
import com.cobblemon.mod.common.client.render.layer.PlayerRecallBallRenderer
|
||||
import com.cobblemon.mod.common.client.render.layer.CobblemonBagRenderer
|
||||
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
|
||||
@@ -391,7 +393,7 @@ object CobblemonClient {
|
||||
CobblemonBlocks.X_SP_ATK,
|
||||
CobblemonBlocks.X_SP_DEF,
|
||||
CobblemonBlocks.X_SPEED,
|
||||
CobblemonBlocks.TM_MACHINE,
|
||||
CobblemonBlocks.TM_MACHINE,
|
||||
|
||||
CobblemonBlocks.GALARICA_CUFF,
|
||||
CobblemonBlocks.GALARICA_WREATH,
|
||||
@@ -419,7 +421,7 @@ object CobblemonClient {
|
||||
CobblemonBlocks.MOOMOO_MILK,
|
||||
CobblemonBlocks.MEDICINAL_BREW,
|
||||
CobblemonBlocks.TERRAIN_EXTENDER,
|
||||
|
||||
|
||||
CobblemonBlocks.RED_MINT_LEAF_LITTER,
|
||||
CobblemonBlocks.POTTED_RED_MINT,
|
||||
CobblemonBlocks.BLUE_MINT_LEAF_LITTER,
|
||||
@@ -539,14 +541,17 @@ object CobblemonClient {
|
||||
skinMap?.get(PlayerSkin.Model.WIDE) as LivingEntityRenderer<Player, PlayerModel<Player>>
|
||||
renderer?.addLayer(PokemonOnShoulderRenderer(renderer))
|
||||
renderer?.addLayer(PlayerRecallBallRenderer(renderer))
|
||||
renderer?.addLayer(CobblemonBagRenderer(renderer))
|
||||
renderer = skinMap[PlayerSkin.Model.SLIM] as LivingEntityRenderer<Player, PlayerModel<Player>>?
|
||||
renderer?.addLayer(PokemonOnShoulderRenderer(renderer))
|
||||
renderer?.addLayer(PlayerRecallBallRenderer(renderer))
|
||||
renderer?.addLayer(CobblemonBagRenderer(renderer))
|
||||
}
|
||||
|
||||
private fun registerMenuScreens() {
|
||||
MenuScreens.register(CobblemonMenuType.COOKING_POT, ::CookingPotScreen)
|
||||
MenuScreens.register(CobblemonMenuType.TM_MACHINE, ::TMMachineScreen)
|
||||
MenuScreens.register(CobblemonMenuType.COBBLEMON_BAG, ::CobblemonBagScreen)
|
||||
}
|
||||
|
||||
private fun registerBlockEntityRenderers() {
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.gui.bag
|
||||
|
||||
import com.cobblemon.mod.common.api.bag.BagCategories
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.render.getDepletableRedGreen
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.sounds.SoundManager
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class BagCategoryButton(
|
||||
x: Int,
|
||||
y: Int,
|
||||
val parent: CobblemonBagScreen,
|
||||
val category: Int,
|
||||
val action: OnPress
|
||||
): Button(x, y, WIDTH, HEIGHT, Component.empty(), action, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
companion object {
|
||||
private const val WIDTH = 32
|
||||
private const val HEIGHT = 31
|
||||
|
||||
private val CATEGORY_SLOT = cobblemonResource("textures/gui/bag/bag_tab.png")
|
||||
private val VOLUME_BAR = cobblemonResource("textures/gui/bag/bag_tab_volume_bar.png")
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
|
||||
val orderedCategories = BagCategories.getOrderedCategoriesList()
|
||||
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = CATEGORY_SLOT,
|
||||
x = x, y = y,
|
||||
width = WIDTH, height = HEIGHT,
|
||||
textureHeight = 64,
|
||||
vOffset = if (parent.menu.currentlySelectedCategory == category) 32 else 0
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = if (category == -1) ResourceLocation("minecraft", "textures/item/bundle.png") else orderedCategories[category].icon,
|
||||
x = x + 8, y = y + 9,
|
||||
width = 16, height = 16
|
||||
)
|
||||
|
||||
//The Bar
|
||||
if (category != -1) {
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = VOLUME_BAR,
|
||||
x = x + 8, y = y + 25,
|
||||
width = 16, height = 3
|
||||
)
|
||||
|
||||
val itemRatio = (parent.menu.categoryItemCounts[category].toFloat() / orderedCategories[category].maxCount).coerceIn(0F, 1F)
|
||||
val barWidth = itemRatio * 14
|
||||
val (red, green) = getDepletableRedGreen(1 - itemRatio)
|
||||
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = x + 9, y = y + 26,
|
||||
width = barWidth, height = 1,
|
||||
red = red * 0.8F, green = green * 0.8F, blue = 0.27F
|
||||
)
|
||||
}
|
||||
|
||||
if (isHovered) {
|
||||
context.renderTooltip(Minecraft.getInstance().font, if (category == -1) lang("bag_categories.all_items") else orderedCategories[category].getName(), mouseX, mouseY)
|
||||
}
|
||||
}
|
||||
|
||||
override fun playDownSound(handler: SoundManager) {}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.bag
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.sounds.SoundManager
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
class BagCategoryPageButton(
|
||||
x: Int,
|
||||
y: Int,
|
||||
val parent: CobblemonBagScreen,
|
||||
val leftButton: Boolean,
|
||||
val action: OnPress
|
||||
): Button(x, y, WIDTH, HEIGHT, Component.empty(), action, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
companion object {
|
||||
private const val WIDTH = 12
|
||||
private const val HEIGHT = 10
|
||||
|
||||
private val LEFT_BUTTON = cobblemonResource("textures/gui/bag/button_left.png")
|
||||
private val RIGHT_BUTTON = cobblemonResource("textures/gui/bag/button_right.png")
|
||||
}
|
||||
|
||||
var enabled = true
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = if (leftButton) LEFT_BUTTON else RIGHT_BUTTON,
|
||||
x = x / 0.5F, y = y / 0.5F,
|
||||
width = WIDTH * 2, height = HEIGHT * 2,
|
||||
textureHeight = 40,
|
||||
vOffset = if (enabled) 20 else 0,
|
||||
scale = 0.5f
|
||||
)
|
||||
}
|
||||
|
||||
override fun playDownSound(handler: SoundManager) {}
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
/*
|
||||
* 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.gui.bag
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendToServer
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.bag.BagCategories
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
|
||||
import com.cobblemon.mod.common.client.render.getDepletableRedGreen
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
import com.cobblemon.mod.common.item.bag.CobblemonBagMenu
|
||||
import com.cobblemon.mod.common.item.battle.BagItem
|
||||
import com.cobblemon.mod.common.net.messages.client.bag.UpdateBagMenuDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.bag.UseItemOnPokemonFromBagPacket
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
||||
import juuxel.adorn.util.Colors
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.inventory.ClickType
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
class CobblemonBagScreen(
|
||||
menu: CobblemonBagMenu,
|
||||
playerInventory: Inventory,
|
||||
title: Component
|
||||
): AbstractContainerScreen<CobblemonBagMenu>(
|
||||
menu,
|
||||
playerInventory,
|
||||
Component.translatable("cobblemon.container.bag")
|
||||
) {
|
||||
companion object {
|
||||
private const val BACKGROUND_HEIGHT = 197
|
||||
private const val BACKGROUND_WIDTH = 184
|
||||
|
||||
private val BACKGROUND = cobblemonResource("textures/gui/bag/bag_base.png")
|
||||
private val SCROLL_TRACK = cobblemonResource("textures/gui/bag/scrollbar_track.png")
|
||||
private val SCROLL_SLIDE = cobblemonResource("textures/gui/bag/scrollbar_slide.png")
|
||||
private val PARTY_SLOT_BASE = cobblemonResource("textures/gui/bag/party_slot_base.png")
|
||||
private val PARTY_SLOT_COLLAPSED = cobblemonResource("textures/gui/bag/party_slot_collapsed.png")
|
||||
private val PARTY_SLOT = cobblemonResource("textures/gui/bag/party_slot.png")
|
||||
private val PARTY_SLOT_FAINTED = cobblemonResource("textures/gui/bag/party_slot_fainted.png")
|
||||
private val GENDER_ICON_MALE = cobblemonResource("textures/gui/party/party_gender_male.png")
|
||||
private val GENDER_ICON_FEMALE = cobblemonResource("textures/gui/party/party_gender_female.png")
|
||||
}
|
||||
|
||||
val categoriesWidgets = mutableListOf<BagCategoryButton>()
|
||||
lateinit var allItemsCategoryWidget: BagCategoryButton
|
||||
lateinit var leftCategoryPageButton: BagCategoryPageButton
|
||||
lateinit var rightCategoryPageButton: BagCategoryPageButton
|
||||
var currentCategoryPage = 0
|
||||
var lastCategory = menu.currentlySelectedCategory
|
||||
var lastScroll = menu.scroll
|
||||
var scrolling = false
|
||||
|
||||
init {
|
||||
this.imageWidth = BACKGROUND_WIDTH + 69
|
||||
this.imageHeight = BACKGROUND_HEIGHT + 27
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
this.leftPos -= 34
|
||||
|
||||
for (i in 0..3) {
|
||||
if (i < BagCategories.getOrderedCategoriesList().size) {
|
||||
categoriesWidgets.add(
|
||||
BagCategoryButton(leftPos + 76 + i * 33, topPos, this, i) {
|
||||
if (menu.currentlySelectedCategory != i) {
|
||||
menu.changeCategory(i)
|
||||
playSound(CobblemonSounds.ITEM_BAG_CLICK)
|
||||
}
|
||||
}.also { addRenderableWidget(it) }
|
||||
)
|
||||
} else break
|
||||
}
|
||||
|
||||
allItemsCategoryWidget = BagCategoryButton(leftPos + 214, topPos, this, -1) {
|
||||
if (menu.currentlySelectedCategory != -1) {
|
||||
menu.changeCategory(-1)
|
||||
playSound(CobblemonSounds.ITEM_BAG_CLICK)
|
||||
}
|
||||
}.also { addRenderableWidget(it) }
|
||||
|
||||
leftCategoryPageButton = BagCategoryPageButton(leftPos + 218, topPos + 32, this, true) {
|
||||
if (leftCategoryPageButton.enabled) {
|
||||
cycleCategoryPage(false)
|
||||
playSound(CobblemonSounds.ITEM_BAG_CLICK)
|
||||
}
|
||||
}.also { addRenderableWidget(it) }
|
||||
rightCategoryPageButton = BagCategoryPageButton(leftPos + 229, topPos + 32, this, false) {
|
||||
if (rightCategoryPageButton.enabled) {
|
||||
cycleCategoryPage(true)
|
||||
playSound(CobblemonSounds.ITEM_BAG_CLICK)
|
||||
}
|
||||
}.also { addRenderableWidget(it) }
|
||||
|
||||
playSound(CobblemonSounds.ITEM_BAG_OPEN)
|
||||
}
|
||||
|
||||
override fun renderBg(context: GuiGraphics, partialTick: Float, mouseX: Int, mouseY: Int) {}
|
||||
override fun renderBackground(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {}
|
||||
|
||||
override fun renderLabels(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int) {}
|
||||
|
||||
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
|
||||
renderTransparentBackground(context)
|
||||
|
||||
val matrices = context.pose()
|
||||
|
||||
// Background
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = BACKGROUND,
|
||||
x = leftPos + 69, y = topPos + 27,
|
||||
width = BACKGROUND_WIDTH, height = BACKGROUND_HEIGHT
|
||||
)
|
||||
|
||||
//Scroll
|
||||
val scrollYOffset = if (menu.maxScroll == 0) {
|
||||
if (menu.scroll == 0) 0 else 53
|
||||
} else ((min(menu.scroll, menu.maxScroll).toFloat() / menu.maxScroll) * 53).toInt()
|
||||
val trackHeight = 71 - scrollYOffset - 13
|
||||
|
||||
if (trackHeight > 0) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = SCROLL_TRACK,
|
||||
x = leftPos + 230, y = topPos + 49 + (71 - trackHeight),
|
||||
width = 9, height = trackHeight,
|
||||
textureHeight = 71,
|
||||
vOffset = 71 - trackHeight
|
||||
)
|
||||
}
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = SCROLL_SLIDE,
|
||||
x = leftPos + 230, y = topPos + 49 + scrollYOffset,
|
||||
width = 9, height = 17
|
||||
)
|
||||
|
||||
// Pokemon
|
||||
val party = CobblemonClient.storage.party
|
||||
for (i in 0..5) {
|
||||
val pokemon = party.get(i)
|
||||
val slotYPosition = topPos + i * 33 + 27
|
||||
|
||||
if (pokemon != null) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = PARTY_SLOT_BASE,
|
||||
x = leftPos, y = slotYPosition,
|
||||
width = 67, height = 31
|
||||
)
|
||||
|
||||
val slotWidth = 32
|
||||
val slotLeft = leftPos + 11
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = if (pokemon.isFainted()) PARTY_SLOT_FAINTED else PARTY_SLOT,
|
||||
x = slotLeft, y = slotYPosition,
|
||||
width = slotWidth, height = 26,
|
||||
textureHeight = 52,
|
||||
vOffset = if (!menu.carried.isEmpty && menu.carried.item is BagItem && partySlotHovered(i, mouseX, mouseY)) 26 else 0
|
||||
)
|
||||
|
||||
val expForThisLevel = pokemon.experience - pokemon.experienceGroup.getExperience(pokemon.level)
|
||||
val expToNextLevel = pokemon.experienceGroup.getExperience(pokemon.level + 1) - pokemon.experienceGroup.getExperience(pokemon.level)
|
||||
val expRatio = expForThisLevel.toFloat() / expToNextLevel
|
||||
|
||||
val expBarHeight = expRatio * 20
|
||||
|
||||
if (expBarHeight > 0) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = leftPos + 6, y = slotYPosition + 24 - expBarHeight,
|
||||
width = 1, height = expBarHeight,
|
||||
red = 0.2, green = 0.65, blue = 0.84
|
||||
)
|
||||
}
|
||||
|
||||
val hpRatio = pokemon.currentHealth.toFloat() / pokemon.maxHealth
|
||||
val hpBarHeight = hpRatio * 20
|
||||
val (red, green) = getDepletableRedGreen(hpRatio)
|
||||
|
||||
if (hpBarHeight > 0) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = leftPos + 8, y = slotYPosition + 24 - hpBarHeight,
|
||||
width = 2, height = hpBarHeight,
|
||||
red = red * 0.8F, green = green * 0.8F, blue = 0.27F
|
||||
)
|
||||
}
|
||||
|
||||
// Ball Icon
|
||||
val ballIcon = cobblemonResource("textures/gui/ball/" + pokemon.caughtBall.name.path + ".png")
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = ballIcon,
|
||||
x = (leftPos + 3.5F) / 0.5F, y = (slotYPosition + 23) / 0.5F,
|
||||
width = 18, height = 22,
|
||||
textureHeight = 44,
|
||||
scale = 0.5F
|
||||
)
|
||||
|
||||
//Gender
|
||||
if (pokemon.gender != Gender.GENDERLESS) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = if (pokemon.gender == Gender.MALE) GENDER_ICON_MALE else GENDER_ICON_FEMALE,
|
||||
x = (leftPos + 62) / 0.5F, y = (slotYPosition + 26) / 0.5F,
|
||||
width = 5, height = 7,
|
||||
scale = 0.5F
|
||||
)
|
||||
}
|
||||
|
||||
//Status
|
||||
val status = pokemon.status?.status
|
||||
if (!pokemon.isFainted() && status != null) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = cobblemonResource("textures/gui/bag/status_${status.showdownName}.png"),
|
||||
x = leftPos + 1, y = slotYPosition + 9,
|
||||
width = 4, height = 14
|
||||
)
|
||||
}
|
||||
|
||||
matrices.pushPose()
|
||||
matrices.translate(slotLeft + ((slotWidth - 8) / 2.0), slotYPosition.toDouble() - 6, 0.0)
|
||||
matrices.scale(3.5F, 3.5F, 1F)
|
||||
|
||||
val state = FloatingState()
|
||||
state.currentAspects = pokemon.aspects
|
||||
drawProfilePokemon(
|
||||
species = pokemon.species.resourceIdentifier,
|
||||
matrixStack = matrices,
|
||||
rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(13F, 35F, 0F)),
|
||||
state = state,
|
||||
scale = 4.5F,
|
||||
partialTicks = 0F
|
||||
)
|
||||
matrices.popPose()
|
||||
|
||||
matrices.pushPose()
|
||||
matrices.translate(0.0, 0.0, 100.0)
|
||||
// Level and Name and Gender
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = pokemon.getDisplayName(),
|
||||
x = leftPos + 14, y = slotYPosition + 26,
|
||||
scale = 0.5F
|
||||
)
|
||||
|
||||
drawScaledTextJustifiedRight(
|
||||
context = context,
|
||||
text = lang("ui.lv"),
|
||||
x = leftPos + 40, y = slotYPosition + 14,
|
||||
shadow = true,
|
||||
scale = 0.5F
|
||||
)
|
||||
|
||||
drawScaledTextJustifiedRight(
|
||||
context = context,
|
||||
text = pokemon.level.toString().text(),
|
||||
x = leftPos + 40, y = slotYPosition + 19,
|
||||
shadow = true,
|
||||
scale = 0.5F
|
||||
)
|
||||
matrices.popPose()
|
||||
} else {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = PARTY_SLOT_COLLAPSED,
|
||||
x = leftPos + 64, y = slotYPosition,
|
||||
width = 5, height = 31
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//Titles
|
||||
context.drawString(font, title, leftPos + 80, topPos + 33, Colors.WHITE, false)
|
||||
context.drawString(font, playerInventoryTitle, leftPos + 80, topPos + 129, 4210752, false)
|
||||
|
||||
super.render(context, mouseX, mouseY, partialTick)
|
||||
renderTooltip(context, mouseX, mouseY)
|
||||
}
|
||||
|
||||
fun partySlotHovered(index: Int, mouseX: Int, mouseY: Int): Boolean {
|
||||
val left = leftPos + 11
|
||||
val top = topPos + index * 33 + 27
|
||||
return mouseX in left..(left + 32) && mouseY in top..(top + 26)
|
||||
}
|
||||
|
||||
fun cycleCategoryPage(next: Boolean) {
|
||||
val orderedCategories = BagCategories.getOrderedCategoriesList()
|
||||
currentCategoryPage = (currentCategoryPage + if (next) 1 else -1).coerceIn(0, (orderedCategories.size - 1) / 4)
|
||||
categoriesWidgets.forEach {
|
||||
removeWidget(it)
|
||||
}
|
||||
categoriesWidgets.clear()
|
||||
for (i in 0..3) {
|
||||
val adjustedIndex = i + 4 * currentCategoryPage
|
||||
if (adjustedIndex < orderedCategories.size) {
|
||||
categoriesWidgets.add(
|
||||
BagCategoryButton(leftPos + 76 + i * 33, topPos, this, adjustedIndex) {
|
||||
playSound(CobblemonSounds.ITEM_BAG_CLICK)
|
||||
menu.changeCategory(adjustedIndex)
|
||||
}.also { addRenderableWidget(it) }
|
||||
)
|
||||
} else break
|
||||
}
|
||||
leftCategoryPageButton.enabled = currentCategoryPage > 0
|
||||
rightCategoryPageButton.enabled = currentCategoryPage < (orderedCategories.size - 1) / 4
|
||||
}
|
||||
|
||||
fun updateScrollValue(mouseY: Double) {
|
||||
val adjustedMouseY = mouseY - topPos - 49.0
|
||||
if (adjustedMouseY < 0) {
|
||||
menu.scroll = 0
|
||||
} else if (adjustedMouseY > 71) {
|
||||
menu.scroll = menu.maxScroll
|
||||
} else {
|
||||
menu.scroll = ((adjustedMouseY / 71.0) * (menu.maxScroll)).roundToInt()
|
||||
}
|
||||
menu.setupSlotsBasedOnContent()
|
||||
}
|
||||
|
||||
override fun slotClicked(slot: Slot?, slotId: Int, mouseButton: Int, type: ClickType) {
|
||||
if (slotId in CobblemonBagMenu.BAG_SLOTS && (lastCategory != menu.currentlySelectedCategory || lastScroll != menu.scroll)) {
|
||||
lastCategory = menu.currentlySelectedCategory
|
||||
lastScroll = menu.scroll
|
||||
sendToServer(UpdateBagMenuDataPacket(menu.currentlySelectedCategory, menu.scroll)) // Synchronize this data
|
||||
}
|
||||
super.slotClicked(slot, slotId, mouseButton, type)
|
||||
}
|
||||
|
||||
override fun mouseScrolled(mouseX: Double, mouseY: Double, scrollX: Double, scrollY: Double): Boolean {
|
||||
if (scrollY < 0 && menu.scroll < menu.maxScroll) {
|
||||
menu.scroll += 1
|
||||
} else if (scrollY > 0 && menu.scroll > 0) {
|
||||
menu.scroll -= 1
|
||||
}
|
||||
menu.setupSlotsBasedOnContent()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (mouseX in (leftPos + 229.0)..(leftPos + 240.0) && mouseY in (topPos + 49.0)..(topPos + 120.0)) {
|
||||
scrolling = true
|
||||
updateScrollValue(mouseY)
|
||||
return true
|
||||
}
|
||||
val party = CobblemonClient.storage.party
|
||||
if (!menu.carried.isEmpty && menu.carried.item is BagItem) {
|
||||
for (i in 0..5) {
|
||||
if (party.get(i) != null && partySlotHovered(i, mouseX.toInt(), mouseY.toInt())) {
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
sendToServer(UseItemOnPokemonFromBagPacket(i))
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
scrolling = false
|
||||
return super.mouseReleased(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, dragX: Double, dragY: Double): Boolean {
|
||||
if (scrolling) {
|
||||
updateScrollValue(mouseY)
|
||||
return true
|
||||
}
|
||||
return super.mouseDragged(mouseX, mouseY, button, dragX, dragY)
|
||||
}
|
||||
|
||||
fun playSound(soundEvent: SoundEvent) {
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
|
||||
}
|
||||
|
||||
override fun onClose() {
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.ITEM_BAG_CLOSE, 1.0F))
|
||||
super.onClose()
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,9 @@ import org.joml.Vector3f
|
||||
|
||||
open class PartySlot(
|
||||
x: Int, y: Int,
|
||||
private val pokemon: TradeStartedPacket.TradeablePokemon?,
|
||||
val pokemon: TradeStartedPacket.TradeablePokemon?,
|
||||
private val parent: TradeGUI,
|
||||
private val isOpposing: Boolean = false,
|
||||
val isOpposing: Boolean = false,
|
||||
onPress: OnPress
|
||||
) : Button(x, y, SIZE, SIZE, Component.literal("PartySlot"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
val state = FloatingState()
|
||||
@@ -42,6 +42,8 @@ open class PartySlot(
|
||||
private val genderIconFemale = cobblemonResource("textures/gui/pc/gender_icon_female.png")
|
||||
private val selectPointerResource = cobblemonResource("textures/gui/pc/pc_pointer.png")
|
||||
private val untradeableResource = cobblemonResource("textures/gui/trade/trade_slot_icon_locked.png")
|
||||
private val tradePartySlotSelectedResource = cobblemonResource("textures/gui/trade/trade_party_slot_selected.png")
|
||||
private val tradePartySlotSelectedOpposingResource = cobblemonResource("textures/gui/trade/trade_party_slot_selected_opposing.png")
|
||||
}
|
||||
|
||||
override fun playDownSound(soundManager: SoundManager) {
|
||||
@@ -60,6 +62,17 @@ open class PartySlot(
|
||||
)
|
||||
}
|
||||
|
||||
if (hasSelected()) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = if (isOpposing) tradePartySlotSelectedOpposingResource else tradePartySlotSelectedResource,
|
||||
x = x - 2,
|
||||
y = y - 2,
|
||||
width = 29,
|
||||
height = 29
|
||||
)
|
||||
}
|
||||
|
||||
if (pokemon != null) {
|
||||
// context.enableScissor(x - 2, y + 2, x + SIZE + 4, y + SIZE + 4)
|
||||
// Render Pokémon
|
||||
@@ -118,7 +131,16 @@ open class PartySlot(
|
||||
}
|
||||
|
||||
matrices.popPose()
|
||||
if (hasSelected()) {
|
||||
|
||||
val isViewed = if (isOpposing) {
|
||||
parent.opposingOfferedPokemon?.uuid == pokemon.pokemonId
|
||||
} else {
|
||||
parent.offeredPokemon?.uuid == pokemon.pokemonId
|
||||
}
|
||||
|
||||
if (isViewed && pokemon.tradeable) {
|
||||
matrices.pushPose()
|
||||
matrices.translate(0.0, 0.0, 200.0)
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = selectPointerResource,
|
||||
@@ -128,6 +150,7 @@ open class PartySlot(
|
||||
height = 8,
|
||||
scale = TradeGUI.SCALE
|
||||
)
|
||||
matrices.popPose()
|
||||
}
|
||||
|
||||
// Held Item
|
||||
@@ -145,8 +168,9 @@ open class PartySlot(
|
||||
}
|
||||
|
||||
open fun hasSelected(): Boolean {
|
||||
val offeredPokemon = if (isOpposing) parent.trade.oppositeOffer.get() else parent.trade.myOffer.get()
|
||||
return pokemon?.pokemonId == offeredPokemon?.uuid && pokemon != null
|
||||
if (pokemon == null) return false
|
||||
val offeredPokemons = if (isOpposing) parent.trade.oppositeOffer.get() else parent.trade.myOffer.get()
|
||||
return offeredPokemons.any { it.uuid == pokemon.pokemonId }
|
||||
}
|
||||
|
||||
fun isHovered(mouseX: Int, mouseY: Int) = pokemon?.tradeable != false && mouseX.toFloat() in (x.toFloat()..(x.toFloat() + SIZE)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + SIZE))
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.cobblemon.mod.common.client.gui.summary.widgets.common.reformatNature
|
||||
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
|
||||
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.pokemon.Gender
|
||||
@@ -98,15 +99,23 @@ class TradeGUI(
|
||||
private val tradeAnimationArrowsResource = cobblemonResource("textures/gui/trade/trade_animation_arrows.png")
|
||||
}
|
||||
|
||||
private var offeredPokemonModel: ModelWidget? = null
|
||||
private var opposingOfferedPokemonModel: ModelWidget? = null
|
||||
private var offeredPokemonModels: MutableList<ModelWidget> = mutableListOf()
|
||||
private var opposingOfferedPokemonModels: MutableList<ModelWidget> = mutableListOf()
|
||||
|
||||
private lateinit var offeredPokemonMarkings: MarkingsWidget
|
||||
private lateinit var opposingOfferedPokemonMarkings: MarkingsWidget
|
||||
|
||||
var selectedPartyIndices = mutableListOf<Int>()
|
||||
var offeredPokemons: MutableList<Pokemon> = mutableListOf()
|
||||
var opposingPokemons: MutableList<Pokemon> = mutableListOf()
|
||||
|
||||
// For backwards compatibility where single is referenced in rendering text info
|
||||
var offeredPokemon: Pokemon? = null
|
||||
var opposingOfferedPokemon: Pokemon? = null
|
||||
|
||||
var myGridMode = true
|
||||
var opposingGridMode = true
|
||||
|
||||
var ticksElapsed = 0
|
||||
var protectiveTicks = 0
|
||||
var selectPointerOffsetY = 0
|
||||
@@ -125,30 +134,67 @@ class TradeGUI(
|
||||
}
|
||||
|
||||
trade.completedEmitter.subscribe {
|
||||
val (pokemonId1, pokemonId2) = it
|
||||
val myTradedPokemon = party.find { it?.pokemonId == pokemonId1 }
|
||||
val theirTradedPokemon = traderParty.find { it?.pokemonId == pokemonId2 }
|
||||
if (myTradedPokemon == null || theirTradedPokemon == null) {
|
||||
val (pokemonIds1, pokemonIds2) = it
|
||||
val myTradedPokemons = pokemonIds1.mapNotNull { id -> party.find { it?.pokemonId == id } }
|
||||
val theirTradedPokemons = pokemonIds2.mapNotNull { id -> traderParty.find { it?.pokemonId == id } }
|
||||
if (myTradedPokemons.isEmpty() || theirTradedPokemons.isEmpty()) {
|
||||
CobblemonNetwork.sendToServer(CancelTradePacket())
|
||||
return@subscribe onClose()
|
||||
}
|
||||
val tradedSlot = party.indexOf(myTradedPokemon)
|
||||
val opposingTradedSlot = traderParty.indexOf(theirTradedPokemon)
|
||||
party[tradedSlot] = theirTradedPokemon
|
||||
traderParty[opposingTradedSlot] = myTradedPokemon
|
||||
|
||||
myTradedPokemons.forEach { pk ->
|
||||
val idx = party.indexOf(pk)
|
||||
if (idx != -1) party[idx] = null
|
||||
}
|
||||
theirTradedPokemons.forEach { pk ->
|
||||
val idx = traderParty.indexOf(pk)
|
||||
if (idx != -1) traderParty[idx] = null
|
||||
}
|
||||
|
||||
val newSelectedIndices = mutableListOf<Int>()
|
||||
theirTradedPokemons.forEach { pk ->
|
||||
val emptyIdx = party.indexOf(null)
|
||||
if (emptyIdx != -1) {
|
||||
party[emptyIdx] = pk
|
||||
}
|
||||
}
|
||||
myTradedPokemons.forEach { pk ->
|
||||
val emptyIdx = traderParty.indexOf(null)
|
||||
if (emptyIdx != -1) traderParty[emptyIdx] = pk
|
||||
}
|
||||
|
||||
val compactUI = { list: MutableList<TradeStartedPacket.TradeablePokemon?> ->
|
||||
val nonNulls = list.filterNotNull()
|
||||
list.clear()
|
||||
list.addAll(nonNulls)
|
||||
while (list.size < 6) list.add(null)
|
||||
}
|
||||
compactUI(party)
|
||||
compactUI(traderParty)
|
||||
|
||||
theirTradedPokemons.forEach { pk ->
|
||||
val idx = party.indexOf(pk)
|
||||
if (idx != -1) newSelectedIndices.add(idx)
|
||||
}
|
||||
|
||||
trade.oppositeAcceptedMyOffer.set(false)
|
||||
CobblemonNetwork.sendToServer(UpdateTradeOfferPacket(Pair(theirTradedPokemon.pokemonId, PartyPosition(tradedSlot))))
|
||||
selectedPartyIndices.clear()
|
||||
selectedPartyIndices.addAll(newSelectedIndices)
|
||||
|
||||
val newOffers = selectedPartyIndices.mapNotNull { idx ->
|
||||
party[idx]?.let { it.pokemonId to PartyPosition(idx) }
|
||||
}
|
||||
CobblemonNetwork.sendToServer(UpdateTradeOfferPacket(newOffers))
|
||||
|
||||
rebuildWidgets()
|
||||
}
|
||||
|
||||
trade.oppositeOffer.subscribe { newOffer: Pokemon? ->
|
||||
setOfferedPokemon(pokemon = newOffer, isOpposing = true)
|
||||
trade.oppositeOffer.subscribe { newOffer: List<Pokemon> ->
|
||||
setOfferedPokemon(pokemonList = newOffer, isOpposing = true)
|
||||
}
|
||||
|
||||
trade.myOffer.subscribe { myOffer: Pokemon? ->
|
||||
setOfferedPokemon(pokemon = myOffer)
|
||||
trade.myOffer.subscribe { myOffer: List<Pokemon> ->
|
||||
setOfferedPokemon(pokemonList = myOffer)
|
||||
}
|
||||
|
||||
trade.oppositeAcceptedMyOffer.subscribe {
|
||||
@@ -216,9 +262,16 @@ class TradeGUI(
|
||||
pokemon = pokemon,
|
||||
parent = this,
|
||||
onPress = {
|
||||
if (!trade.acceptedOppositeOffer && !tradeProcessing) {
|
||||
val pk = if (offeredPokemon?.uuid == pokemon?.pokemonId) null else pokemon
|
||||
CobblemonNetwork.sendToServer(UpdateTradeOfferPacket(pk?.let { it.pokemonId to PartyPosition(partyIndex) }))
|
||||
if (!trade.acceptedOppositeOffer && !tradeProcessing && pokemon != null) {
|
||||
if (selectedPartyIndices.contains(partyIndex)) {
|
||||
selectedPartyIndices.remove(partyIndex)
|
||||
} else if (selectedPartyIndices.size < 6) {
|
||||
selectedPartyIndices.add(partyIndex)
|
||||
}
|
||||
val newOffers = selectedPartyIndices.mapNotNull { idx ->
|
||||
party[idx]?.let { it.pokemonId to PartyPosition(idx) }
|
||||
}
|
||||
CobblemonNetwork.sendToServer(UpdateTradeOfferPacket(newOffers))
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
}
|
||||
}
|
||||
@@ -250,8 +303,8 @@ class TradeGUI(
|
||||
).also { widget -> addRenderableWidget(widget) }
|
||||
}
|
||||
|
||||
setOfferedPokemon(pokemon = offeredPokemon, isOpposing = false)
|
||||
setOfferedPokemon(pokemon = opposingOfferedPokemon, isOpposing = true)
|
||||
setOfferedPokemon(pokemonList = offeredPokemons.toList(), isOpposing = false)
|
||||
setOfferedPokemon(pokemonList = opposingPokemons.toList(), isOpposing = true)
|
||||
|
||||
offeredPokemonMarkings = MarkingsWidget(x + 74.5, y + 21, offeredPokemon, false)
|
||||
opposingOfferedPokemonMarkings = MarkingsWidget(x + 177.5, y + 21, opposingOfferedPokemon, false)
|
||||
@@ -284,7 +337,7 @@ class TradeGUI(
|
||||
if (tradeAnimationProgress > REVERSE_ANIMATION_TICK)
|
||||
(REVERSE_ANIMATION_TICK + TRADE_ANIMATION_FRAMES - tradeAnimationProgress)
|
||||
else (tradeAnimationProgress - MODEL_SCALE_BUFFER_FRAMES)
|
||||
) * TRADE_ANIMATION_HEIGHT
|
||||
) * TRADE_ANIMATION_HEIGHT
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = tradeAnimationResource,
|
||||
@@ -313,20 +366,56 @@ class TradeGUI(
|
||||
// Render Model Portraits
|
||||
var scale = PORTRAIT_SCALE
|
||||
var scaleOffsetY = PORTRAIT_MODEL_OFFSET_Y
|
||||
var isShrinking = false
|
||||
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
|
||||
isShrinking = true
|
||||
}
|
||||
val shrinkPercentage = if (isShrinking) 1.0F - (scale / PORTRAIT_SCALE) else 0.0F
|
||||
val myCenterXDest = x + 70 + (PORTRAIT_SIZE / 2.0)
|
||||
val myCenterYDest = y + 30 + (PORTRAIT_SIZE / 2.0)
|
||||
val opCenterXDest = x + 145 + (PORTRAIT_SIZE / 2.0)
|
||||
val opCenterYDest = y + 30 + (PORTRAIT_SIZE / 2.0)
|
||||
|
||||
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)
|
||||
offeredPokemonModels.forEachIndexed { i, model ->
|
||||
val posAndScale = getModelPositionAndScale(i, offeredPokemons.size, x + 70, y + 30)
|
||||
val originalScale = posAndScale.second
|
||||
model.baseScale = if (isShrinking) (originalScale / PORTRAIT_SCALE) * scale else originalScale
|
||||
model.offsetY = if (isShrinking) scaleOffsetY else PORTRAIT_MODEL_OFFSET_Y
|
||||
|
||||
val startCenterX = posAndScale.first.first + (PORTRAIT_SIZE / 2.0)
|
||||
val startCenterY = posAndScale.first.second + (PORTRAIT_SIZE / 2.0)
|
||||
|
||||
context.pose().pushPose()
|
||||
context.pose().translate((myCenterXDest - startCenterX) * shrinkPercentage, (myCenterYDest - startCenterY) * shrinkPercentage, 0.0)
|
||||
|
||||
model.render(context, mouseX, mouseY, delta)
|
||||
|
||||
context.pose().popPose()
|
||||
}
|
||||
|
||||
opposingOfferedPokemonModels.forEachIndexed { i, model ->
|
||||
val posAndScale = getModelPositionAndScale(i, opposingPokemons.size, x + 145, y + 30)
|
||||
val originalScale = posAndScale.second
|
||||
model.baseScale = if (isShrinking) (originalScale / PORTRAIT_SCALE) * scale else originalScale
|
||||
model.offsetY = if (isShrinking) scaleOffsetY else PORTRAIT_MODEL_OFFSET_Y
|
||||
|
||||
val startCenterX = posAndScale.first.first + (PORTRAIT_SIZE / 2.0)
|
||||
val startCenterY = posAndScale.first.second + (PORTRAIT_SIZE / 2.0)
|
||||
|
||||
context.pose().pushPose()
|
||||
context.pose().translate((opCenterXDest - startCenterX) * shrinkPercentage, (opCenterYDest - startCenterY) * shrinkPercentage, 0.0)
|
||||
|
||||
model.render(context, mouseX, mouseY, delta)
|
||||
|
||||
context.pose().popPose()
|
||||
}
|
||||
|
||||
context.disableScissor()
|
||||
}
|
||||
|
||||
@@ -341,8 +430,46 @@ class TradeGUI(
|
||||
|
||||
renderInfoLabels(context, x, y)
|
||||
|
||||
renderPokemonInfo(offeredPokemon, false, context, x, y, mouseX, mouseY)
|
||||
renderPokemonInfo(opposingOfferedPokemon, true, context, x, y, mouseX, mouseY)
|
||||
children().filterIsInstance<PartySlot>().forEach { slot ->
|
||||
if (slot.isHovered(mouseX, mouseY)) {
|
||||
val hoveredId = slot.pokemon?.pokemonId
|
||||
if (slot.isOpposing && slot.hasSelected()) {
|
||||
opposingPokemons.find { it.uuid == hoveredId }?.let { opposingOfferedPokemon = it }
|
||||
} else if (!slot.isOpposing) {
|
||||
val matchingModel = offeredPokemons.find { it.uuid == hoveredId }
|
||||
if (matchingModel != null) {
|
||||
offeredPokemon = matchingModel
|
||||
} else {
|
||||
val realPokemon = com.cobblemon.mod.common.client.CobblemonClient.storage.party.find { it?.uuid == hoveredId }
|
||||
if (realPokemon != null) {
|
||||
offeredPokemon = realPokemon
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val currentOffered = offeredPokemon
|
||||
if (currentOffered != null) {
|
||||
renderPokemonInfo(currentOffered, false, context, x, y, mouseX, mouseY)
|
||||
if (::offeredPokemonMarkings.isInitialized) {
|
||||
offeredPokemonMarkings.visible = true
|
||||
if (offeredPokemonMarkings.pokemon != currentOffered) offeredPokemonMarkings.setActivePokemon(currentOffered)
|
||||
}
|
||||
} else {
|
||||
if (::offeredPokemonMarkings.isInitialized) offeredPokemonMarkings.visible = false
|
||||
}
|
||||
|
||||
val currentOpposing = opposingOfferedPokemon
|
||||
if (currentOpposing != null) {
|
||||
renderPokemonInfo(currentOpposing, true, context, x, y, mouseX, mouseY)
|
||||
if (::opposingOfferedPokemonMarkings.isInitialized) {
|
||||
opposingOfferedPokemonMarkings.visible = true
|
||||
if (opposingOfferedPokemonMarkings.pokemon != currentOpposing) opposingOfferedPokemonMarkings.setActivePokemon(currentOpposing)
|
||||
}
|
||||
} else {
|
||||
if (::opposingOfferedPokemonMarkings.isInitialized) opposingOfferedPokemonMarkings.visible = false
|
||||
}
|
||||
|
||||
if (trade.acceptedOppositeOffer) {
|
||||
blitk(
|
||||
@@ -431,19 +558,25 @@ class TradeGUI(
|
||||
|
||||
super.render(context, mouseX, mouseY, delta)
|
||||
|
||||
// Item Tooltip
|
||||
if (offeredPokemon != null && !offeredPokemon!!.heldItemNoCopy().isEmpty) {
|
||||
val itemX = x + 50
|
||||
val itemY = y + 125
|
||||
val itemHovered = mouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && mouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
|
||||
if (itemHovered) context.renderTooltip(Minecraft.getInstance().font, offeredPokemon!!.heldItemNoCopy(), mouseX, mouseY)
|
||||
val ttOffered = offeredPokemon
|
||||
if (ttOffered != null) {
|
||||
// Item Tooltip
|
||||
if (!ttOffered.heldItemNoCopy().isEmpty) {
|
||||
val itemX = x + 50
|
||||
val itemY = y + 125
|
||||
val itemHovered = mouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && mouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
|
||||
if (itemHovered) context.renderTooltip(Minecraft.getInstance().font, ttOffered.heldItemNoCopy(), mouseX, mouseY)
|
||||
}
|
||||
}
|
||||
|
||||
if (opposingOfferedPokemon != null && !opposingOfferedPokemon!!.heldItemNoCopy().isEmpty) {
|
||||
val itemX = x + 227
|
||||
val itemY = y + 125
|
||||
val itemHovered = mouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && mouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
|
||||
if (itemHovered) context.renderTooltip(Minecraft.getInstance().font, opposingOfferedPokemon!!.heldItemNoCopy(), mouseX, mouseY)
|
||||
val ttOpposing = opposingOfferedPokemon
|
||||
if (ttOpposing != null) {
|
||||
if (!ttOpposing.heldItemNoCopy().isEmpty) {
|
||||
val itemX = x + 227
|
||||
val itemY = y + 125
|
||||
val itemHovered = mouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && mouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
|
||||
if (itemHovered) context.renderTooltip(Minecraft.getInstance().font, ttOpposing.heldItemNoCopy(), mouseX, mouseY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,11 +613,13 @@ class TradeGUI(
|
||||
}
|
||||
|
||||
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"))
|
||||
offeredPokemonModels.forEach {
|
||||
it.state.activeAnimations.clear()
|
||||
it.state.addFirstAnimation(setOf("cry"))
|
||||
}
|
||||
opposingOfferedPokemonModels.forEach {
|
||||
it.state.activeAnimations.clear()
|
||||
it.state.addFirstAnimation(setOf("cry"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,41 +650,140 @@ class TradeGUI(
|
||||
|
||||
override fun renderMenuBackground(context: GuiGraphics) {}
|
||||
|
||||
private fun setOfferedPokemon(pokemon: Pokemon?, isOpposing: Boolean = false) {
|
||||
private fun setOfferedPokemon(pokemonList: List<Pokemon>, isOpposing: Boolean = false) {
|
||||
protectiveTicks = 20
|
||||
if (isOpposing) {
|
||||
val wasEmpty = opposingPokemons.isEmpty()
|
||||
val currentViewedUuid = if (opposingGridMode) null else opposingOfferedPokemon?.uuid
|
||||
opposingPokemons.clear()
|
||||
opposingPokemons.addAll(pokemonList)
|
||||
|
||||
val matchingPokemon = opposingPokemons.find { it.uuid == currentViewedUuid }
|
||||
|
||||
if (matchingPokemon != null) {
|
||||
opposingOfferedPokemon = matchingPokemon
|
||||
if (::opposingOfferedPokemonMarkings.isInitialized) opposingOfferedPokemonMarkings.setActivePokemon(opposingOfferedPokemon)
|
||||
} else {
|
||||
opposingOfferedPokemon = opposingPokemons.lastOrNull()
|
||||
if (::opposingOfferedPokemonMarkings.isInitialized) opposingOfferedPokemonMarkings.setActivePokemon(opposingOfferedPokemon)
|
||||
if (wasEmpty && opposingOfferedPokemon != null) {
|
||||
opposingGridMode = false
|
||||
} else if (!opposingGridMode) {
|
||||
opposingGridMode = true
|
||||
}
|
||||
}
|
||||
trade.acceptedOppositeOffer = false
|
||||
} else {
|
||||
val wasEmpty = offeredPokemons.isEmpty()
|
||||
val currentViewedUuid = if (myGridMode) null else offeredPokemon?.uuid
|
||||
offeredPokemons.clear()
|
||||
offeredPokemons.addAll(pokemonList)
|
||||
|
||||
val matchingPokemon = offeredPokemons.find { it.uuid == currentViewedUuid }
|
||||
|
||||
if (matchingPokemon != null) {
|
||||
offeredPokemon = matchingPokemon
|
||||
if (::offeredPokemonMarkings.isInitialized) offeredPokemonMarkings.setActivePokemon(offeredPokemon)
|
||||
} else {
|
||||
offeredPokemon = offeredPokemons.lastOrNull()
|
||||
if (::offeredPokemonMarkings.isInitialized) offeredPokemonMarkings.setActivePokemon(offeredPokemon)
|
||||
if (wasEmpty && offeredPokemon != null) {
|
||||
myGridMode = false
|
||||
} else if (!myGridMode) {
|
||||
myGridMode = true
|
||||
}
|
||||
}
|
||||
}
|
||||
rebuildSelectedModels(isOpposing)
|
||||
}
|
||||
|
||||
private fun rebuildSelectedModels(isOpposing: Boolean) {
|
||||
val x = (width - BASE_WIDTH) / 2
|
||||
val y = (height - BASE_HEIGHT) / 2
|
||||
|
||||
if (isOpposing) {
|
||||
opposingOfferedPokemon = pokemon
|
||||
opposingOfferedPokemonModel = if (pokemon != null) ModelWidget(
|
||||
pX = x + 145,
|
||||
pY = y + 30,
|
||||
pWidth = PORTRAIT_SIZE,
|
||||
pHeight = PORTRAIT_SIZE,
|
||||
pokemon = pokemon.asRenderablePokemon(),
|
||||
baseScale = PORTRAIT_SCALE,
|
||||
rotationY = 35F,
|
||||
offsetY = PORTRAIT_MODEL_OFFSET_Y
|
||||
) else null
|
||||
if (::opposingOfferedPokemonMarkings.isInitialized) opposingOfferedPokemonMarkings.setActivePokemon(opposingOfferedPokemon)
|
||||
trade.acceptedOppositeOffer = false
|
||||
opposingOfferedPokemonModels.clear()
|
||||
val total = opposingPokemons.size
|
||||
opposingPokemons.forEachIndexed { i, pk ->
|
||||
val (pos, currentScale) = getModelPositionAndScale(i, total, x + 145, y + 30)
|
||||
opposingOfferedPokemonModels.add(ModelWidget(
|
||||
pX = pos.first, pY = pos.second,
|
||||
pWidth = PORTRAIT_SIZE, pHeight = PORTRAIT_SIZE,
|
||||
pokemon = pk.asRenderablePokemon(),
|
||||
baseScale = currentScale,
|
||||
rotationY = 35F,
|
||||
offsetY = PORTRAIT_MODEL_OFFSET_Y
|
||||
))
|
||||
}
|
||||
} else {
|
||||
offeredPokemon = pokemon
|
||||
offeredPokemonModel = if (pokemon != null) ModelWidget(
|
||||
pX = x + 70,
|
||||
pY = y + 30,
|
||||
pWidth = PORTRAIT_SIZE,
|
||||
pHeight = PORTRAIT_SIZE,
|
||||
pokemon = pokemon.asRenderablePokemon(),
|
||||
baseScale = PORTRAIT_SCALE,
|
||||
rotationY = 325F,
|
||||
offsetY = PORTRAIT_MODEL_OFFSET_Y
|
||||
) else null
|
||||
if (::offeredPokemonMarkings.isInitialized) offeredPokemonMarkings.setActivePokemon(offeredPokemon)
|
||||
offeredPokemonModels.clear()
|
||||
val total = offeredPokemons.size
|
||||
offeredPokemons.forEachIndexed { i, pk ->
|
||||
val (pos, currentScale) = getModelPositionAndScale(i, total, x + 70, y + 30)
|
||||
offeredPokemonModels.add(ModelWidget(
|
||||
pX = pos.first, pY = pos.second,
|
||||
pWidth = PORTRAIT_SIZE, pHeight = PORTRAIT_SIZE,
|
||||
pokemon = pk.asRenderablePokemon(),
|
||||
baseScale = currentScale,
|
||||
rotationY = 325F,
|
||||
offsetY = PORTRAIT_MODEL_OFFSET_Y
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getModelPositionAndScale(index: Int, total: Int, baseX: Int, baseY: Int): Pair<Pair<Int, Int>, Float> {
|
||||
val scale = when (total) {
|
||||
1 -> PORTRAIT_SCALE
|
||||
2 -> PORTRAIT_SCALE * 0.75f
|
||||
3 -> PORTRAIT_SCALE * 0.65f
|
||||
4 -> PORTRAIT_SCALE * 0.55f
|
||||
else -> PORTRAIT_SCALE * 0.5f
|
||||
}
|
||||
|
||||
if (total <= 1) {
|
||||
return Pair(Pair(baseX, baseY), scale)
|
||||
}
|
||||
|
||||
val rowLength1 = when (total) {
|
||||
2, 3, 4 -> 2
|
||||
else -> 3
|
||||
}
|
||||
val rowLength2 = total - rowLength1
|
||||
|
||||
val isBottomRow = index >= rowLength1
|
||||
val row = if (isBottomRow) 1 else 0
|
||||
val col = if (isBottomRow) index - rowLength1 else index
|
||||
val currentRowLength = if (isBottomRow) rowLength2 else rowLength1
|
||||
|
||||
val colSpacing = when (total) {
|
||||
2 -> 35
|
||||
3 -> 30
|
||||
4 -> 25
|
||||
else -> 23
|
||||
}
|
||||
val rowSpacing = when (total) {
|
||||
2, 3 -> 30
|
||||
4 -> 28
|
||||
else -> 25
|
||||
}
|
||||
|
||||
val totalWidth = (currentRowLength - 1) * colSpacing
|
||||
val startX = baseX - (totalWidth / 2)
|
||||
|
||||
val totalHeight = if (rowLength2 > 0) rowSpacing else 0
|
||||
var startY = baseY + 12 - (totalHeight / 2)
|
||||
|
||||
if (total >= 4) {
|
||||
startY += 8
|
||||
}
|
||||
|
||||
val posX = startX + (col * colSpacing)
|
||||
val posY = startY + (row * rowSpacing)
|
||||
|
||||
return Pair(Pair(posX, posY), scale)
|
||||
}
|
||||
|
||||
private fun playSound(soundEvent: SoundEvent): SoundInstance {
|
||||
val soundInstance = SimpleSoundInstance.forUI(soundEvent, 1.0F)
|
||||
Minecraft.getInstance().soundManager.play(soundInstance)
|
||||
@@ -557,10 +791,10 @@ class TradeGUI(
|
||||
}
|
||||
|
||||
private fun cancelTradeSound() {
|
||||
if (tradeSoundInstance !== null) Minecraft.getInstance().soundManager.stop(tradeSoundInstance)
|
||||
if (tradeSoundInstance != null) Minecraft.getInstance().soundManager.stop(tradeSoundInstance!!)
|
||||
}
|
||||
|
||||
private fun renderPokemonInfo(pokemon: Pokemon?, isOpposing: Boolean, context: GuiGraphics, x: Int, y: Int, mouseX: Int, mouseY: Int) {
|
||||
public fun renderPokemonInfo(pokemon: Pokemon?, isOpposing: Boolean, context: GuiGraphics, x: Int, y: Int, mouseX: Int, mouseY: Int) {
|
||||
if (pokemon != null) {
|
||||
val matrices = context.pose()
|
||||
// Level
|
||||
@@ -1047,4 +1281,4 @@ class TradeGUI(
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.net.bag
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.item.bag.CobblemonBagMenu
|
||||
import com.cobblemon.mod.common.net.messages.client.bag.UpdateBagMenuDataPacket
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
object UpdateBagMenuDataHandler : ServerNetworkPacketHandler<UpdateBagMenuDataPacket> {
|
||||
override fun handle(packet: UpdateBagMenuDataPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
if (player.containerMenu !is CobblemonBagMenu) {
|
||||
Cobblemon.LOGGER.debug("Player {} interacted with invalid menu {}", player, player.containerMenu)
|
||||
return
|
||||
}
|
||||
|
||||
val menu = player.containerMenu as? CobblemonBagMenu ?: return
|
||||
menu.currentlySelectedCategory = packet.categoryId
|
||||
menu.scroll = packet.scroll
|
||||
menu.setupSlotsBasedOnContent()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.net.bag
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.item.bag.CobblemonBagMenu
|
||||
import com.cobblemon.mod.common.net.messages.client.bag.UseItemOnPokemonFromBagPacket
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
object UseItemOnPokemonFromBagHandler: ServerNetworkPacketHandler<UseItemOnPokemonFromBagPacket> {
|
||||
override fun handle(packet: UseItemOnPokemonFromBagPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
if (player.containerMenu !is CobblemonBagMenu) {
|
||||
Cobblemon.LOGGER.debug("Player {} interacted with invalid menu {}", player, player.containerMenu)
|
||||
return
|
||||
}
|
||||
|
||||
val menu = player.containerMenu as? CobblemonBagMenu ?: return
|
||||
menu.useHeldItemOnPokemon(packet.partySlot)
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,9 @@ import net.minecraft.client.Minecraft
|
||||
|
||||
object TradeAcceptanceChangedHandler : ClientNetworkPacketHandler<TradeAcceptanceChangedPacket> {
|
||||
override fun handle(packet: TradeAcceptanceChangedPacket, client: Minecraft) {
|
||||
if (CobblemonClient.trade?.myOffer?.get()?.uuid == packet.pokemonId) {
|
||||
if (CobblemonClient.trade?.myOffer?.get()?.any { it.uuid == packet.pokemonId } == true) {
|
||||
CobblemonClient.trade!!.oppositeAcceptedMyOffer.set(packet.accepted)
|
||||
} else if (CobblemonClient.trade?.oppositeOffer?.get()?.uuid == packet.pokemonId) {
|
||||
} else if (CobblemonClient.trade?.oppositeOffer?.get()?.any { it.uuid == packet.pokemonId } == true) {
|
||||
CobblemonClient.trade!!.acceptedOppositeOffer = packet.accepted
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ import net.minecraft.client.Minecraft
|
||||
object TradeCompletedHandler : ClientNetworkPacketHandler<TradeCompletedPacket> {
|
||||
override fun handle(packet: TradeCompletedPacket, client: Minecraft) {
|
||||
val trade = CobblemonClient.trade ?: return
|
||||
trade.completedEmitter.emit(packet.pokemonId1 to packet.pokemonId2)
|
||||
trade.completedEmitter.emit(packet.pokemonIds1 to packet.pokemonIds2)
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import net.minecraft.client.Minecraft
|
||||
|
||||
object TradeStartedHandler : ClientNetworkPacketHandler<TradeStartedPacket> {
|
||||
override fun handle(packet: TradeStartedPacket, client: Minecraft) {
|
||||
Cobblemon.LOGGER.error("handle trade start from: " + packet.traderId)
|
||||
Cobblemon.LOGGER.info("Trade started with {}", packet.traderId)
|
||||
val trade = ClientTrade()
|
||||
CobblemonClient.trade = trade
|
||||
Minecraft.getInstance().setScreen(
|
||||
|
||||
@@ -18,9 +18,9 @@ object TradeUpdatedHandler : ClientNetworkPacketHandler<TradeUpdatedPacket> {
|
||||
val trade = CobblemonClient.trade ?: return
|
||||
|
||||
if (packet.playerId == Minecraft.getInstance().player?.uuid) {
|
||||
trade.myOffer.set(packet.pokemon)
|
||||
trade.myOffer.set(packet.pokemons)
|
||||
} else {
|
||||
trade.oppositeOffer.set(packet.pokemon)
|
||||
trade.oppositeOffer.set(packet.pokemons)
|
||||
}
|
||||
trade.oppositeAcceptedMyOffer.set(false)
|
||||
trade.acceptedOppositeOffer = false
|
||||
|
||||
@@ -19,16 +19,11 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.BASE_HEIG
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.BASE_WIDTH
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokedex.scanner.BlockScanData
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext
|
||||
import com.cobblemon.mod.common.pokedex.scanner.ScannableBlockEntity
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.PokemonSizeCategory
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.DeltaTracker
|
||||
@@ -87,45 +82,13 @@ class PokedexScannerRenderer {
|
||||
fun infoFrameResource(isLeft: Boolean, tier: Int): ResourceLocation = cobblemonResource("textures/gui/pokedex/scan/scan_info_frame_${if (isLeft) "left" else "right"}_$tier.png")
|
||||
}
|
||||
|
||||
private var lastFocusedBlockTypeId: ResourceLocation? = null
|
||||
|
||||
private fun getBlockEntityInfo(entity: BlockEntity): BlockScanData? {
|
||||
val scannable = entity as? ScannableBlockEntity ?: return null
|
||||
return scannable.resolveBlockScan()
|
||||
}
|
||||
|
||||
fun renderInfoFrames(graphics: GuiGraphics, poseStack: PoseStack, usageContext: PokedexUsageContext, centerX: Int, centerY: Int, opacity: Float) {
|
||||
if (usageContext.focusIntervals > 0) {
|
||||
var infoDisplayedCounter = 0
|
||||
usageContext.availableInfoFrames.forEachIndexed {index, isLeftSide ->
|
||||
if (isLeftSide !== null) {
|
||||
val isPokemonFocus = usageContext.scannableEntityInFocus != null
|
||||
if (isPokemonFocus && infoDisplayedCounter > 1 && !usageContext.isPokemonInFocusOwned) return@forEachIndexed
|
||||
if (infoDisplayedCounter > 2 && !usageContext.isPokemonInFocusOwned) return@forEachIndexed
|
||||
infoDisplayedCounter++
|
||||
|
||||
val blockEntityInFocus: BlockEntity? = usageContext.blockEntityInFocus
|
||||
val pokedexEntityData = usageContext.scannableEntityInFocus?.resolvePokemonScan()
|
||||
|
||||
// store the block entity typeId so our close animation knows what to show
|
||||
if (pokedexEntityData != null) {
|
||||
lastFocusedBlockTypeId = null
|
||||
} else if (blockEntityInFocus != null) {
|
||||
lastFocusedBlockTypeId = BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(blockEntityInFocus.type)
|
||||
} else if (usageContext.focusIntervals <= 0f) {
|
||||
lastFocusedBlockTypeId = null
|
||||
}
|
||||
|
||||
val scanInfo = blockEntityInFocus?.let { getBlockEntityInfo(it) }
|
||||
|
||||
// if a block entity is being focused on make sure we only show the frames needed
|
||||
if (pokedexEntityData == null && scanInfo != null) {
|
||||
val hasFrameForSlot = scanInfo.frames.any { it.slot == infoDisplayedCounter }
|
||||
if (!hasFrameForSlot) {
|
||||
return@forEachIndexed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Frames
|
||||
val isInnerFrame = index == 1 || index == 2
|
||||
val frameHeight = if (isInnerFrame) INNER_INFO_FRAME_HEIGHT else OUTER_INFO_FRAME_HEIGHT
|
||||
@@ -147,79 +110,77 @@ class PokedexScannerRenderer {
|
||||
val yOffsetText = when(index) { 0 -> 5; 1 -> 4; 2 -> 8; 3 -> 42; else -> 0 }
|
||||
|
||||
// Text
|
||||
if (usageContext.focusIntervals == PokedexUsageContext.FOCUS_INTERVALS && (pokedexEntityData != null || blockEntityInFocus != null)) {
|
||||
if (pokedexEntityData != null) {
|
||||
if (infoDisplayedCounter == 1) {
|
||||
if (usageContext.focusIntervals == PokedexUsageContext.FOCUS_INTERVALS && usageContext.scannableEntityInFocus != null) {
|
||||
val pokedexEntityData = usageContext.scannableEntityInFocus?.resolvePokemonScan() ?: return
|
||||
|
||||
if (infoDisplayedCounter == 1) {
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = lang("ui.lv.number", pokedexEntityData.pokemon.level).bold(),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText,
|
||||
shadow = true,
|
||||
centered = true
|
||||
)
|
||||
}
|
||||
|
||||
if (infoDisplayedCounter == 2) {
|
||||
val hasTrainer = (usageContext.scannableEntityInFocus?.resolveEntityScan() as? PokemonEntity)?.ownerUUID !== null
|
||||
val speciesName = pokedexEntityData.getApparentSpecies().translatedName.bold()
|
||||
var yOffsetName = if (hasTrainer) 2 else 0
|
||||
if (hasTrainer) {
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = lang("ui.lv.number", pokedexEntityData.pokemon.level).bold(),
|
||||
text = lang("ui.pokedex.scan.trainer_owned"),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText,
|
||||
y = centerY + yOffset + yOffsetText - yOffsetName,
|
||||
shadow = true,
|
||||
centered = true
|
||||
centered = true,
|
||||
scale = 0.5F
|
||||
)
|
||||
}
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = speciesName,
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText + yOffsetName,
|
||||
shadow = true,
|
||||
centered = true
|
||||
)
|
||||
|
||||
if (infoDisplayedCounter == 2) {
|
||||
val hasTrainer =
|
||||
(usageContext.scannableEntityInFocus?.resolveEntityScan() as? PokemonEntity)?.ownerUUID !== null
|
||||
val speciesName = pokedexEntityData.getApparentSpecies().translatedName.bold()
|
||||
var yOffsetName = if (hasTrainer) 2 else 0
|
||||
if (hasTrainer) {
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
text = lang("ui.pokedex.scan.trainer_owned"),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText - yOffsetName,
|
||||
shadow = true,
|
||||
centered = true,
|
||||
scale = 0.5F
|
||||
)
|
||||
}
|
||||
val gender = pokedexEntityData.pokemon.gender
|
||||
val speciesNameWidth = Minecraft.getInstance().font.width(speciesName.font(CobblemonResources.DEFAULT_LARGE))
|
||||
if (gender != Gender.GENDERLESS) {
|
||||
val isMale = gender == Gender.MALE
|
||||
val textSymbol = if (isMale) "♂".text().bold() else "♀".text().bold()
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = speciesName,
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
text = textSymbol,
|
||||
x = centerX + xOffset + xOffsetText + 2 + (speciesNameWidth / 2),
|
||||
y = centerY + yOffset + yOffsetText + yOffsetName,
|
||||
shadow = true,
|
||||
centered = true
|
||||
colour = if (isMale) 0x32CBFF else 0xFC5454,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
val gender = pokedexEntityData.pokemon.gender
|
||||
val speciesNameWidth =
|
||||
Minecraft.getInstance().font.width(speciesName.font(CobblemonResources.DEFAULT_LARGE))
|
||||
if (gender != Gender.GENDERLESS) {
|
||||
val isMale = gender == Gender.MALE
|
||||
val textSymbol = if (isMale) "♂".text().bold() else "♀".text().bold()
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = textSymbol,
|
||||
x = centerX + xOffset + xOffsetText + 2 + (speciesNameWidth / 2),
|
||||
y = centerY + yOffset + yOffsetText + yOffsetName,
|
||||
colour = if (isMale) 0x32CBFF else 0xFC5454,
|
||||
shadow = true
|
||||
)
|
||||
}
|
||||
|
||||
if (usageContext.isPokemonInFocusOwned) {
|
||||
blitk(
|
||||
matrixStack = poseStack,
|
||||
texture = caughtIndicator,
|
||||
x = (centerX + xOffset + xOffsetText - 7 - (speciesNameWidth / 2)) / SCALE,
|
||||
y = (centerY + yOffset + yOffsetText + yOffsetName + 2) / SCALE,
|
||||
height = 10,
|
||||
width = 10,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (usageContext.isPokemonInFocusOwned) {
|
||||
blitk(
|
||||
matrixStack = poseStack,
|
||||
texture = caughtIndicator,
|
||||
x = (centerX + xOffset + xOffsetText - 7 - (speciesNameWidth / 2)) / SCALE,
|
||||
y = (centerY + yOffset + yOffsetText + yOffsetName + 2) / SCALE,
|
||||
height = 10,
|
||||
width = 10,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (infoDisplayedCounter == 3) {
|
||||
val pokemon = pokedexEntityData.pokemon
|
||||
if (infoDisplayedCounter == 3) {
|
||||
val pokemon = pokedexEntityData.pokemon
|
||||
val entityAlpha = (usageContext.scannableEntityInFocus?.resolveEntityScan() as? PokemonEntity)
|
||||
?.entityData
|
||||
?.get(PokemonEntity.IS_ALPHA)
|
||||
@@ -242,66 +203,35 @@ class PokedexScannerRenderer {
|
||||
}
|
||||
|
||||
if (infoDisplayedCounter == 4) {
|
||||
val typeText = lang(
|
||||
"type.suffix",
|
||||
pokedexEntityData.getApparentForm().types.map { it.displayName.copy() }
|
||||
.reduce { acc, next -> acc.plus("/").plus(next) }).bold()
|
||||
val typeWidth =
|
||||
Minecraft.getInstance().font.width(typeText.font(CobblemonResources.DEFAULT_LARGE))
|
||||
// Split into 2 lines if text width is too long
|
||||
if (typeWidth > (OUTER_INFO_FRAME_WIDTH - 8) && pokedexEntityData.getApparentForm().secondaryType !== null) {
|
||||
val splitLines = typeText.string.split("/")
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = text(splitLines[0], "/").bold(),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText - 4,
|
||||
shadow = true,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = text(splitLines[1]).bold(),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText + 3,
|
||||
shadow = true,
|
||||
centered = true
|
||||
)
|
||||
} else {
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = typeText,
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText,
|
||||
shadow = true,
|
||||
centered = true
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (blockEntityInFocus != null && scanInfo != null) {
|
||||
val frame = scanInfo.frames.firstOrNull { it.slot == infoDisplayedCounter } ?: return@forEachIndexed
|
||||
|
||||
val scale = frame.scale
|
||||
if (scale != null) {
|
||||
val typeText = lang("type.suffix", pokedexEntityData.getApparentForm().types.map { it.displayName.copy() }.reduce { acc, next -> acc.plus("/").plus(next) }).bold()
|
||||
val typeWidth = Minecraft.getInstance().font.width(typeText.font(CobblemonResources.DEFAULT_LARGE))
|
||||
// Split into 2 lines if text width is too long
|
||||
if (typeWidth > (OUTER_INFO_FRAME_WIDTH - 8) && pokedexEntityData.getApparentForm().secondaryType !== null) {
|
||||
val splitLines = typeText.string.split("/")
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = frame.text.bold(),
|
||||
text = text(splitLines[0], "/").bold(),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText,
|
||||
y = centerY + yOffset + yOffsetText - 4,
|
||||
shadow = true,
|
||||
centered = true,
|
||||
scale = scale
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = text(splitLines[1]).bold(),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText + 3,
|
||||
shadow = true,
|
||||
centered = true
|
||||
)
|
||||
} else {
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = frame.text.bold(),
|
||||
text = typeText,
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText,
|
||||
shadow = true,
|
||||
@@ -532,13 +462,4 @@ class PokedexScannerRenderer {
|
||||
renderScanOverlay(graphics, tickDelta)
|
||||
}
|
||||
}
|
||||
|
||||
private fun anyToComponent(value: Any?): MutableComponent {
|
||||
return when (value) {
|
||||
null -> text("")
|
||||
is MutableComponent -> value
|
||||
is Component -> value.copy()
|
||||
else -> text(value.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.layer
|
||||
|
||||
import com.cobblemon.mod.common.client.CobblemonBakingOverrides
|
||||
import com.cobblemon.mod.common.item.bag.CobblemonBagItem
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.math.Axis
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.model.PlayerModel
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.entity.RenderLayerParent
|
||||
import net.minecraft.client.renderer.entity.layers.RenderLayer
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture
|
||||
import net.minecraft.world.entity.EquipmentSlot
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemDisplayContext
|
||||
|
||||
class CobblemonBagRenderer<T : Player>(renderLayerParent: RenderLayerParent<T, PlayerModel<T>>): RenderLayer<T, PlayerModel<T>>(renderLayerParent) {
|
||||
override fun render(
|
||||
poseStack: PoseStack,
|
||||
bufferSource: MultiBufferSource,
|
||||
packedLight: Int,
|
||||
player: T,
|
||||
limbSwing: Float,
|
||||
limbSwingAmount: Float,
|
||||
partialTick: Float,
|
||||
ageInTicks: Float,
|
||||
netHeadYaw: Float,
|
||||
headPitch: Float
|
||||
) {
|
||||
val bagItem = player.getItemBySlot(EquipmentSlot.CHEST)
|
||||
if (bagItem.item !is CobblemonBagItem) return
|
||||
val model = CobblemonBakingOverrides.bagModels[bagItem.item]?.getModel() ?: return
|
||||
|
||||
player.level().profiler.push("cobblemon_bag_render")
|
||||
poseStack.pushPose()
|
||||
|
||||
parentModel.body.translateAndRotate(poseStack)
|
||||
|
||||
poseStack.rotateAround(Axis.ZP.rotationDegrees(180F), 0F, 1F, 0F)
|
||||
poseStack.translate(0.0, 1.0, 0.0)
|
||||
|
||||
Minecraft.getInstance().itemRenderer.render(
|
||||
bagItem,
|
||||
ItemDisplayContext.FIXED,
|
||||
false,
|
||||
poseStack,
|
||||
bufferSource,
|
||||
packedLight,
|
||||
OverlayTexture.NO_OVERLAY,
|
||||
model
|
||||
)
|
||||
|
||||
poseStack.popPose()
|
||||
player.level().profiler.pop()
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,12 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import java.util.UUID
|
||||
|
||||
class ClientTrade {
|
||||
var myOffer = SettableObservable<Pokemon?>(null)
|
||||
var oppositeOffer = SettableObservable<Pokemon?>(null)
|
||||
var myOffer = SettableObservable<List<Pokemon>>(emptyList())
|
||||
var oppositeOffer = SettableObservable<List<Pokemon>>(emptyList())
|
||||
var tradeProcessing = SettableObservable(Pair(false, false))
|
||||
var oppositeAcceptedMyOffer = SettableObservable(false)
|
||||
var acceptedOppositeOffer = false
|
||||
|
||||
var completedEmitter = SimpleObservable<Pair<UUID, UUID>>()
|
||||
var completedEmitter = SimpleObservable<Pair<List<UUID>, List<UUID>>>()
|
||||
var cancelEmitter = SimpleObservable<Unit>()
|
||||
}
|
||||
@@ -47,10 +47,6 @@ class CobblemonConfig {
|
||||
@IntConstraint(min = 0, max = 1000)
|
||||
var maxPokemonFriendship = 255
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "power_item_ride_stat_gain_interval_seconds", SERVER)
|
||||
@IntConstraint(min = -1, max = Int.MAX_VALUE)
|
||||
var powerItemRideStatGainIntervalSeconds = 120
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "announce_drop_items", SERVER)
|
||||
var announceDropItems = true
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "default_drop_item_method", SERVER)
|
||||
|
||||
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.data
|
||||
import com.cobblemon.mod.common.*
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.bag.BagCategories
|
||||
import com.cobblemon.mod.common.api.berry.Berries
|
||||
import com.cobblemon.mod.common.api.cooking.Seasonings
|
||||
import com.cobblemon.mod.common.api.data.DataProvider
|
||||
@@ -120,6 +121,7 @@ object CobblemonDataProvider : DataProvider {
|
||||
this.register(SpawnBaitEffects, reloadable = false)
|
||||
this.register(CobblemonRideSettings, reloadable = true)
|
||||
this.register(TechnicalMachines, reloadable = false)
|
||||
this.register(BagCategories, reloadable = false)
|
||||
SpawnBait.Effects.setupEffects()
|
||||
|
||||
PlatformEvents.SERVER_PLAYER_LOGOUT.subscribe {
|
||||
|
||||
@@ -160,5 +160,4 @@ class CobblemonChestBoatEntity(entityType: EntityType<CobblemonChestBoatEntity>,
|
||||
private const val INVENTORY_SLOTS = 27
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.TickingSpeciesFeature
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause.Companion.DROPS_REROLL_ASPECT
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
@@ -47,15 +46,10 @@ import kotlin.math.*
|
||||
|
||||
/** Handles purely server logic for a Pokémon */
|
||||
class PokemonServerDelegate : PokemonSideDelegate {
|
||||
companion object {
|
||||
private const val RIDE_STAT_GAIN_AMOUNT = 1F
|
||||
}
|
||||
|
||||
lateinit var entity: PokemonEntity
|
||||
var acknowledgedHPValue = -1
|
||||
var acknowledgedLevel = -1
|
||||
val passengerOffsets = mutableMapOf<Int, Vec3>()
|
||||
private var rideStatGainAnchor: Vec3? = null
|
||||
|
||||
/** Mocked properties exposed to the client [PokemonEntity]. */
|
||||
private val mock: PokemonProperties?
|
||||
@@ -361,30 +355,9 @@ class PokemonServerDelegate : PokemonSideDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
val rideStatGainIntervalSeconds = Cobblemon.config.powerItemRideStatGainIntervalSeconds
|
||||
if (rideStatGainIntervalSeconds > 0) {
|
||||
val rideStatGainIntervalTicks = rideStatGainIntervalSeconds * 20L
|
||||
if (entity.level().gameTime % rideStatGainIntervalTicks == 0L) {
|
||||
trainRideStats()
|
||||
}
|
||||
}
|
||||
|
||||
updateTrackedValues()
|
||||
}
|
||||
|
||||
private fun trainRideStats() {
|
||||
val position = entity.position()
|
||||
val anchor = rideStatGainAnchor
|
||||
rideStatGainAnchor = position
|
||||
if (anchor == null || anchor == position || entity.passengers.isEmpty()) return
|
||||
|
||||
val heldItem = entity.pokemon.heldItemNoCopy()
|
||||
if (heldItem.isEmpty) return
|
||||
|
||||
val stat = RidingStat.entries.firstOrNull { heldItem.`is`(it.powerItem) } ?: return
|
||||
entity.pokemon.addRideBoost(stat, RIDE_STAT_GAIN_AMOUNT)
|
||||
}
|
||||
|
||||
fun updateShownItem() {
|
||||
val trackedShownItem = when {
|
||||
// Show Hand Item if Held item is hidden
|
||||
|
||||
@@ -31,6 +31,7 @@ import net.minecraft.world.item.BlockItem
|
||||
import com.cobblemon.mod.common.platform.events.ServerPlayerEvent
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.item.GemItem
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.util.effectiveName
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
@@ -164,26 +165,21 @@ object AdvancementHandler : EventHandler {
|
||||
}
|
||||
|
||||
fun onTradeCompleted(event : TradeEvent.Post) {
|
||||
val player1 = event.tradeParticipant1Pokemon.getOwnerPlayer()
|
||||
val player2 = event.tradeParticipant2Pokemon.getOwnerPlayer()
|
||||
if (player1 != null) {
|
||||
CobblemonCriteria.TRADE_POKEMON.trigger(player1, TradePokemonContext(event.tradeParticipant1Pokemon, event.tradeParticipant2Pokemon))
|
||||
val playerData = Cobblemon.playerDataManager.getGenericData(player1)
|
||||
val advancementData = playerData.advancementData
|
||||
advancementData.updateTotalTradedCount()
|
||||
advancementData.updateAspectsCollected(player1, event.tradeParticipant2Pokemon)
|
||||
CobblemonCriteria.COLLECT_ASPECT.trigger(player1, advancementData.aspectsCollected)
|
||||
Cobblemon.playerDataManager.saveSingle(playerData, PlayerInstancedDataStoreTypes.GENERAL)
|
||||
}
|
||||
if (player2 != null) {
|
||||
CobblemonCriteria.TRADE_POKEMON.trigger(player2, TradePokemonContext(event.tradeParticipant2Pokemon, event.tradeParticipant1Pokemon))
|
||||
val playerData = Cobblemon.playerDataManager.getGenericData(player2)
|
||||
val advancementData = playerData.advancementData
|
||||
advancementData.updateTotalTradedCount()
|
||||
advancementData.updateAspectsCollected(player2, event.tradeParticipant1Pokemon)
|
||||
CobblemonCriteria.COLLECT_ASPECT.trigger(player2, advancementData.aspectsCollected)
|
||||
Cobblemon.playerDataManager.saveSingle(playerData, PlayerInstancedDataStoreTypes.GENERAL)
|
||||
onTradeCompleted(event.tradeParticipant1Pokemon, event.tradeParticipant2Pokemon)
|
||||
onTradeCompleted(event.tradeParticipant2Pokemon, event.tradeParticipant1Pokemon)
|
||||
}
|
||||
|
||||
private fun onTradeCompleted(received: List<Pokemon>, traded: List<Pokemon>) {
|
||||
val player = received.firstOrNull()?.getOwnerPlayer() ?: return
|
||||
received.forEach { receivedPokemon ->
|
||||
traded.forEach { tradedPokemon -> CobblemonCriteria.TRADE_POKEMON.trigger(player, TradePokemonContext(tradedPokemon, receivedPokemon)) }
|
||||
}
|
||||
val playerData = Cobblemon.playerDataManager.getGenericData(player)
|
||||
val advancementData = playerData.advancementData
|
||||
advancementData.updateTotalTradedCount()
|
||||
received.forEach { advancementData.updateAspectsCollected(player, it) }
|
||||
CobblemonCriteria.COLLECT_ASPECT.trigger(player, advancementData.aspectsCollected)
|
||||
Cobblemon.playerDataManager.saveSingle(playerData, PlayerInstancedDataStoreTypes.GENERAL)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -110,8 +110,8 @@ object StatHandler : EventHandler {
|
||||
}
|
||||
|
||||
fun onTradeCompleted(event : TradeEvent.Post) {
|
||||
event.tradeParticipant1Pokemon.getOwnerPlayer()?.awardStat(getStat(Cobblemon.statistics.TRADED))
|
||||
event.tradeParticipant2Pokemon.getOwnerPlayer()?.awardStat(getStat(Cobblemon.statistics.TRADED))
|
||||
event.tradeParticipant1Pokemon.firstOrNull()?.getOwnerPlayer()?.awardStat(getStat(Cobblemon.statistics.TRADED))
|
||||
event.tradeParticipant2Pokemon.firstOrNull()?.getOwnerPlayer()?.awardStat(getStat(Cobblemon.statistics.TRADED))
|
||||
}
|
||||
|
||||
fun onFossilRevived(event : FossilRevivedEvent) {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.item.bag
|
||||
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.world.Container
|
||||
import net.minecraft.world.ContainerHelper
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import kotlin.math.min
|
||||
|
||||
class CobblemonBagContainer: Container {
|
||||
val items: MutableList<ItemStack> = mutableListOf()
|
||||
|
||||
override fun getContainerSize(): Int {
|
||||
return items.size
|
||||
}
|
||||
|
||||
override fun getItem(slot: Int): ItemStack {
|
||||
if (slot >= items.size) return ItemStack.EMPTY
|
||||
return items[slot]
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return items.isEmpty()
|
||||
}
|
||||
|
||||
override fun removeItem(slot: Int, amount: Int): ItemStack {
|
||||
if (slot >= items.size) return ItemStack.EMPTY
|
||||
val itemStack = items[slot].split(amount)
|
||||
if (items[slot].isEmpty) {
|
||||
items.removeAt(slot)
|
||||
}
|
||||
return itemStack
|
||||
}
|
||||
|
||||
override fun removeItemNoUpdate(slot: Int): ItemStack { return ItemStack.EMPTY }
|
||||
|
||||
override fun clearContent() {
|
||||
items.clear()
|
||||
setChanged()
|
||||
}
|
||||
|
||||
override fun setChanged() {
|
||||
}
|
||||
|
||||
override fun setItem(slot: Int, stack: ItemStack) {} // Obsolete
|
||||
|
||||
override fun stillValid(player: Player): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getMaxStackSize(): Int {
|
||||
return 99999
|
||||
}
|
||||
|
||||
fun fromTag(tag: ListTag, levelRegistry: HolderLookup.Provider) {
|
||||
items.clear()
|
||||
|
||||
tag.forEach {
|
||||
val compoundTag = it as CompoundTag
|
||||
val count = compoundTag.getInt("count")
|
||||
compoundTag.putInt("count", 1)
|
||||
val item = ItemStack.parse(levelRegistry, it)
|
||||
item.ifPresent { theItem ->
|
||||
theItem.count = count
|
||||
items.add(theItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createTag(levelRegistry: HolderLookup.Provider): ListTag {
|
||||
val tag = ListTag()
|
||||
|
||||
items.forEach {
|
||||
val fakeItem = it.copyWithCount(1)
|
||||
val compoundTag = fakeItem.save(levelRegistry) as CompoundTag
|
||||
compoundTag.putInt("count", it.count) // ItemStack does not save count above 99 for some reason, so we save it ourselves
|
||||
tag.add(compoundTag)
|
||||
}
|
||||
|
||||
return tag
|
||||
}
|
||||
|
||||
fun dropAll(player: Player) {
|
||||
items.forEach {
|
||||
player.drop(it, true, false)
|
||||
}
|
||||
items.clear()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.item.bag
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import com.cobblemon.mod.common.bag.BagEntity
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResultHolder
|
||||
import net.minecraft.world.MenuProvider
|
||||
import net.minecraft.world.entity.EquipmentSlot
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||
import net.minecraft.world.item.Equipable
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.Level
|
||||
|
||||
class CobblemonBagItem: CobblemonItem(Properties().stacksTo(1)), MenuProvider, Equipable {
|
||||
override fun use(level: Level, player: Player, usedHand: InteractionHand): InteractionResultHolder<ItemStack?> {
|
||||
val itemStack = player.getItemInHand(usedHand)
|
||||
player.openMenu(this)
|
||||
return InteractionResultHolder.success(itemStack)
|
||||
}
|
||||
|
||||
override fun createMenu(i: Int, inventory: Inventory, player: Player): AbstractContainerMenu {
|
||||
val bagEntity = (player as? BagEntity)
|
||||
val container = bagEntity?.`cobblemon$getBagContainer`() ?: CobblemonBagContainer()
|
||||
|
||||
return CobblemonBagMenu(i, inventory, container)
|
||||
}
|
||||
|
||||
override fun getDisplayName(): Component? {
|
||||
return Component.translatable("cobblemon.container.bag")
|
||||
}
|
||||
|
||||
override fun getEquipmentSlot(): EquipmentSlot = EquipmentSlot.CHEST
|
||||
|
||||
override fun getEquipSound(): Holder<SoundEvent?>? {
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.ITEM_BAG_EQUIP, 1.0F))
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
/*
|
||||
* 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.item.bag
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonMenuType
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.advancement.criterion.PokemonInteractContext
|
||||
import com.cobblemon.mod.common.api.bag.BagCategories
|
||||
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.util.isInBattle
|
||||
import net.minecraft.CrashReport
|
||||
import net.minecraft.ReportedException
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.core.NonNullList
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.SimpleContainer
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||
import net.minecraft.world.inventory.ClickAction
|
||||
import net.minecraft.world.inventory.ClickType
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class CobblemonBagMenu: AbstractContainerMenu {
|
||||
companion object {
|
||||
val BAG_SLOTS = 0..31
|
||||
private val PARTY_SLOTS = 32..37
|
||||
private val INVENTORY_SLOTS = 38..64
|
||||
private val HOTBAR_SLOTS = 65..73
|
||||
}
|
||||
|
||||
private val playerInventory: Inventory
|
||||
private val bagContainer: CobblemonBagContainer
|
||||
private val partyContainer: SimpleContainer
|
||||
|
||||
private var serverParty: PartyStore? = null
|
||||
var currentlySelectedCategory = -1
|
||||
var scroll = 0
|
||||
var maxScroll = 0
|
||||
var categoryItemCounts = mutableListOf<Int>()
|
||||
|
||||
constructor(containerId: Int, playerInventory: Inventory): super(CobblemonMenuType.COBBLEMON_BAG, containerId) {
|
||||
this.playerInventory = playerInventory
|
||||
this.bagContainer = CobblemonBagContainer()
|
||||
this.partyContainer = SimpleContainer(6)
|
||||
for (i in 0..<BagCategories.getOrderedCategoriesList().size) { //Pre-init so Screen does not crash
|
||||
categoryItemCounts.add(0)
|
||||
}
|
||||
initializeSlots(playerInventory)
|
||||
}
|
||||
|
||||
constructor(containerId: Int, playerInventory: Inventory, bagContainer: CobblemonBagContainer): super(CobblemonMenuType.COBBLEMON_BAG, containerId) {
|
||||
this.playerInventory = playerInventory
|
||||
this.bagContainer = bagContainer
|
||||
this.partyContainer = SimpleContainer(6)
|
||||
this.serverParty = Cobblemon.storage.getParty(playerInventory.player as ServerPlayer)
|
||||
initializeSlots(playerInventory)
|
||||
setupSlotsBasedOnContent()
|
||||
calculateCategoriesItemCounts()
|
||||
}
|
||||
|
||||
private fun initializeSlots(playerInventory: Inventory) {
|
||||
for ((index, _) in BAG_SLOTS.withIndex()) {
|
||||
val i = index / 8
|
||||
val j = index % 8
|
||||
addSlot(CobblemonBagSlot(bagContainer, -1, 81 + j * 18, 50 + i * 18))
|
||||
}
|
||||
|
||||
for ((index, _) in PARTY_SLOTS.withIndex()) {
|
||||
val xPos = 46
|
||||
val yPos = 32 + 33 * index
|
||||
if (serverParty != null) {
|
||||
addSlot(CobblemonBagPartySlot(partyContainer, index,
|
||||
{ serverParty?.get(index) != null },
|
||||
{ serverParty?.get(index)?.heldItem() ?: ItemStack.EMPTY },
|
||||
xPos, yPos))
|
||||
} else {
|
||||
addSlot(CobblemonBagPartySlot(partyContainer, index,
|
||||
{ CobblemonClient.storage.party.get(index) != null },
|
||||
{ CobblemonClient.storage.party.get(index)?.heldItem() ?: ItemStack.EMPTY },
|
||||
xPos, yPos))
|
||||
}
|
||||
}
|
||||
|
||||
for ((index, _) in INVENTORY_SLOTS.withIndex()) {
|
||||
val i = index / 9
|
||||
val j = index % 9
|
||||
addSlot(Slot(playerInventory, 9 + index, 81 + j * 18, 142 + i * 18))
|
||||
}
|
||||
|
||||
for ((index, _) in HOTBAR_SLOTS.withIndex()) {
|
||||
addSlot(Slot(playerInventory, index, 81 + index * 18, 200))
|
||||
}
|
||||
}
|
||||
|
||||
fun setupSlotsBasedOnContent() {
|
||||
BAG_SLOTS.forEach {
|
||||
(getSlot(it) as CobblemonBagSlot).let {
|
||||
it.simulatedSlot = -1
|
||||
it.slotOverflowValue = 0
|
||||
}
|
||||
}
|
||||
|
||||
var slotId = BAG_SLOTS.first - 8 * scroll
|
||||
bagContainer.items.forEachIndexed { index, itemStack ->
|
||||
if (currentlySelectedCategory == -1 || BagCategories.getOrderedCategoriesList()[currentlySelectedCategory].itemIsValid(itemStack)) {
|
||||
val stackCount = ceil(itemStack.count.toFloat() / itemStack.maxStackSize).toInt()
|
||||
for (i in 0..<stackCount) {
|
||||
if (slotId in BAG_SLOTS) {
|
||||
val slot = getSlot(slotId) as CobblemonBagSlot
|
||||
slot.simulatedSlot = index
|
||||
slot.slotOverflowValue = i
|
||||
}
|
||||
slotId += 1
|
||||
if (slotId > BAG_SLOTS.last) return@forEachIndexed
|
||||
}
|
||||
}
|
||||
}
|
||||
updateMaxScroll()
|
||||
}
|
||||
|
||||
fun calculateCategoriesItemCounts() {
|
||||
val orderedCategories = BagCategories.getOrderedCategoriesList()
|
||||
categoryItemCounts.clear()
|
||||
for (i in 0..<orderedCategories.size) {
|
||||
categoryItemCounts.add(0)
|
||||
}
|
||||
|
||||
bagContainer.items.forEach { itemStack ->
|
||||
for (i in 0..<orderedCategories.size) {
|
||||
val category = orderedCategories[i]
|
||||
if (category.itemIsValid(itemStack)) {
|
||||
categoryItemCounts[i] += itemStack.count
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun changeCategory(categoryId: Int) {
|
||||
currentlySelectedCategory = categoryId
|
||||
scroll = 0
|
||||
setupSlotsBasedOnContent()
|
||||
}
|
||||
|
||||
fun updateMaxScroll() {
|
||||
var itemCount = 0
|
||||
bagContainer.items.forEach { itemStack ->
|
||||
if (currentlySelectedCategory == -1 || BagCategories.getOrderedCategoriesList()[currentlySelectedCategory].itemIsValid(itemStack)) {
|
||||
itemCount += ceil(itemStack.count.toFloat() / itemStack.maxStackSize).toInt()
|
||||
}
|
||||
}
|
||||
val rowsWithItems = (itemCount - 1) / 8 + 1
|
||||
maxScroll = max(rowsWithItems - 3, 0)
|
||||
}
|
||||
|
||||
override fun stillValid(player: Player): Boolean {
|
||||
return true //Add some extra logic like checking for maybe bag item existance
|
||||
}
|
||||
|
||||
override fun sendAllDataToRemote() { //We want to send more info than just the slots
|
||||
val items = NonNullList.create<ItemStack>()
|
||||
for (i in PARTY_SLOTS.first..HOTBAR_SLOTS.last) {
|
||||
this.remoteSlots[i] = this.slots[i].item.copy()
|
||||
items.add(this.remoteSlots[i].copy())
|
||||
}
|
||||
|
||||
this.remoteCarried = this.carried.copy()
|
||||
|
||||
bagContainer.items.forEach {
|
||||
items.add(it.copy())
|
||||
}
|
||||
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer!!.sendInitialData(this, items, this.remoteCarried, this.remoteDataSlots.toIntArray())
|
||||
}
|
||||
}
|
||||
|
||||
override fun initializeContents(stateId: Int, items: List<ItemStack>, carried: ItemStack) {
|
||||
bagContainer.items.clear()
|
||||
items.forEachIndexed { index, stack ->
|
||||
val adjustedIndex = index + PARTY_SLOTS.first
|
||||
if (adjustedIndex in PARTY_SLOTS.first..HOTBAR_SLOTS.last) {
|
||||
this.slots[adjustedIndex].set(stack)
|
||||
} else {
|
||||
bagContainer.items.add(stack)
|
||||
}
|
||||
}
|
||||
|
||||
this.carried = carried
|
||||
this.stateId = stateId
|
||||
setupSlotsBasedOnContent()
|
||||
calculateCategoriesItemCounts()
|
||||
}
|
||||
|
||||
override fun clicked(slotId: Int, button: Int, clickType: ClickType, player: Player) {
|
||||
if (slotId in BAG_SLOTS) {
|
||||
try {
|
||||
doBagClick(slotId, button, clickType, player)
|
||||
} catch (exception: Exception) {
|
||||
val crashReport = CrashReport.forThrowable(exception, "Container click")
|
||||
val crashReportCategory = crashReport.addCategory("Click info")
|
||||
crashReportCategory.setDetail("Menu Type") {
|
||||
if (this.menuType != null) BuiltInRegistries.MENU.getKey(this.menuType!!)
|
||||
.toString() else "<no type>"
|
||||
}
|
||||
crashReportCategory.setDetail("Menu Class") { this.javaClass.getCanonicalName() }
|
||||
crashReportCategory.setDetail("Slot Count", this.slots.size)
|
||||
crashReportCategory.setDetail("Slot", slotId)
|
||||
crashReportCategory.setDetail("Button", button)
|
||||
crashReportCategory.setDetail("Type", clickType)
|
||||
throw ReportedException(crashReport)
|
||||
}
|
||||
return
|
||||
} else if (slotId in PARTY_SLOTS) {
|
||||
onPartySlotClick(slotId, button, clickType, player)
|
||||
}
|
||||
super.clicked(slotId, button, clickType, player)
|
||||
}
|
||||
|
||||
// We handle the bag slots completely differently than normal slots, but a lot of default logic is cut
|
||||
private fun doBagClick(slotId: Int, button: Int, clickType: ClickType, player: Player) {
|
||||
val slot = (getSlot(slotId) as? CobblemonBagSlot) ?: return
|
||||
val slotItem = slot.item
|
||||
|
||||
if (clickType == ClickType.PICKUP && (button == 0 || button == 1)) {
|
||||
val primaryButton = if (button == 0) ClickAction.PRIMARY else ClickAction.SECONDARY
|
||||
val carriedStack = carried
|
||||
|
||||
if (carriedStack.isEmpty) {
|
||||
if (!slotItem.isEmpty) {
|
||||
val pickupCount = if (primaryButton == ClickAction.PRIMARY) slotItem.count else (slotItem.count + 1) / 2
|
||||
|
||||
val removedItemStack = slot.remove(pickupCount)
|
||||
if (!removedItemStack.isEmpty) {
|
||||
carried = removedItemStack
|
||||
slot.onTake(player, removedItemStack)
|
||||
getAndReduceCategoryCount(removedItemStack)
|
||||
}
|
||||
|
||||
playSound(CobblemonSounds.ITEM_BAG_ITEM_OUT)
|
||||
setupSlotsBasedOnContent()
|
||||
}
|
||||
} else {
|
||||
val (categoryId, allowedTransfer) = calculateInsertCount(carriedStack)
|
||||
if (allowedTransfer <= 0) return
|
||||
|
||||
val transferCount = if (primaryButton == ClickAction.PRIMARY) min(carriedStack.count, allowedTransfer) else 1
|
||||
|
||||
bagContainer.items.forEachIndexed { index, slotStack ->
|
||||
if (ItemStack.isSameItemSameComponents(carriedStack, slotStack) && !tryItemClickBehaviourOverride(player, primaryButton, CobblemonBagSlot(bagContainer, index, 0, 0), slotStack, carriedStack)
|
||||
) { // Provide a slot with index equal to the item, as there is not really any 'real' slots
|
||||
slotStack.grow(transferCount)
|
||||
carriedStack.shrink(transferCount)
|
||||
categoryItemCounts[categoryId] += transferCount
|
||||
playSound(CobblemonSounds.ITEM_BAG_ITEM_IN)
|
||||
setupSlotsBasedOnContent()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val transferItem = carriedStack.split(transferCount)
|
||||
bagContainer.items.add(transferItem)
|
||||
categoryItemCounts[categoryId] += transferCount
|
||||
playSound(CobblemonSounds.ITEM_BAG_ITEM_IN)
|
||||
setupSlotsBasedOnContent()
|
||||
}
|
||||
} else if (clickType == ClickType.QUICK_MOVE && (button == 0 || button == 1)) {
|
||||
quickMoveStackFromBag(player, slot)
|
||||
} else if (clickType == ClickType.CLONE && player.hasInfiniteMaterials() && carried.isEmpty && !slotItem.isEmpty) {
|
||||
carried = slotItem.copyWithCount(slotItem.maxStackSize)
|
||||
} else if (clickType == ClickType.THROW && carried.isEmpty && !slotItem.isEmpty) {
|
||||
val throwCount = if (button == 0) 1 else min(slotItem.count, slotItem.maxStackSize)
|
||||
val bagSize = bagContainer.items.size
|
||||
val removedItemStack = slot.remove(throwCount)
|
||||
player.drop(removedItemStack, true)
|
||||
playSound(CobblemonSounds.ITEM_BAG_ITEM_OUT)
|
||||
getAndReduceCategoryCount(removedItemStack)
|
||||
|
||||
if (bagSize != bagContainer.items.size) {
|
||||
setupSlotsBasedOnContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Handle party slot clicked
|
||||
private fun onPartySlotClick(slotId: Int, button: Int, clickType: ClickType, player: Player) {
|
||||
val slotItem = getSlot(slotId).item
|
||||
if (clickType == ClickType.CLONE && player.hasInfiniteMaterials() && carried.isEmpty && !slotItem.isEmpty) {
|
||||
carried = slotItem.copyWithCount(slotItem.maxStackSize)
|
||||
} else if (clickType != ClickType.PICKUP) return
|
||||
|
||||
if (serverParty == null) return // Client does nothing
|
||||
|
||||
if (!carried.isEmpty && (isBlacklisted(carried) || !isWhitelisted(carried))) return //Blacklisted and Whitelisted checks
|
||||
|
||||
val index = slotId - PARTY_SLOTS.first
|
||||
val pokemon = serverParty?.get(index) ?: return
|
||||
|
||||
val heldItem = pokemon.heldItem()
|
||||
|
||||
if (carried.isEmpty || carried.count <= 1) { // Can Swap/Pickup
|
||||
val returned = pokemon.swapHeldItem(carried, aiCanDrop = false)
|
||||
carried = returned
|
||||
broadcastChanges()
|
||||
} else if (heldItem.isEmpty) { // Can Placedown
|
||||
pokemon.swapHeldItem(carried, aiCanDrop = false)
|
||||
broadcastChanges()
|
||||
}
|
||||
}
|
||||
|
||||
//Handled only by server
|
||||
fun useHeldItemOnPokemon(partySlot: Int) {
|
||||
if (carried.isEmpty) return
|
||||
|
||||
val pokemon = serverParty?.get(partySlot) ?: return
|
||||
val item = carried.item as? PokemonSelectingItem ?: return
|
||||
val player = playerInventory.player as? ServerPlayer ?: return
|
||||
|
||||
if (player.isInBattle()) return
|
||||
|
||||
val interaction = item.applyToPokemon(player, carried, pokemon)
|
||||
if (interaction?.result == InteractionResult.SUCCESS) {
|
||||
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(pokemon.species.resourceIdentifier, BuiltInRegistries.ITEM.getKey(carried.item)))
|
||||
broadcastChanges()
|
||||
}
|
||||
}
|
||||
|
||||
private fun isBlacklisted(stack: ItemStack): Boolean =
|
||||
BuiltInRegistries.ITEM.getTagOrEmpty(CobblemonItemTags.BLACKLISTED_ITEMS_TO_HOLD).any()
|
||||
&& stack.`is`(CobblemonItemTags.BLACKLISTED_ITEMS_TO_HOLD)
|
||||
|
||||
private fun isWhitelisted(stack: ItemStack): Boolean =
|
||||
BuiltInRegistries.ITEM.getTagOrEmpty(CobblemonItemTags.WHITELISTED_ITEMS_TO_HOLD).none()
|
||||
|| stack.`is`(CobblemonItemTags.WHITELISTED_ITEMS_TO_HOLD)
|
||||
|
||||
override fun quickMoveStack(player: Player, index: Int): ItemStack { // This will only be called by base inventory
|
||||
if (index < 0 || index >= slots.size) return ItemStack.EMPTY
|
||||
val slot = slots[index]
|
||||
val itemStack = slot.item
|
||||
|
||||
val (categoryId, allowedTransfer) = calculateInsertCount(itemStack)
|
||||
if (allowedTransfer <= 0) return ItemStack.EMPTY
|
||||
|
||||
val transferCount = min(itemStack.count, allowedTransfer)
|
||||
|
||||
if (slot.hasItem()) {
|
||||
bagContainer.items.forEach { slotStack ->
|
||||
if (ItemStack.isSameItemSameComponents(slotStack, itemStack)) {
|
||||
slotStack.grow(transferCount)
|
||||
itemStack.shrink(transferCount)
|
||||
categoryItemCounts[categoryId] += transferCount
|
||||
playSound(CobblemonSounds.ITEM_BAG_ITEM_IN)
|
||||
setupSlotsBasedOnContent()
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
bagContainer.items.add(itemStack.split(transferCount))
|
||||
categoryItemCounts[categoryId] += transferCount
|
||||
playSound(CobblemonSounds.ITEM_BAG_ITEM_IN)
|
||||
setupSlotsBasedOnContent()
|
||||
}
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
fun quickMoveStackFromBag(player: Player, slot: CobblemonBagSlot) { //Called by bag items
|
||||
val transferStack = slot.item
|
||||
val startingCount = transferStack.count
|
||||
|
||||
if (moveItemStackTo(transferStack, INVENTORY_SLOTS.first, HOTBAR_SLOTS.last + 1, false)) {
|
||||
val itemsMoved = startingCount - transferStack.count
|
||||
getAndReduceCategoryCount(slot.remove(itemsMoved))
|
||||
playSound(CobblemonSounds.ITEM_BAG_ITEM_OUT)
|
||||
setupSlotsBasedOnContent()
|
||||
}
|
||||
}
|
||||
|
||||
fun calculateInsertCount(stack: ItemStack): Pair<Int, Int> {
|
||||
if (stack.isEmpty) return -1 to 0
|
||||
|
||||
BagCategories.getOrderedCategoriesList().forEachIndexed { index, category ->
|
||||
if (category.itemIsValid(stack)) {
|
||||
val maxAllowed = max(category.maxCount - categoryItemCounts[index], 0)
|
||||
return index to min(stack.count, maxAllowed)
|
||||
}
|
||||
}
|
||||
return -1 to 0
|
||||
}
|
||||
|
||||
fun getAndReduceCategoryCount(stack: ItemStack) {
|
||||
if (stack.isEmpty) return
|
||||
|
||||
BagCategories.getOrderedCategoriesList().forEachIndexed { index, category ->
|
||||
if (category.itemIsValid(stack)) {
|
||||
categoryItemCounts[index] -= stack.count
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun canTakeItemForPickAll(stack: ItemStack, slot: Slot): Boolean {
|
||||
return slot.index !in BAG_SLOTS
|
||||
}
|
||||
|
||||
fun playSound(soundEvent: SoundEvent) {
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.item.bag
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.world.SimpleContainer
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
class CobblemonBagPartySlot(
|
||||
container: SimpleContainer,
|
||||
index: Int,
|
||||
val activeGetter: () -> Boolean,
|
||||
val itemGetter: () -> ItemStack,
|
||||
x: Int,
|
||||
y: Int
|
||||
): Slot(container, index, x, y) {
|
||||
override fun getMaxStackSize(): Int = 1
|
||||
|
||||
override fun mayPlace(stack: ItemStack): Boolean = false
|
||||
override fun mayPickup(player: Player): Boolean = false
|
||||
|
||||
override fun getItem(): ItemStack {
|
||||
return itemGetter.invoke()
|
||||
}
|
||||
|
||||
override fun isActive(): Boolean {
|
||||
return activeGetter.invoke()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.item.bag
|
||||
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import java.util.Optional
|
||||
import kotlin.math.min
|
||||
|
||||
class CobblemonBagSlot(
|
||||
container: CobblemonBagContainer,
|
||||
var simulatedSlot: Int, //Cant override base slot variable so it is forever -1
|
||||
x: Int,
|
||||
y: Int
|
||||
): Slot(container, 0, x, y) {
|
||||
var slotOverflowValue = 0
|
||||
|
||||
override fun set(stack: ItemStack) {}
|
||||
|
||||
override fun getItem(): ItemStack {
|
||||
if (simulatedSlot == -1) return ItemStack.EMPTY
|
||||
val itemStack = container.getItem(simulatedSlot)
|
||||
val count = min(itemStack.count - itemStack.maxStackSize * slotOverflowValue, itemStack.maxStackSize)
|
||||
return itemStack.copyWithCount(count)
|
||||
}
|
||||
|
||||
override fun remove(amount: Int): ItemStack {
|
||||
if (simulatedSlot == -1) return ItemStack.EMPTY
|
||||
return container.removeItem(simulatedSlot, amount)
|
||||
}
|
||||
|
||||
override fun mayPlace(stack: ItemStack): Boolean = false
|
||||
override fun mayPickup(player: Player): Boolean = false
|
||||
}
|
||||
@@ -945,6 +945,8 @@ object CobblemonItemGroups {
|
||||
CobblemonItems.pokeBalls.forEach(entries::accept)
|
||||
CobblemonItems.pokeRods.forEach(entries::accept)
|
||||
CobblemonItems.pokedexes.forEach(entries::accept)
|
||||
entries.accept(CobblemonItems.TRAINER_PACK)
|
||||
entries.accept(CobblemonItems.TRAINER_BAG)
|
||||
}
|
||||
|
||||
private fun blocksInjections(injector: Injector) {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.net.messages.client.bag
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class UpdateBagMenuDataPacket(val categoryId: Int, val scroll: Int): NetworkPacket<UpdateBagMenuDataPacket> {
|
||||
companion object {
|
||||
val ID = cobblemonResource("update_bag_menu_data")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): UpdateBagMenuDataPacket {
|
||||
return UpdateBagMenuDataPacket(buffer.readInt(), buffer.readInt())
|
||||
}
|
||||
}
|
||||
|
||||
override val id = ID
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeInt(categoryId)
|
||||
buffer.writeInt(scroll)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.net.messages.client.bag
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class UseItemOnPokemonFromBagPacket(val partySlot: Int): NetworkPacket<UseItemOnPokemonFromBagPacket> {
|
||||
companion object {
|
||||
val ID = cobblemonResource("use_item_on_pokemon_from_bag")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): UseItemOnPokemonFromBagPacket {
|
||||
return UseItemOnPokemonFromBagPacket(buffer.readInt())
|
||||
}
|
||||
}
|
||||
override val id = ID
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeInt(partySlot)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.net.messages.client.data
|
||||
|
||||
import com.cobblemon.mod.common.api.bag.BagCategories
|
||||
import com.cobblemon.mod.common.api.bag.BagCategory
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class BagCategoriesSyncPacket(categories: List<BagCategory>): DataRegistrySyncPacket<BagCategory, BagCategoriesSyncPacket>(categories) {
|
||||
companion object {
|
||||
val ID = cobblemonResource("bag_categories")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = BagCategoriesSyncPacket(emptyList()).apply { decodeBuffer(buffer) }
|
||||
}
|
||||
|
||||
override val id = ID
|
||||
override fun encodeEntry(buffer: RegistryFriendlyByteBuf, entry: BagCategory) {
|
||||
entry.encode(buffer)
|
||||
}
|
||||
|
||||
override fun decodeEntry(buffer: RegistryFriendlyByteBuf): BagCategory = BagCategory.decode(buffer)
|
||||
|
||||
override fun synchronizeDecoded(entries: Collection<BagCategory>) {
|
||||
BagCategories.reload(entries.associateBy { it.identifier })
|
||||
}
|
||||
}
|
||||
@@ -23,16 +23,19 @@ import java.util.UUID
|
||||
* @author Hiroku
|
||||
* @since March 5th, 2023
|
||||
*/
|
||||
class TradeCompletedPacket(val pokemonId1: UUID, val pokemonId2: UUID) : NetworkPacket<TradeCompletedPacket> {
|
||||
class TradeCompletedPacket(val pokemonIds1: List<UUID>, val pokemonIds2: List<UUID>) : NetworkPacket<TradeCompletedPacket> {
|
||||
companion object {
|
||||
val ID = cobblemonResource("trade_completed")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = TradeCompletedPacket(buffer.readUUID(), buffer.readUUID())
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = TradeCompletedPacket(
|
||||
buffer.readList { it.readUUID() },
|
||||
buffer.readList { it.readUUID() }
|
||||
)
|
||||
}
|
||||
|
||||
override val id = ID
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeUUID(pokemonId1)
|
||||
buffer.writeUUID(pokemonId2)
|
||||
buffer.writeCollection(pokemonIds1) { buf, id -> buf.writeUUID(id) }
|
||||
buffer.writeCollection(pokemonIds2) { buf, id -> buf.writeUUID(id) }
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,6 @@ import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.net.UnsplittablePacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readNullable
|
||||
import com.cobblemon.mod.common.util.readUUID
|
||||
import com.cobblemon.mod.common.util.writeNullable
|
||||
import com.cobblemon.mod.common.util.writeUUID
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import java.util.UUID
|
||||
|
||||
@@ -27,15 +23,18 @@ import java.util.UUID
|
||||
* @author Hiroku
|
||||
* @since March 5th, 2023
|
||||
*/
|
||||
class TradeUpdatedPacket(val playerId: UUID, val pokemon: Pokemon?) : NetworkPacket<TradeUpdatedPacket>, UnsplittablePacket {
|
||||
class TradeUpdatedPacket(val playerId: UUID, val pokemons: List<Pokemon>) : NetworkPacket<TradeUpdatedPacket>, UnsplittablePacket {
|
||||
companion object {
|
||||
val ID = cobblemonResource("trade_updated")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = TradeUpdatedPacket(buffer.readUUID(), buffer.readNullable { Pokemon.S2C_CODEC.decode(buffer) })
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = TradeUpdatedPacket(
|
||||
buffer.readUUID(),
|
||||
buffer.readList { Pokemon.S2C_CODEC.decode(it as RegistryFriendlyByteBuf) }
|
||||
)
|
||||
}
|
||||
|
||||
override val id = ID
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeUUID(playerId)
|
||||
buffer.writeNullable(pokemon) { _, pokemon -> Pokemon.S2C_CODEC.encode(buffer, pokemon) }
|
||||
buffer.writeCollection(pokemons) { buf, pokemon -> Pokemon.S2C_CODEC.encode(buf as RegistryFriendlyByteBuf, pokemon) }
|
||||
}
|
||||
}
|
||||
@@ -11,26 +11,30 @@ package com.cobblemon.mod.common.net.messages.server.trade
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyPosition
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readList
|
||||
import com.cobblemon.mod.common.util.readNullable
|
||||
import com.cobblemon.mod.common.util.readPartyPosition
|
||||
import com.cobblemon.mod.common.util.readUUID
|
||||
import com.cobblemon.mod.common.util.writeCollection
|
||||
import com.cobblemon.mod.common.util.writeNullable
|
||||
import com.cobblemon.mod.common.util.writePartyPosition
|
||||
import com.cobblemon.mod.common.util.writeUUID
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import java.util.UUID
|
||||
|
||||
class UpdateTradeOfferPacket(val newOffer: Pair<UUID, PartyPosition>?): NetworkPacket<UpdateTradeOfferPacket> {
|
||||
class UpdateTradeOfferPacket(val newOffers: List<Pair<UUID, PartyPosition>>): NetworkPacket<UpdateTradeOfferPacket> {
|
||||
companion object {
|
||||
val ID = cobblemonResource("update_trade_offer")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = UpdateTradeOfferPacket(buffer.readNullable { buffer.readUUID() to buffer.readPartyPosition() })
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = UpdateTradeOfferPacket(
|
||||
buffer.readList { it.readUUID() to it.readPartyPosition() }
|
||||
)
|
||||
}
|
||||
|
||||
override val id = ID
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeNullable(newOffer) { buffer, (pokemonId, partyPosition) ->
|
||||
buffer.writeUUID(pokemonId)
|
||||
buffer.writePartyPosition(partyPosition)
|
||||
buffer.writeCollection(newOffers) { buf, (pokemonId, partyPosition) ->
|
||||
buf.writeUUID(pokemonId)
|
||||
buf.writePartyPosition(partyPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ object ChangeTradeAcceptanceHandler : ServerNetworkPacketHandler<ChangeTradeAcce
|
||||
override fun handle(packet: ChangeTradeAcceptancePacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
val trade = TradeManager.getActiveTrade(player.uuid) ?: return player.sendPacket(TradeCancelledPacket())
|
||||
val tradeParticipant = trade.getTradeParticipant(player.uuid)
|
||||
if (trade.getOpposingOffer(tradeParticipant).pokemon?.uuid == packet.pokemonOfferId) {
|
||||
if (trade.getOpposingOffer(tradeParticipant).pokemon.any { it.uuid == packet.pokemonOfferId }) {
|
||||
trade.updateAcceptance(tradeParticipant, packet.newAcceptance)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ object PerformTradeHandler : ServerNetworkPacketHandler<PerformTradePacket> {
|
||||
override fun handle(packet: PerformTradePacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
val trade = TradeManager.getActiveTrade(player.uuid) ?: return player.sendPacket(TradeCancelledPacket())
|
||||
val tradeParticipant = trade.getTradeParticipant(player.uuid)
|
||||
if (trade.getOpposingOffer(tradeParticipant).pokemon?.uuid == packet.pokemonOfferId) {
|
||||
if (trade.getOpposingOffer(tradeParticipant).pokemon.any { it.uuid == packet.pokemonOfferId }) {
|
||||
trade.performTrade(tradeParticipant)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.CancelTradePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.UpdateTradeOfferPacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.trade.TradeManager
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import net.minecraft.server.MinecraftServer
|
||||
@@ -21,20 +22,16 @@ object UpdateTradeOfferHandler : ServerNetworkPacketHandler<UpdateTradeOfferPack
|
||||
override fun handle(packet: UpdateTradeOfferPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
val trade = TradeManager.getActiveTrade(player.uuid) ?: return player.sendPacket(CancelTradePacket())
|
||||
val tradeParticipant = trade.getTradeParticipant(player.uuid)
|
||||
val newOffer = packet.newOffer
|
||||
if (newOffer == null) {
|
||||
trade.updateOffer(tradeParticipant, null)
|
||||
} else {
|
||||
val (pokemonId, partyPosition) = newOffer
|
||||
val party = player.party()
|
||||
val newOffers = packet.newOffers
|
||||
|
||||
val validPokemons = mutableListOf<Pokemon>()
|
||||
val party = player.party()
|
||||
for ((pokemonId, partyPosition) in newOffers) {
|
||||
val pokemon = party[partyPosition]
|
||||
if (pokemon == null || pokemon.uuid != pokemonId) {
|
||||
return
|
||||
} else if (!pokemon.tradeable) {
|
||||
return
|
||||
} else {
|
||||
trade.updateOffer(tradeParticipant, pokemon)
|
||||
if (pokemon != null && pokemon.uuid == pokemonId && pokemon.tradeable) {
|
||||
validPokemons.add(pokemon)
|
||||
}
|
||||
}
|
||||
trade.updateOffer(tradeParticipant, validPokemons)
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.cobblemon.mod.common.pokedex.scanner
|
||||
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
|
||||
data class BlockScanData(
|
||||
val frames: MutableSet<Frame> = mutableSetOf()
|
||||
)
|
||||
|
||||
data class Frame(
|
||||
val slot: Int,
|
||||
val text: MutableComponent,
|
||||
val scale: Float? = null
|
||||
)
|
||||
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.pokedex.scanner
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexLearnedInformation
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUI
|
||||
import com.cobblemon.mod.common.client.pokedex.PokedexScannerRenderer
|
||||
@@ -19,8 +18,6 @@ import com.cobblemon.mod.common.client.pokedex.PokedexType
|
||||
import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedRegisterPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.random.Random
|
||||
@@ -29,7 +26,6 @@ import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.player.LocalPlayer
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import kotlin.math.exp
|
||||
@@ -55,8 +51,6 @@ class PokedexUsageContext {
|
||||
var infoGuiOpen: Boolean = false
|
||||
var scanningGuiOpen: Boolean = false
|
||||
var isPokemonInFocusOwned: Boolean = false
|
||||
var blockEntityInFocus: BlockEntity? = null
|
||||
var blockPosInFocus: BlockPos? = null
|
||||
var registerCompleted: Boolean = false
|
||||
var scannedSpecies: ResourceLocation? = null
|
||||
var scannableEntityInFocus: ScannableEntity? = null
|
||||
@@ -74,8 +68,6 @@ class PokedexUsageContext {
|
||||
var availableInfoFrames: MutableList<Boolean?> = mutableListOf(null, null, null, null)
|
||||
val renderer: PokedexScannerRenderer = PokedexScannerRenderer()
|
||||
|
||||
private fun hasAnyFocus() = (scannableEntityInFocus != null) || (blockEntityInFocus != null)
|
||||
|
||||
fun stopUsing(ticksInUse: Int, speciesId: ResourceLocation? = null) {
|
||||
if (ticksInUse < OPEN_SCANNER_BUFFER_TICKS) {
|
||||
openPokedexGUI(type, speciesId)
|
||||
@@ -97,9 +89,7 @@ class PokedexUsageContext {
|
||||
if (transitionIntervals == TRANSITION_INTERVALS) playSound(CobblemonSounds.POKEDEX_SCAN_CLOSE)
|
||||
transitionIntervals = max(transitionIntervals - updateInterval, 0F)
|
||||
if (transitionIntervals <= 0) {
|
||||
if (viewInfoTicks >= VIEW_INFO_BUFFER_TICKS && scannableEntityInFocus != null) {
|
||||
openPokedexGUI(type, scannableEntityInFocus!!.resolvePokemonScan()?.getApparentSpecies()?.resourceIdentifier)
|
||||
}
|
||||
if (viewInfoTicks >= VIEW_INFO_BUFFER_TICKS) openPokedexGUI(type, scannableEntityInFocus!!.resolvePokemonScan()?.getApparentSpecies()?.resourceIdentifier)
|
||||
resetState()
|
||||
}
|
||||
}
|
||||
@@ -112,7 +102,7 @@ class PokedexUsageContext {
|
||||
if (scanningProgress >= MAX_SCAN_PROGRESS) targetId?.let { FinishScanningPacket(it, zoomLevel.toInt()).sendToServer() }
|
||||
if (focusIntervals > 0) focusIntervals = max(0F, focusIntervals - updateInterval)
|
||||
} else {
|
||||
if (hasAnyFocus()) {
|
||||
if (scannableEntityInFocus != null) {
|
||||
if (focusIntervals < FOCUS_INTERVALS) focusIntervals = min(focusIntervals + updateInterval, FOCUS_INTERVALS)
|
||||
} else {
|
||||
if (focusIntervals > 0) focusIntervals = max(0F, focusIntervals - updateInterval)
|
||||
@@ -147,9 +137,8 @@ class PokedexUsageContext {
|
||||
playSound(CobblemonSounds.POKEDEX_OPEN)
|
||||
}
|
||||
|
||||
// todo display info of scanned berry to the Berry Dex later
|
||||
fun attackKeyHeld(isHeld: Boolean) {
|
||||
if (isHeld && hasAnyFocus() && viewInfoTicks < VIEW_INFO_BUFFER_TICKS && scanningProgress == 0F) {
|
||||
if (isHeld && scannableEntityInFocus !== null && viewInfoTicks < VIEW_INFO_BUFFER_TICKS && scanningProgress == 0F) {
|
||||
viewInfoTicks++
|
||||
if (viewInfoTicks % 2 == 0) playSound(CobblemonSounds.POKEDEX_SCAN_LOOP)
|
||||
} else if (viewInfoTicks > 0 && viewInfoTicks < VIEW_INFO_BUFFER_TICKS) {
|
||||
@@ -158,30 +147,26 @@ class PokedexUsageContext {
|
||||
}
|
||||
|
||||
fun tryScanPokemon(user: LocalPlayer) {
|
||||
// Pokémon/entity scan
|
||||
val targetScannableEntity = PokemonScanner.findScannableEntity(user, zoomLevel.toInt())
|
||||
if (targetScannableEntity != null) {
|
||||
if (targetScannableEntity != scannableEntityInFocus) {
|
||||
resetFocusedPokemonState()
|
||||
scannableEntityInFocus = targetScannableEntity
|
||||
|
||||
val resolvedPokemon = scannableEntityInFocus?.resolvePokemonScan()
|
||||
if (resolvedPokemon == null) {
|
||||
if(resolvedPokemon == null) {
|
||||
resetFocusedPokemonState()
|
||||
return
|
||||
}
|
||||
|
||||
// Check if Pokémon in focus is owned
|
||||
isPokemonInFocusOwned =
|
||||
CobblemonClient.clientPokedexData.getHighestKnowledgeForSpecies(resolvedPokemon.getApparentSpecies().resourceIdentifier) == PokedexEntryProgress.OWNED
|
||||
isPokemonInFocusOwned = CobblemonClient.clientPokedexData.getHighestKnowledgeForSpecies(resolvedPokemon.getApparentSpecies().resourceIdentifier) == PokedexEntryProgress.OWNED
|
||||
|
||||
// Randomize info frames for render
|
||||
if (focusIntervals == 0F) {
|
||||
availableInfoFrames = mutableListOf(null, null, null, null)
|
||||
|
||||
for (i in 0..3) {
|
||||
var randomIndex = Random.nextInt(availableInfoFrames.size)
|
||||
if (availableInfoFrames[randomIndex] != null) randomIndex = availableInfoFrames.indexOfFirst { it == null }
|
||||
if (availableInfoFrames[randomIndex] !== null) randomIndex = availableInfoFrames.indexOfFirst { it == null }
|
||||
availableInfoFrames.set(randomIndex, Random.nextBoolean())
|
||||
}
|
||||
}
|
||||
@@ -191,31 +176,9 @@ class PokedexUsageContext {
|
||||
if (newPokemonInfo == PokedexLearnedInformation.NONE) playSound(CobblemonSounds.POKEDEX_SCAN_DETAIL)
|
||||
else scannedSpecies = resolvedPokemon.getApparentSpecies().resourceIdentifier
|
||||
}
|
||||
return
|
||||
} else {
|
||||
resetFocusedPokemonState()
|
||||
}
|
||||
|
||||
// BlockEntity scan
|
||||
val blockHit = PokemonScanner.findScannableBlockEntity(user, zoomLevel.toInt())
|
||||
if (blockHit != null) {
|
||||
val (pos, be) = blockHit
|
||||
if (be != blockEntityInFocus || pos != blockPosInFocus) {
|
||||
resetFocusedPokemonState()
|
||||
blockEntityInFocus = be
|
||||
blockPosInFocus = pos
|
||||
|
||||
if (focusIntervals == 0F) {
|
||||
availableInfoFrames = mutableListOf(null, null, null, null)
|
||||
for (i in 0..3) {
|
||||
var randomIndex = Random.nextInt(availableInfoFrames.size)
|
||||
if (availableInfoFrames[randomIndex] != null) randomIndex = availableInfoFrames.indexOfFirst { it == null }
|
||||
availableInfoFrames[randomIndex] = Random.nextBoolean()
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
resetFocusedPokemonState()
|
||||
}
|
||||
|
||||
fun onServerConfirmedRegister(packet: ServerConfirmedRegisterPacket) {
|
||||
@@ -233,8 +196,6 @@ class PokedexUsageContext {
|
||||
|
||||
fun resetFocusedPokemonState() {
|
||||
scannableEntityInFocus = null
|
||||
blockEntityInFocus = null
|
||||
blockPosInFocus = null
|
||||
scannedSpecies = null
|
||||
viewInfoTicks = 0
|
||||
scanningProgress = 0F
|
||||
|
||||
@@ -10,14 +10,10 @@ package com.cobblemon.mod.common.pokedex.scanner
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext.Companion.BLOCK_LENGTH_PER_ZOOM_STAGE
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.level.ClipContext
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.HitResult
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
|
||||
//Handles the actual raycasting to figure out what pokemon we are looking at
|
||||
object PokemonScanner {
|
||||
@@ -104,34 +100,4 @@ object PokemonScanner {
|
||||
fun isEntityInRange(castingEntity: Entity, targetEntity: Entity, zoomLevel: Int): Boolean {
|
||||
return targetEntity.position().distanceTo(castingEntity.position()) <= Cobblemon.config.maxPokedexScanningDetectionRange + (zoomLevel * BLOCK_LENGTH_PER_ZOOM_STAGE)
|
||||
}
|
||||
|
||||
fun findScannableBlockEntity(castingEntity: Entity, zoomLevel: Int): Pair<BlockPos, BlockEntity>? {
|
||||
val eyePos = castingEntity.getEyePosition(1.0F)
|
||||
val lookVec = castingEntity.getViewVector(1.0F)
|
||||
|
||||
val maxDistance =
|
||||
Cobblemon.config.maxPokedexScanningDetectionRange + (zoomLevel * BLOCK_LENGTH_PER_ZOOM_STAGE)
|
||||
|
||||
val end = eyePos.add(lookVec.scale(maxDistance.toDouble()))
|
||||
|
||||
val hit = castingEntity.level().clip(
|
||||
ClipContext(
|
||||
eyePos,
|
||||
end,
|
||||
ClipContext.Block.OUTLINE,
|
||||
ClipContext.Fluid.NONE,
|
||||
castingEntity
|
||||
)
|
||||
)
|
||||
|
||||
if (hit.type != HitResult.Type.BLOCK) return null
|
||||
|
||||
val pos = (hit as BlockHitResult).blockPos
|
||||
val be = castingEntity.level().getBlockEntity(pos) ?: return null
|
||||
|
||||
val typeId = BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(be.type) ?: return null
|
||||
// if (BlockScanProviderRegistry.get(typeId) == null) return null
|
||||
|
||||
return pos to be
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.cobblemon.mod.common.pokedex.scanner
|
||||
|
||||
interface ScannableBlockEntity {
|
||||
fun resolveBlockScan(): BlockScanData
|
||||
}
|
||||
@@ -19,7 +19,7 @@ class ActiveTrade(val player1: TradeParticipant, val player2: TradeParticipant)
|
||||
fun getOffer(tradeParticipant: TradeParticipant) = if (tradeParticipant == player1) player1Offer else player2Offer
|
||||
fun getOpposingOffer(tradeParticipant: TradeParticipant) = if (tradeParticipant == player1) player2Offer else player1Offer
|
||||
|
||||
fun updateOffer(tradeParticipant: TradeParticipant, pokemon: Pokemon?) {
|
||||
fun updateOffer(tradeParticipant: TradeParticipant, pokemon: List<Pokemon>) {
|
||||
getOffer(tradeParticipant).updateOffer(pokemon)
|
||||
getOffer(getOppositePlayer(tradeParticipant)).accepted = false
|
||||
player1.updateOffer(this, tradeParticipant, pokemon)
|
||||
@@ -30,8 +30,9 @@ class ActiveTrade(val player1: TradeParticipant, val player2: TradeParticipant)
|
||||
val offer = getOpposingOffer(tradeParticipant)
|
||||
if (offer.accepted != acceptance) {
|
||||
offer.accepted = acceptance
|
||||
getOppositePlayer(tradeParticipant).changeTradeAcceptance(this, offer.pokemon!!.uuid, acceptance)
|
||||
tradeParticipant.changeTradeAcceptance(this, offer.pokemon!!.uuid, acceptance)
|
||||
val firstId = offer.pokemon.firstOrNull()?.uuid ?: java.util.UUID.randomUUID()
|
||||
getOppositePlayer(tradeParticipant).changeTradeAcceptance(this, firstId, acceptance)
|
||||
tradeParticipant.changeTradeAcceptance(this, firstId, acceptance)
|
||||
}
|
||||
|
||||
if (offer.accepted && getOffer(tradeParticipant).accepted) {
|
||||
@@ -51,8 +52,8 @@ class ActiveTrade(val player1: TradeParticipant, val player2: TradeParticipant)
|
||||
activeTrade = this,
|
||||
player1 = player1,
|
||||
player2 = player2,
|
||||
pokemon1 = player1Offer.pokemon!!,
|
||||
pokemon2 = player2Offer.pokemon!!
|
||||
pokemon1 = player1Offer.pokemon,
|
||||
pokemon2 = player2Offer.pokemon
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -64,12 +65,14 @@ class ActiveTrade(val player1: TradeParticipant, val player2: TradeParticipant)
|
||||
}
|
||||
|
||||
fun completeTrade() {
|
||||
player1.completeTrade(this, player1Offer.pokemon!!.uuid, player2Offer.pokemon!!.uuid)
|
||||
player2.completeTrade(this, player2Offer.pokemon!!.uuid, player1Offer.pokemon!!.uuid)
|
||||
val uuids1 = player1Offer.pokemon.map { it.uuid }
|
||||
val uuids2 = player2Offer.pokemon.map { it.uuid }
|
||||
player1.completeTrade(this, uuids1, uuids2)
|
||||
player2.completeTrade(this, uuids2, uuids1)
|
||||
|
||||
player1Offer.pokemon = null
|
||||
player1Offer.pokemon = emptyList()
|
||||
player1Offer.accepted = false
|
||||
player2Offer.pokemon = null
|
||||
player2Offer.pokemon = emptyList()
|
||||
player2Offer.accepted = false
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.pokemon.TradeEvent
|
||||
import com.cobblemon.mod.common.api.interaction.ServerPlayerActionRequest
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.api.text.aqua
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferExpiredPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferNotificationPacket
|
||||
@@ -101,71 +102,98 @@ object TradeManager : RequestManager<TradeManager.TradeRequest>() {
|
||||
fun performTrade(
|
||||
activeTrade: ActiveTrade,
|
||||
player1: TradeParticipant,
|
||||
pokemon1: Pokemon,
|
||||
pokemon1: List<Pokemon>,
|
||||
player2: TradeParticipant,
|
||||
pokemon2: Pokemon
|
||||
pokemon2: List<Pokemon>
|
||||
) {
|
||||
val party1 = player1.party
|
||||
val party2 = player2.party
|
||||
|
||||
if (pokemon1 !in party1 || pokemon2 !in party2) {
|
||||
if (pokemon1.any { it !in party1 } || pokemon2.any { it !in party2 }) {
|
||||
Cobblemon.LOGGER.warn("Attempted to perform a trade with Pokémon that aren't in the party anymore. Could be attempted duping between ${player1.name} and ${player2.name}")
|
||||
activeTrade.cancelTrade()
|
||||
return
|
||||
}
|
||||
|
||||
CobblemonEvents.TRADE_EVENT_PRE.postThen(TradeEvent.Pre(player1, pokemon2, player2, pokemon1)) {
|
||||
val party1 = player1.party
|
||||
val party2 = player2.party
|
||||
if (pokemon1.isEmpty() || pokemon2.isEmpty()) {
|
||||
Cobblemon.LOGGER.warn("Attempted to perform a trade where one side offered no Pokémon between ${player1.name} and ${player2.name}. This should never happen.")
|
||||
activeTrade.cancelTrade()
|
||||
return
|
||||
}
|
||||
|
||||
val player1Removed = party1.remove(pokemon1)
|
||||
val player2Removed = party2.remove(pokemon2)
|
||||
CobblemonEvents.TRADE_EVENT_PRE.postThen(
|
||||
TradeEvent.Pre(player1, pokemon2, player2, pokemon1)
|
||||
) {
|
||||
val player1Removed = pokemon1.filter { party1.remove(it) }
|
||||
val player2Removed = pokemon2.filter { party2.remove(it) }
|
||||
|
||||
if (!player1Removed || !player2Removed) {
|
||||
if (!player1Removed) {
|
||||
Cobblemon.LOGGER.warn("Failed to remove traded Pokémon ${pokemon1.species.resourceIdentifier} from ${player1.name.string}(${player1.uuid}). Aborting trade with ${player2.name.string}(${player2.uuid}), potential exploit abused.")
|
||||
if (player1Removed.size != pokemon1.size || player2Removed.size != pokemon2.size) {
|
||||
(pokemon1 - player1Removed.toSet()).forEach {
|
||||
Cobblemon.LOGGER.warn("Failed to remove traded Pokémon ${it.species.resourceIdentifier} from ${player1.name.string}(${player1.uuid}). Aborting trade with ${player2.name.string}(${player2.uuid}), potential exploit abused.")
|
||||
}
|
||||
|
||||
if (!player2Removed) {
|
||||
Cobblemon.LOGGER.warn("Failed to remove traded Pokémon ${pokemon2.species.resourceIdentifier} from ${player2.name.string}(${player2.uuid}). Aborting trade with ${player1.name.string}(${player1.uuid}), potential exploit abused.")
|
||||
(pokemon2 - player2Removed.toSet()).forEach {
|
||||
Cobblemon.LOGGER.warn("Failed to remove traded Pokémon ${it.species.resourceIdentifier} from ${player2.name.string}(${player2.uuid}). Aborting trade with ${player1.name.string}(${player1.uuid}), potential exploit abused.")
|
||||
}
|
||||
|
||||
// Revert any changes if one of the removals failed
|
||||
if (player1Removed) party1.add(pokemon1)
|
||||
if (player2Removed) party2.add(pokemon2)
|
||||
player1Removed.forEach { party1.add(it) }
|
||||
player2Removed.forEach { party2.add(it) }
|
||||
return@postThen
|
||||
}
|
||||
|
||||
// If player1 is the OT, keep track of the friendship
|
||||
pokemon1.cacheFriendship(player1.uuid)
|
||||
pokemon1.forEach { it.cacheFriendship(player1.uuid) }
|
||||
// If player2 is the OT, keep track of the friendship
|
||||
pokemon2.cacheFriendship(player2.uuid)
|
||||
pokemon2.forEach { it.cacheFriendship(player2.uuid) }
|
||||
|
||||
// If pokemon2 was originally player1's, set the friendship back to what it was at point of trading
|
||||
if ( !pokemon2.restoreFriendship(player1.uuid) ) {
|
||||
// Otherwise, reset the friendship
|
||||
pokemon2.setFriendship(pokemon2.form.baseFriendship)
|
||||
// If a Pokémon received by player1 was originally theirs, set the friendship back to what it was at point of trading
|
||||
pokemon2.forEach {
|
||||
if (!it.restoreFriendship(player1.uuid)) {
|
||||
// Otherwise, reset the friendship
|
||||
it.setFriendship(it.form.baseFriendship)
|
||||
}
|
||||
}
|
||||
// If pokemon1 was originally player2's, set the friendship back to what it was at point of trading
|
||||
if ( !pokemon1.restoreFriendship(player2.uuid) ) {
|
||||
// Otherwise, reset the friendship
|
||||
pokemon1.setFriendship(pokemon1.form.baseFriendship)
|
||||
// If a Pokémon received by player2 was originally theirs, set the friendship back to what it was at point of trading
|
||||
pokemon1.forEach {
|
||||
if (!it.restoreFriendship(player2.uuid)) {
|
||||
// Otherwise, reset the friendship
|
||||
it.setFriendship(it.form.baseFriendship)
|
||||
}
|
||||
}
|
||||
|
||||
party2.add(pokemon1)
|
||||
party1.add(pokemon2)
|
||||
pokemon1.forEach { party2.add(it) }
|
||||
pokemon2.forEach { party1.add(it) }
|
||||
|
||||
pokemon1.lockedEvolutions.filterIsInstance<TradeEvolution>().firstOrNull {
|
||||
it.attemptEvolution(pokemon1, pokemon2)
|
||||
}
|
||||
// Each traded Pokémon may evolve if any of the Pokémon it was traded for satisfies its context.
|
||||
// Evolutions are resolved before any are applied so one evolving can't invalidate another's context.
|
||||
(pokemon1.map { it to pokemon2 } + pokemon2.map { it to pokemon1 })
|
||||
.mapNotNull { (pokemon, tradedFor) ->
|
||||
pokemon.lockedEvolutions.filterIsInstance<TradeEvolution>()
|
||||
.firstOrNull { evolution -> tradedFor.any { evolution.testContext(pokemon, it) } && evolution.test(pokemon) }
|
||||
?.let { pokemon to it }
|
||||
}
|
||||
.forEach { (pokemon, evolution) -> evolution.evolve(pokemon) }
|
||||
|
||||
pokemon2.lockedEvolutions.filterIsInstance<TradeEvolution>().firstOrNull {
|
||||
it.attemptEvolution(pokemon2, pokemon1)
|
||||
val compactParty = { party: PartyStore ->
|
||||
var writeIdx = 0
|
||||
for (readIdx in 0 until party.size()) {
|
||||
if (party.get(readIdx) != null) {
|
||||
if (readIdx != writeIdx) {
|
||||
party.swap(readIdx, writeIdx)
|
||||
}
|
||||
writeIdx++
|
||||
}
|
||||
}
|
||||
}
|
||||
compactParty(party1)
|
||||
compactParty(party2)
|
||||
|
||||
activeTrade.completeTrade()
|
||||
|
||||
CobblemonEvents.TRADE_EVENT_POST.post(TradeEvent.Post(player1, pokemon2, player2, pokemon1))
|
||||
CobblemonEvents.TRADE_EVENT_POST.post(
|
||||
TradeEvent.Post(player1, pokemon2, player2, pokemon1)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,10 @@ package com.cobblemon.mod.common.trade
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
class TradeOffer {
|
||||
var pokemon: Pokemon? = null
|
||||
var pokemon: List<Pokemon> = emptyList()
|
||||
var accepted = false
|
||||
|
||||
fun updateOffer(pokemon: Pokemon?) {
|
||||
fun updateOffer(pokemon: List<Pokemon>) {
|
||||
this.pokemon = pokemon
|
||||
accepted = false
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ interface TradeParticipant {
|
||||
*
|
||||
* If it's null, it means they're no longer offering a Pokémon (so they're redeciding).
|
||||
*/
|
||||
fun updateOffer(trade: ActiveTrade, tradeParticipant: TradeParticipant, pokemon: Pokemon?)
|
||||
fun updateOffer(trade: ActiveTrade, tradeParticipant: TradeParticipant, pokemon: List<Pokemon>)
|
||||
|
||||
/**
|
||||
* Notifies the participant that the acceptance state for trading with the opponent [Pokemon] with UUID [pokemonId]
|
||||
@@ -59,7 +59,7 @@ interface TradeParticipant {
|
||||
fun cancelTrade(trade: ActiveTrade)
|
||||
|
||||
/** Notifies the participant that the trade has been completed. The actual Pokémon have already been exchanged. */
|
||||
fun completeTrade(trade: ActiveTrade, pokemonId1: UUID, pokemonId2: UUID)
|
||||
fun completeTrade(trade: ActiveTrade, pokemonIds1: List<UUID>, pokemonIds2: List<UUID>)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,7 +73,7 @@ class PlayerTradeParticipant(val player: ServerPlayer): TradeParticipant {
|
||||
override val uuid = player.uuid
|
||||
override val party = player.party()
|
||||
|
||||
override fun updateOffer(trade: ActiveTrade, tradeParticipant: TradeParticipant, pokemon: Pokemon?) {
|
||||
override fun updateOffer(trade: ActiveTrade, tradeParticipant: TradeParticipant, pokemon: List<Pokemon>) {
|
||||
player.sendPacket(TradeUpdatedPacket(tradeParticipant.uuid, pokemon))
|
||||
}
|
||||
|
||||
@@ -89,8 +89,8 @@ class PlayerTradeParticipant(val player: ServerPlayer): TradeParticipant {
|
||||
player.sendPacket(TradeCancelledPacket())
|
||||
}
|
||||
|
||||
override fun completeTrade(trade: ActiveTrade, pokemonId1: UUID, pokemonId2: UUID) {
|
||||
player.sendPacket(TradeCompletedPacket(pokemonId1, pokemonId2))
|
||||
override fun completeTrade(trade: ActiveTrade, pokemonIds1: List<UUID>, pokemonIds2: List<UUID>) {
|
||||
player.sendPacket(TradeCompletedPacket(pokemonIds1, pokemonIds2))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ class DummyTradeParticipant(val pokemonList: MutableList<Pokemon>) : TradePartic
|
||||
// Don't need to do any notifying.
|
||||
}
|
||||
|
||||
override fun completeTrade(trade: ActiveTrade, pokemonId1: UUID, pokemonId2: UUID) {
|
||||
override fun completeTrade(trade: ActiveTrade, pokemonIds1: List<UUID>, pokemonIds2: List<UUID>) {
|
||||
// Don't need to do any notifying.
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ class DummyTradeParticipant(val pokemonList: MutableList<Pokemon>) : TradePartic
|
||||
// React, maybe. Change code and hotswap.
|
||||
}
|
||||
|
||||
override fun updateOffer(trade: ActiveTrade, tradeParticipant: TradeParticipant, pokemon: Pokemon?) {
|
||||
override fun updateOffer(trade: ActiveTrade, tradeParticipant: TradeParticipant, pokemon: List<Pokemon>) {
|
||||
// React, maybe. Change code and hotswap.
|
||||
}
|
||||
}
|
||||
@@ -30,4 +30,6 @@ object CobblemonGameRules {
|
||||
val HEALER_SET_SPAWN: GameRules.Key<GameRules.BooleanValue> = Cobblemon.implementation.registerGameRule("healersSetSpawn", GameRules.Category.MISC, BooleanRuleInvoker.`cobblemon$create`(false))
|
||||
@JvmField
|
||||
val DO_POKEMON_PREDATION: GameRules.Key<GameRules.BooleanValue> = Cobblemon.implementation.registerGameRule("doPokemonPredation", GameRules.Category.MOBS, BooleanRuleInvoker.`cobblemon$create`(true))
|
||||
@JvmField
|
||||
val BAG_DROP_ITEMS: GameRules.Key<GameRules.BooleanValue> = Cobblemon.implementation.registerGameRule("bagDropItems", GameRules.Category.PLAYER, BooleanRuleInvoker.`cobblemon$create`(false))
|
||||
}
|
||||
@@ -456,12 +456,6 @@
|
||||
"cannon_left": {
|
||||
"position": [0, 0, 3]
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.25": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.25": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"2.25": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"3.25": "q.run_molang('cobblemon:stepsound_standard_large');"
|
||||
}
|
||||
},
|
||||
"animation.blastoise.ground_run": {
|
||||
|
||||
@@ -83,7 +83,6 @@
|
||||
},
|
||||
"animation.parasect.ground_walk": {
|
||||
"loop": true,
|
||||
"animation_length": 1,
|
||||
"bones": {
|
||||
"body": {
|
||||
"rotation": [0, 0, 0],
|
||||
@@ -135,12 +134,6 @@
|
||||
"locator_seat_1": {
|
||||
"rotation": [7.5, 0, 0]
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.0": "q.run_molang('cobblemon:stepsound_minecraft');",
|
||||
"0.25": "q.run_molang('cobblemon:stepsound_minecraft');",
|
||||
"0.5": "q.run_molang('cobblemon:stepsound_minecraft');",
|
||||
"0.75": "q.run_molang('cobblemon:stepsound_minecraft');"
|
||||
}
|
||||
},
|
||||
"animation.parasect.ground_run": {
|
||||
@@ -265,10 +258,6 @@
|
||||
"rotation": ["5+Math.cos((query.anim_time) * 90 * 16 - 90) * 2", 0, 0],
|
||||
"position": [0, "0+Math.cos((query.anim_time) * 90 * 32) * -0.2", -0.25]
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.0": "q.run_molang('cobblemon:stepsound_minecraft');",
|
||||
"0.125": "q.run_molang('cobblemon:stepsound_minecraft');"
|
||||
}
|
||||
},
|
||||
"animation.parasect.ride_ground_run": {
|
||||
|
||||
@@ -391,10 +391,6 @@
|
||||
"0.7083": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.0833": "q.run_molang('cobblemon:stepsound_standard_medium');",
|
||||
"0.2917": "q.run_molang('cobblemon:stepsound_standard_medium');"
|
||||
}
|
||||
},
|
||||
"animation.dewgong.water_idle": {
|
||||
|
||||
@@ -1071,16 +1071,6 @@
|
||||
"head": {
|
||||
"rotation": ["-0.8*math.cos(q.anim_time*90*8-120)", 0, "3*math.sin(q.anim_time*90*4+70)"]
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"0.5": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.5": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"2.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"2.5": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"3.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"3.5": "q.run_molang('cobblemon:stepsound_standard_large');"
|
||||
}
|
||||
},
|
||||
"animation.rhyhorn.ride_ground_walk": {
|
||||
@@ -1927,16 +1917,6 @@
|
||||
],
|
||||
"position": [0, "0.4+0.6*math.cos(q.anim_time*90*8-120)", 0]
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"0.375": "q.run_molang('cobblemon:stepsound_standard_medium');",
|
||||
"0.5": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"0.875": "q.run_molang('cobblemon:stepsound_standard_medium');",
|
||||
"1.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.375": "q.run_molang('cobblemon:stepsound_standard_medium');",
|
||||
"1.5": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.875": "q.run_molang('cobblemon:stepsound_standard_medium');"
|
||||
}
|
||||
},
|
||||
"animation.rhyhorn.ride_ground_run": {
|
||||
|
||||
@@ -329,12 +329,6 @@
|
||||
"locator_seat_1": {
|
||||
"rotation": [-15, 0, 0]
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"2.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"3.0": "q.run_molang('cobblemon:stepsound_standard_large');"
|
||||
}
|
||||
},
|
||||
"animation.rhydon.ride_ground_walk": {
|
||||
@@ -813,12 +807,6 @@
|
||||
"locator_seat_1": {
|
||||
"rotation": [-15, 0, 0]
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.3333": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"0.8333": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.3333": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.8333": "q.run_molang('cobblemon:stepsound_standard_large');"
|
||||
}
|
||||
},
|
||||
"animation.rhydon.ride_ground_run": {
|
||||
|
||||
@@ -391,10 +391,6 @@
|
||||
"1.0": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.0833": "q.run_molang('cobblemon:stepsound_minecraft');",
|
||||
"0.5833": "q.run_molang('cobblemon:stepsound_minecraft');"
|
||||
}
|
||||
},
|
||||
"animation.mr_mime.ground_run": {
|
||||
|
||||
@@ -800,6 +800,8 @@
|
||||
"block.cobblemon.relic_coin_sack": "Relic Coin Sack",
|
||||
"item.cobblemon.automaton_armor_trim_smithing_template": "Smithing Template",
|
||||
"trim_pattern.cobblemon.automaton": "Automaton Armor Trim",
|
||||
"item.cobblemon.trainer_pack": "Trainer Pack",
|
||||
"item.cobblemon.trainer_bag": "Trainer Bag",
|
||||
"cobblemon.tooltip.see_more_info": "Hold shift to see info",
|
||||
"cobblemon.item_class.fishing_bait": "Fishing Bait",
|
||||
"cobblemon.item_class.cooking_pot_recipe_absorbs": "Absorbs",
|
||||
@@ -1198,7 +1200,14 @@
|
||||
"cobblemon.ui.edit.habitat.validation.phases_list": "Phases must use a valid format for integer ranges, e.g. 1-3 or 1, 3-5, 6.",
|
||||
"cobblemon.ui.edit.habitat.validation.min_light": "Min Light must be between 0 and 15.",
|
||||
"cobblemon.ui.edit.habitat.validation.max_light": "Max Light must be between 0 and 15.",
|
||||
|
||||
"cobblemon.container.bag": "Trainer Bag",
|
||||
"cobblemon.bag_category.poke_balls": "Poké Balls",
|
||||
"cobblemon.bag_category.consumables": "Consumables",
|
||||
"cobblemon.bag_category.berries": "Berries",
|
||||
"cobblemon.bag_category.held_evo": "Held and Evolution Items",
|
||||
"cobblemon.bag_category.tms": "Technical Machines",
|
||||
"cobblemon.bag_category.currency": "Currency",
|
||||
"cobblemon.bag_categories.all_items": "Bag",
|
||||
|
||||
"cobblemon.ui.pokenav.title": "PokéNav",
|
||||
"cobblemon.ui.pokenav.narrator.backbutton": "PokéNav button pressed",
|
||||
@@ -1473,9 +1482,6 @@
|
||||
"cobblemon.ui.pokedex.weight": "Weight: %1$skg",
|
||||
"cobblemon.ui.pokedex.height": "Height: %1$sm",
|
||||
|
||||
"cobblemon.ui.pokedex.scan.berry.stage": "Stage: %s",
|
||||
"cobblemon.ui.pokedex.scan.berry.mulch_none": "No Mulch",
|
||||
|
||||
"cobblemon.ui.pokedex.evolution.item_interact.tooltip": "Needs %1$s item to evolve",
|
||||
"cobblemon.ui.pokedex.evolution.trade.tooltip.with_species": "Evolves when traded with %1$s",
|
||||
"cobblemon.ui.pokedex.evolution.trade.tooltip.with_any_species": "Evolves when traded",
|
||||
@@ -2437,8 +2443,6 @@
|
||||
"cobblemon.config.ui.max_pokemon_level.tooltip": "The maximum level Pokémon can get to.",
|
||||
"cobblemon.config.ui.max_pokemon_friendship": "Max Pokémon Friendship",
|
||||
"cobblemon.config.ui.max_pokemon_friendship.tooltip": "The maximum friendship value.",
|
||||
"cobblemon.config.ui.power_item_ride_stat_gain_interval_seconds": "Power Item Riding Stat Training Interval (Seconds)",
|
||||
"cobblemon.config.ui.power_item_ride_stat_gain_interval_seconds.tooltip": "Seconds between riding stat gains while riding a Pokémon holding a Power item. Defaults to 120 seconds. Set to -1 or 0 to disable; values below -1 are clamped to -1.",
|
||||
"cobblemon.config.ui.announce_drop_items": "Announce Drop Items",
|
||||
"cobblemon.config.ui.announce_drop_items.tooltip": "Whether to notify the Player of dropped items from defeating wild Pokémon.",
|
||||
"cobblemon.config.ui.default_drop_item_method": "Default Drop Item Method",
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
{
|
||||
"format_version": "1.9.0",
|
||||
"credit": "Made with Blockbench",
|
||||
"texture_size": [64, 64],
|
||||
"textures": {
|
||||
"particle": "cobblemon:item/bag/bag_penny",
|
||||
"texture": "cobblemon:item/bag/bag_penny"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"name": "straps",
|
||||
"from": [3.65, 11.65, 5.65],
|
||||
"to": [12.35, 24.35, 10.35],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [1, 1, 3, 4], "texture": "#texture"},
|
||||
"east": {"uv": [0, 1, 1, 4], "texture": "#texture"},
|
||||
"south": {"uv": [4, 1, 6, 4], "texture": "#texture"},
|
||||
"west": {"uv": [3, 1, 4, 4], "texture": "#texture"},
|
||||
"up": {"uv": [3, 1, 1, 0], "texture": "#texture"},
|
||||
"down": {"uv": [5, 0, 3, 1], "texture": "#texture"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bag",
|
||||
"from": [4.51, 19.01, 10.01],
|
||||
"to": [11.49, 21.99, 12.99],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 19.5, 11.5]},
|
||||
"faces": {
|
||||
"north": {"uv": [6.75, 2.5, 8.5, 3.25], "texture": "#texture"},
|
||||
"east": {"uv": [6, 2.5, 6.75, 3.25], "texture": "#texture"},
|
||||
"south": {"uv": [9.25, 2.5, 11, 3.25], "texture": "#texture"},
|
||||
"west": {"uv": [8.5, 2.5, 9.25, 3.25], "texture": "#texture"},
|
||||
"up": {"uv": [8.5, 2.5, 6.75, 1.75], "texture": "#texture"},
|
||||
"down": {"uv": [10.25, 1.75, 8.5, 2.5], "texture": "#texture"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bag",
|
||||
"from": [1.99, 21.99, 12.49],
|
||||
"to": [14.01, 26.01, 12.51],
|
||||
"rotation": {"angle": 22.5, "axis": "x", "origin": [8, 22, 12.5]},
|
||||
"faces": {
|
||||
"north": {"uv": [7.5, 3.75, 10.5, 4.75], "texture": "#texture"},
|
||||
"east": {"uv": [7.5, 3.75, 7.5, 4.75], "texture": "#texture"},
|
||||
"south": {"uv": [10.5, 3.75, 13.5, 4.75], "texture": "#texture"},
|
||||
"west": {"uv": [10.5, 3.75, 10.5, 4.75], "texture": "#texture"},
|
||||
"up": {"uv": [10.5, 3.75, 7.5, 3.75], "texture": "#texture"},
|
||||
"down": {"uv": [13.5, 3.75, 10.5, 3.75], "texture": "#texture"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bag",
|
||||
"from": [4.51, 12.51, 10.01],
|
||||
"to": [11.49, 16.49, 12.99],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 14, 11.5]},
|
||||
"faces": {
|
||||
"north": {"uv": [6.75, 0.75, 8.5, 1.75], "texture": "#texture"},
|
||||
"east": {"uv": [6, 0.75, 6.75, 1.75], "texture": "#texture"},
|
||||
"south": {"uv": [9.25, 0.75, 11, 1.75], "texture": "#texture"},
|
||||
"west": {"uv": [8.5, 0.75, 9.25, 1.75], "texture": "#texture"},
|
||||
"up": {"uv": [8.5, 0.75, 6.75, 0], "texture": "#texture"},
|
||||
"down": {"uv": [10.25, 0, 8.5, 0.75], "texture": "#texture"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bag",
|
||||
"from": [4.99, 11.49, 11.99],
|
||||
"to": [11.01, 12.51, 12.01],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 12, 11]},
|
||||
"faces": {
|
||||
"north": {"uv": [6, 5, 7.5, 5.25], "texture": "#texture"},
|
||||
"east": {"uv": [6, 5, 6, 5.25], "texture": "#texture"},
|
||||
"south": {"uv": [7.5, 5, 9, 5.25], "texture": "#texture"},
|
||||
"west": {"uv": [7.5, 5, 7.5, 5.25], "texture": "#texture"},
|
||||
"up": {"uv": [7.5, 5, 6, 5], "texture": "#texture"},
|
||||
"down": {"uv": [9, 5, 7.5, 5], "texture": "#texture"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bag",
|
||||
"from": [5.99, 8.49, 10.99],
|
||||
"to": [10.01, 12.51, 11.01],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 10, 10.5]},
|
||||
"faces": {
|
||||
"north": {"uv": [6, 5.5, 7, 6.5], "texture": "#texture"},
|
||||
"east": {"uv": [6, 5.5, 6, 6.5], "texture": "#texture"},
|
||||
"south": {"uv": [7, 5.5, 8, 6.5], "texture": "#texture"},
|
||||
"west": {"uv": [7, 5.5, 7, 6.5], "texture": "#texture"},
|
||||
"up": {"uv": [7, 5.5, 6, 5.5], "texture": "#texture"},
|
||||
"down": {"uv": [8, 5.5, 7, 5.5], "texture": "#texture"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bag",
|
||||
"from": [4.01, 16.01, 9.51],
|
||||
"to": [11.99, 18.99, 13.49],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 16.5, 11]},
|
||||
"faces": {
|
||||
"north": {"uv": [1, 5, 3, 5.75], "texture": "#texture"},
|
||||
"east": {"uv": [0, 5, 1, 5.75], "texture": "#texture"},
|
||||
"south": {"uv": [4, 5, 6, 5.75], "texture": "#texture"},
|
||||
"west": {"uv": [3, 5, 4, 5.75], "texture": "#texture"},
|
||||
"up": {"uv": [3, 5, 1, 4], "texture": "#texture"},
|
||||
"down": {"uv": [5, 4, 3, 5], "texture": "#texture"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bag",
|
||||
"from": [3.8, 15.8, 9.3],
|
||||
"to": [12.2, 19.2, 13.7],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 16.5, 11]},
|
||||
"faces": {
|
||||
"north": {"uv": [1, 6.75, 3, 7.5], "texture": "#texture"},
|
||||
"east": {"uv": [0, 6.75, 1, 7.5], "texture": "#texture"},
|
||||
"south": {"uv": [4, 6.75, 6, 7.5], "texture": "#texture"},
|
||||
"west": {"uv": [3, 6.75, 4, 7.5], "texture": "#texture"},
|
||||
"up": {"uv": [3, 6.75, 1, 5.75], "texture": "#texture"},
|
||||
"down": {"uv": [5, 5.75, 3, 6.75], "texture": "#texture"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"name": "bag",
|
||||
"origin": [0, 0, 0],
|
||||
"color": 0,
|
||||
"children": [0, 1, 2, 3, 4, 5, 6, 7]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"format_version": "1.9.0",
|
||||
"credit": "Made with Blockbench",
|
||||
"texture_size": [32, 32],
|
||||
"textures": {
|
||||
"0": "cobblemon:item/bag/trainer_bag",
|
||||
"particle": "cobblemon:item/bag/trainer_bag"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"name": "straps",
|
||||
"from": [3.65, 11.65, 5.65],
|
||||
"to": [12.35, 24.35, 10.35],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [2, 2, 6, 8], "texture": "#0"},
|
||||
"east": {"uv": [0, 2, 2, 8], "texture": "#0"},
|
||||
"south": {"uv": [8, 2, 12, 8], "texture": "#0"},
|
||||
"west": {"uv": [6, 2, 8, 8], "texture": "#0"},
|
||||
"up": {"uv": [6, 2, 2, 0], "texture": "#0"},
|
||||
"down": {"uv": [10, 0, 6, 2], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "straps inverted",
|
||||
"from": [12.35, 24.35, 10.35],
|
||||
"to": [3.65, 11.65, 5.65],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [8, 8, 12, 2], "texture": "#0"},
|
||||
"east": {"uv": [6, 8, 8, 2], "texture": "#0"},
|
||||
"south": {"uv": [2, 8, 6, 2], "texture": "#0"},
|
||||
"west": {"uv": [0, 8, 2, 2], "texture": "#0"},
|
||||
"up": {"uv": [6, 0, 10, 2], "texture": "#0"},
|
||||
"down": {"uv": [2, 2, 6, 0], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bag",
|
||||
"from": [4, 14, 10],
|
||||
"to": [12, 20, 13],
|
||||
"rotation": {"angle": 22.5, "axis": "z", "origin": [8, 17, 11.5]},
|
||||
"faces": {
|
||||
"north": {"uv": [1.5, 9.5, 5.5, 12.5], "texture": "#0"},
|
||||
"east": {"uv": [0, 9.5, 1.5, 12.5], "texture": "#0"},
|
||||
"south": {"uv": [7, 9.5, 11, 12.5], "texture": "#0"},
|
||||
"west": {"uv": [5.5, 9.5, 7, 12.5], "texture": "#0"},
|
||||
"up": {"uv": [5.5, 9.5, 1.5, 8], "texture": "#0"},
|
||||
"down": {"uv": [9.5, 8, 5.5, 9.5], "texture": "#0"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"gui_light": "front",
|
||||
"groups": [
|
||||
{
|
||||
"name": "bag",
|
||||
"origin": [8, 0, 8],
|
||||
"scope": 0,
|
||||
"color": 0,
|
||||
"children": [0, 1, 2]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"format_version": "1.9.0",
|
||||
"credit": "Made with Blockbench",
|
||||
"texture_size": [64, 32],
|
||||
"textures": {
|
||||
"0": "cobblemon:item/bag/trainer_pack",
|
||||
"particle": "cobblemon:item/bag/trainer_pack"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"name": "straps",
|
||||
"from": [3.65, 11.65, 5.65],
|
||||
"to": [12.35, 24.35, 10.35],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [1, 2, 3, 8], "texture": "#0"},
|
||||
"east": {"uv": [0, 2, 1, 8], "texture": "#0"},
|
||||
"south": {"uv": [4, 2, 6, 8], "texture": "#0"},
|
||||
"west": {"uv": [3, 2, 4, 8], "texture": "#0"},
|
||||
"up": {"uv": [3, 2, 1, 0], "texture": "#0"},
|
||||
"down": {"uv": [5, 0, 3, 2], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "straps inverted",
|
||||
"from": [12.35, 24.35, 10.35],
|
||||
"to": [3.65, 11.65, 5.65],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 8, 6, 2], "texture": "#0"},
|
||||
"east": {"uv": [3, 8, 4, 2], "texture": "#0"},
|
||||
"south": {"uv": [1, 8, 3, 2], "texture": "#0"},
|
||||
"west": {"uv": [0, 8, 1, 2], "texture": "#0"},
|
||||
"up": {"uv": [3, 0, 5, 2], "texture": "#0"},
|
||||
"down": {"uv": [1, 2, 3, 0], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bag",
|
||||
"from": [6, 21.5, 11.5],
|
||||
"to": [10, 23.5, 11.5],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 24, 11.5]},
|
||||
"faces": {
|
||||
"north": {"uv": [6.25, 5, 7.25, 6], "texture": "#0"},
|
||||
"south": {"uv": [7.25, 5, 8.25, 6], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bag",
|
||||
"from": [4.01, 16.5, 10],
|
||||
"to": [11.99, 21.5, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 15.5, 11.5]},
|
||||
"faces": {
|
||||
"north": {"uv": [7.25, 2, 9.25, 4.5], "texture": "#0"},
|
||||
"east": {"uv": [6.25, 2, 7.25, 4.5], "texture": "#0"},
|
||||
"south": {"uv": [10.25, 2, 12.25, 4.5], "texture": "#0"},
|
||||
"west": {"uv": [9.25, 2, 10.25, 4.5], "texture": "#0"},
|
||||
"up": {"uv": [9.25, 2, 7.25, 0], "texture": "#0"},
|
||||
"down": {"uv": [11.25, 0, 9.25, 2], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bag",
|
||||
"from": [3.5, 12.5, 10],
|
||||
"to": [12.5, 16.5, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 15.5, 11.5]},
|
||||
"faces": {
|
||||
"north": {"uv": [1, 14, 3.25, 16], "texture": "#0"},
|
||||
"east": {"uv": [0, 14, 1, 16], "texture": "#0"},
|
||||
"south": {"uv": [4.25, 14, 6.5, 16], "texture": "#0"},
|
||||
"west": {"uv": [3.25, 14, 4.25, 16], "texture": "#0"},
|
||||
"up": {"uv": [3.25, 14, 1, 12], "texture": "#0"},
|
||||
"down": {"uv": [5.5, 12, 3.25, 14], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bag",
|
||||
"from": [4.5, 12.5, 14],
|
||||
"to": [11.5, 16.5, 15],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 16.5, 15.5]},
|
||||
"faces": {
|
||||
"north": {"uv": [0.25, 10, 2, 12], "texture": "#0"},
|
||||
"east": {"uv": [0, 10, 0.25, 12], "texture": "#0"},
|
||||
"south": {"uv": [2.25, 10, 4, 12], "texture": "#0"},
|
||||
"west": {"uv": [2, 10, 2.25, 12], "texture": "#0"},
|
||||
"up": {"uv": [2, 10, 0.25, 9.5], "texture": "#0"},
|
||||
"down": {"uv": [3.75, 9.5, 2, 10], "texture": "#0"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"gui_light": "front",
|
||||
"groups": [
|
||||
{
|
||||
"name": "bag",
|
||||
"origin": [8, 0, 8],
|
||||
"scope": 0,
|
||||
"color": 0,
|
||||
"children": [0, 1, 2, 3, 4, 5]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"parent": "cobblemon:item/bag/trainer_bag",
|
||||
"gui_light": "front",
|
||||
"display": {
|
||||
"thirdperson_righthand": {
|
||||
"rotation": [75, -155, 0],
|
||||
"translation": [2.25, -4, -11],
|
||||
"scale": [0.75, 0.75, 0.75]
|
||||
},
|
||||
"thirdperson_lefthand": {
|
||||
"rotation": [75, -155, 0],
|
||||
"translation": [2.25, -4, -11],
|
||||
"scale": [0.75, 0.75, 0.75]
|
||||
},
|
||||
"firstperson_righthand": {
|
||||
"rotation": [0, -100, 0],
|
||||
"translation": [5, -7, 0],
|
||||
"scale": [0.8, 0.8, 0.8]
|
||||
},
|
||||
"firstperson_lefthand": {
|
||||
"rotation": [0, -100, 0],
|
||||
"translation": [5, -7, 0],
|
||||
"scale": [0.8, 0.8, 0.8]
|
||||
},
|
||||
"ground": {
|
||||
"translation": [0, -3, -1.75],
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
},
|
||||
"gui": {
|
||||
"rotation": [30, -22.5, 0],
|
||||
"translation": [0.75, -7, 0],
|
||||
"scale": [0.875, 0.875, 0.875]
|
||||
},
|
||||
"head": {
|
||||
"translation": [0, -32.25, 0],
|
||||
"scale": [1.6, 1.6, 1.6]
|
||||
},
|
||||
"fixed": {
|
||||
"rotation": [0, -180, 0],
|
||||
"translation": [0, -9.25, 3.5]
|
||||
},
|
||||
"on_shelf": {
|
||||
"translation": [0, 0, -7.5],
|
||||
"scale": [1.75, 1.75, 1.75]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"parent": "cobblemon:item/bag/trainer_pack",
|
||||
"gui_light": "front",
|
||||
"display": {
|
||||
"thirdperson_righthand": {
|
||||
"rotation": [75, -155, 0],
|
||||
"translation": [2.25, -4, -11],
|
||||
"scale": [0.75, 0.75, 0.75]
|
||||
},
|
||||
"thirdperson_lefthand": {
|
||||
"rotation": [75, -155, 0],
|
||||
"translation": [2.25, -4, -11],
|
||||
"scale": [0.75, 0.75, 0.75]
|
||||
},
|
||||
"firstperson_righthand": {
|
||||
"rotation": [0, -100, 0],
|
||||
"translation": [5, -7, 0],
|
||||
"scale": [0.8, 0.8, 0.8]
|
||||
},
|
||||
"firstperson_lefthand": {
|
||||
"rotation": [0, -100, 0],
|
||||
"translation": [5, -7, 0],
|
||||
"scale": [0.8, 0.8, 0.8]
|
||||
},
|
||||
"ground": {
|
||||
"translation": [0, -3, -1.75],
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
},
|
||||
"gui": {
|
||||
"rotation": [30, -22.5, 0],
|
||||
"translation": [0.75, -7, 0],
|
||||
"scale": [0.875, 0.875, 0.875]
|
||||
},
|
||||
"head": {
|
||||
"translation": [0, -32.25, 0],
|
||||
"scale": [1.6, 1.6, 1.6]
|
||||
},
|
||||
"fixed": {
|
||||
"rotation": [0, -180, 0],
|
||||
"translation": [0, -9.25, 3.5]
|
||||
},
|
||||
"on_shelf": {
|
||||
"translation": [0, 0, -7.5],
|
||||
"scale": [1.75, 1.75, 1.75]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -261,6 +261,24 @@
|
||||
"fishing.bait_detach": {
|
||||
"sounds": [ { "name": "cobblemon:fishing/fishing_rod_remove_bait", "volume": 0.8 } ]
|
||||
},
|
||||
"item.bag.open": {
|
||||
"sounds": [ { "name": "cobblemon:item/bag/bag_open", "volume": 0.5 } ]
|
||||
},
|
||||
"item.bag.close": {
|
||||
"sounds": [ { "name": "cobblemon:item/bag/bag_close", "volume": 0.5 } ]
|
||||
},
|
||||
"item.bag.click": {
|
||||
"sounds": [ { "name": "cobblemon:item/bag/bag_click", "volume": 0.3 } ]
|
||||
},
|
||||
"item.bag.item_in": {
|
||||
"sounds": [ { "name": "cobblemon:item/bag/bag_item_in", "volume": 0.3 } ]
|
||||
},
|
||||
"item.bag.item_out": {
|
||||
"sounds": [ { "name": "cobblemon:item/bag/bag_item_out", "volume": 0.3 } ]
|
||||
},
|
||||
"item.bag.equip": {
|
||||
"sounds": [ { "name": "cobblemon:item/bag/bag_equip", "volume": 0.4 } ]
|
||||
},
|
||||
"block.healing_machine.active": {
|
||||
"sounds": [ { "name": "cobblemon:block/healing_machine/healing_machine_active", "volume": 0.7 } ]
|
||||
},
|
||||
@@ -1653,18 +1671,6 @@
|
||||
"move.horndrill.target_2": {
|
||||
"sounds": [ { "name": "cobblemon:move/horndrill/horndrill_target_2", "volume": 1.0 } ]
|
||||
},
|
||||
"move.gigatonhammer.actor": {
|
||||
"sounds": [ { "name": "cobblemon:move/gigatonhammer/gigatonhammer_actor", "volume": 1.0 } ]
|
||||
},
|
||||
"move.gigatonhammer.target": {
|
||||
"sounds": [ { "name": "cobblemon:move/gigatonhammer/gigatonhammer_target", "volume": 1.0 } ]
|
||||
},
|
||||
"move.pyroball.actor": {
|
||||
"sounds": [ { "name": "cobblemon:move/pyroball/pyroball_actor", "volume": 1.0 } ]
|
||||
},
|
||||
"move.pyroball.target": {
|
||||
"sounds": [ { "name": "cobblemon:move/pyroball/pyroball_target", "volume": 1.0 } ]
|
||||
},
|
||||
"impact.normal": {
|
||||
"sounds": [
|
||||
{ "name": "cobblemon:move/impact_generic/impact_generic_fighting_1", "volume": 0.8 },
|
||||
@@ -8684,9 +8690,6 @@
|
||||
"pokemon.cinderace.cry": {
|
||||
"sounds": [ { "name": "cobblemon:pokemon/cinderace/cinderace_cry", "volume": 0.8 } ]
|
||||
},
|
||||
"pokemon.cinderace.pyroball": {
|
||||
"sounds": [ { "name": "cobblemon:pokemon/cinderace/cinderace_pyroball", "volume": 0.5 } ]
|
||||
},
|
||||
"pokemon.sobble.cry": {
|
||||
"sounds": [ { "name": "cobblemon:pokemon/sobble/sobble_cry", "volume": 0.8 } ]
|
||||
},
|
||||
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 420 B |
|
After Width: | Height: | Size: 126 B |
|
After Width: | Height: | Size: 373 B |
|
After Width: | Height: | Size: 384 B |
|
After Width: | Height: | Size: 266 B |
|
After Width: | Height: | Size: 347 B |
|
After Width: | Height: | Size: 174 B |
|
After Width: | Height: | Size: 291 B |
|
After Width: | Height: | Size: 251 B |
|
After Width: | Height: | Size: 196 B |
|
After Width: | Height: | Size: 206 B |
|
After Width: | Height: | Size: 199 B |
|
After Width: | Height: | Size: 207 B |
|
After Width: | Height: | Size: 214 B |