Compare commits

..
Author SHA1 Message Date
Polymeta da76b2fb94 license
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-31 18:24:08 +00:00
Polymeta 87d8197dd0 Merge branch 'bag-of-plenty' into 'version/1.9'
Bag

See merge request cable-mc/cobblemon!2433
2026-10-30 23:58:31 +00:00
Polymeta 2ed6f89d09 [skip-ci] changelog cleanup
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-30 23:57:57 +00:00
Polymeta 5947468cd9 Merge branch 'version/1.9' into bag-of-plenty
# Conflicts:
#	common/src/main/java/com/cobblemon/mod/common/mixin/PlayerMixin.java
#	common/src/main/java/com/cobblemon/mod/common/mixin/ServerPlayerMixin.java
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/world/gamerules/CobblemonGameRules.kt
#	common/src/main/resources/cobblemon-common.accesswidener
2026-10-30 23:55:24 +00:00
Polymeta 7ba1507ac8 [skip-ci] changelog cleanup
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-30 23:51:39 +00:00
Polymeta 9fdffdc392 [skip-ci] changelog cleanup
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-30 23:51:03 +00:00
Polymeta 6a01fe3863 Merge branch 'feature/more-trades' into 'version/1.9'
Feature/more trades

See merge request cable-mc/cobblemon!2208
2026-10-30 23:50:51 +00:00
Polymeta 9b5a153e3f Merge branch 'main' into version/1.9
# Conflicts:
#	gradle.properties
2026-10-30 23:48:34 +00:00
whatsy def975c03f Add bag sounds 2026-10-17 19:08:15 -04:00
Sterrezo e67567e8dc Update CobblemonSounds.kt 2026-10-17 23:25:34 +02:00
Sterrezo 23a42a29b3 Added bag sounds for implementation 2026-10-17 23:22:19 +02:00
saultsc 2e88d971f7 docs: detail multi-Pokémon trading and trade UI changes in the changelog 2026-10-04 14:34:16 -04:00
saultsc bfc1c66097 fix: log trade start at info level instead of error 2026-10-04 14:34:16 -04:00
saultscandClaude Opus 5.5 13d9f84d8d Merge branch 'main' into feature/more-trades
Resolve conflicts with the trade dupe fix and per-OT trade friendship
caching by applying both to every Pokémon in a multi-Pokémon trade.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 10:55:28 -04:00
saultsc 148eb130d9 ci: install findutils in pipeline jobs and sync CI config with main 2026-10-04 10:11:00 -04:00
saultsc eb07b0e03f docs: document trade evolutions checking every Pokémon received in the changelog 2026-10-04 10:05:51 -04:00
saultsc 1b8d416962 fix: evaluate trade evolutions against every Pokémon received in a multi-Pokémon trade 2026-10-04 10:05:27 -04:00
saultsc 3030b544b5 refactor: migrate trade advancement and stat handlers to the list-based TradeEvent API 2026-10-04 10:05:21 -04:00
saultsc b890d662f6 feature: include every traded Pokémon in TradeEvent.Pre and TradeEvent.Post 2026-10-04 10:05:20 -04:00
saultsc 2a9bbc1d68 fix: log a warning when a trade is performed with an empty offer 2026-10-04 10:05:13 -04:00
dhi_holo 735817869a Forgot a rename 2026-09-28 16:34:56 +10:00
dhi_holo 488e6dcf7a ID Corrections and Recipes 2026-09-28 16:16:51 +10:00
dhi_holo a0f32ea444 Models and Translations 2026-09-28 15:19:53 +10:00
Gertimoshka 182e06205d Idiocity Fixes 2026-09-27 13:59:26 +02:00
Gertimoshka 4c391645fe Bag Drop Gamerule to drop all bag items, plus some rendering changes 2026-09-27 13:18:03 +02:00
Gertimoshka 79bf50004a Merge branch 'main' into bag-of-plenty 2026-09-27 11:51:06 +02:00
Paul 396ab13164 1.9 in gradle properties 2026-09-06 20:01:10 +10:00
saultsc c10c91d86a changes add to changelog 2026-04-23 11:21:30 -04:00
saultsc d5fd2e9a07 fix to reorder now is good :) 2026-04-22 22:37:24 -04:00
saultsc 45cc0ba579 trader hover select poing more tall z 2026-04-22 22:12:38 -04:00
saultsc 1880a0a24e reorder the party if you have a blank space in the middle 2026-04-22 19:59:55 -04:00
saultsc 5f3a0b96cc summary show is changed for a hover 2026-04-22 19:46:11 -04:00
saultsc 7ab1610700 remove the hover to the opposing party 2026-04-20 23:06:50 -04:00
saultsc daad9f6c00 add trade party selected assets and apply in trade ui 2026-04-20 22:52:08 -04:00
saultsc 2b234b740e trade animation directed towards the pokeball 2026-04-20 21:49:14 -04:00
saultsc 39e3983020 Prevent exiting summary view when clicking the active Pokémon 2026-04-20 21:17:20 -04:00
saultsc 9d58cdb21c deleting a suspicius comment 2026-04-20 03:07:18 -04:00
saultsc f05e35e792 Improve trade GUI interaction, modify summary interaction style 2026-04-20 02:59:10 -04:00
saultsc a333e68620 Update trade network packets and handlers 2026-04-20 02:58:15 -04:00
saultsc 2c2791ec69 Refactor trade list<Pokemon> flow on server 2026-04-20 02:57:13 -04:00
terrainwax 635b09d82f add bag rendering 2026-04-16 17:22:45 +02:00
Gertimoshka 46c3ff9db4 first attempt to make bag render 2026-04-16 16:51:01 +03:00
Gertimoshka 8bfc95c23a Items now render in multiple stacks then there are several stacks worth of same item 2026-04-16 10:41:11 +03:00
Gertimoshka 99d05b6ef7 More Tag Fixes and TM Category 2026-04-16 09:51:48 +03:00
Gertimoshka 1c0a14c819 Merging 1.8.0 into bag-of-plenty 2026-04-15 23:30:11 +03:00
Gertimoshka fe5cd58801 Bag Categories are now Datapackable 2026-04-15 23:24:39 +03:00
Gertimoshka c3bd007f5a Party Slots now render in the GUI, and held items can be given and taken, and items can be used on the pokemon from the GUI 2026-04-13 11:12:46 +03:00
Gertimoshka dba24e104e Reduced unnecessary nbt data saved for bag 2026-04-10 22:40:56 +03:00
Gertimoshka 35815e371b Bag can now only hold a specific amount of items based on Categories 2026-04-10 22:26:50 +03:00
Gertimoshka d0f8ae0565 Categories Filtering and Scrollbar Work 2026-04-10 19:13:59 +03:00
Gertimoshka b2e21969ca Partial Implementation of Bag Categories 2026-04-10 00:59:31 +03:00
Gertimoshka ea57c233a3 Bag data is now stored on player and saved in the nbt 2026-04-08 23:06:46 +03:00
Gertimoshka 0d39b47f22 Bag Container now properly moves items in and out of Container 2026-04-08 21:08:01 +03:00
Gertimoshka 9f3a279b03 The hotmess of slotless container 2026-04-07 12:00:50 +03:00
425 changed files with 3535 additions and 12133 deletions

No files matched your search

+21 -13
View File
@@ -1,27 +1,35 @@
# Changelog
## [1.8.5 (MONTH xth, 2026)](#1-8-5)
## [1.9.0 (MONTH xth, 2026)](#1-9-0)
### Additions
- 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
-2
View File
@@ -1,2 +0,0 @@
Spec Doc: https://docs.google.com/document/d/1hEKKrT4JEagJBykx-k9K2BoeNxiMGahUWXnf8YQ85xo/edit?tab=t.0
Marshal: dhi_holo
@@ -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());
}
}
@@ -8,9 +8,6 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.block.appliances.CopperFridgeBlockEntity
import com.cobblemon.mod.common.block.appliances.CopperMowerBlockEntity
import com.cobblemon.mod.common.block.appliances.CopperWasherBlockEntity
import com.cobblemon.mod.common.block.entity.*
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
@@ -129,49 +126,4 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
CobblemonBlocks.MISTLEAF
).build(null)
)
@JvmField
val COPPER_FRIDGE: BlockEntityType<CopperFridgeBlockEntity> = this.create(
"copper_fridge",
BlockEntityType.Builder.of(::CopperFridgeBlockEntity,
CobblemonBlocks.COPPER_FRIDGE,
CobblemonBlocks.EXPOSED_COPPER_FRIDGE,
CobblemonBlocks.WEATHERED_COPPER_FRIDGE,
CobblemonBlocks.OXIDIZED_COPPER_FRIDGE,
CobblemonBlocks.WAXED_COPPER_FRIDGE,
CobblemonBlocks.WAXED_EXPOSED_COPPER_FRIDGE,
CobblemonBlocks.WAXED_WEATHERED_COPPER_FRIDGE,
CobblemonBlocks.WAXED_OXIDIZED_COPPER_FRIDGE
).build(null)
)
@JvmField
val COPPER_WASHER: BlockEntityType<CopperWasherBlockEntity> = this.create(
"copper_washer",
BlockEntityType.Builder.of(::CopperWasherBlockEntity,
CobblemonBlocks.COPPER_WASHER,
CobblemonBlocks.EXPOSED_COPPER_WASHER,
CobblemonBlocks.WEATHERED_COPPER_WASHER,
CobblemonBlocks.OXIDIZED_COPPER_WASHER,
CobblemonBlocks.WAXED_COPPER_WASHER,
CobblemonBlocks.WAXED_EXPOSED_COPPER_WASHER,
CobblemonBlocks.WAXED_WEATHERED_COPPER_WASHER,
CobblemonBlocks.WAXED_OXIDIZED_COPPER_WASHER
).build(null)
)
@JvmField
val COPPER_MOWER: BlockEntityType<CopperMowerBlockEntity> = this.create(
"copper_mower",
BlockEntityType.Builder.of(::CopperMowerBlockEntity,
CobblemonBlocks.COPPER_MOWER,
CobblemonBlocks.EXPOSED_COPPER_MOWER,
CobblemonBlocks.WEATHERED_COPPER_MOWER,
CobblemonBlocks.OXIDIZED_COPPER_MOWER,
CobblemonBlocks.WAXED_COPPER_MOWER,
CobblemonBlocks.WAXED_EXPOSED_COPPER_MOWER,
CobblemonBlocks.WAXED_WEATHERED_COPPER_MOWER,
CobblemonBlocks.WAXED_OXIDIZED_COPPER_MOWER
).build(null)
)
}
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.api.apricorn.Apricorn
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.block.*
import com.cobblemon.mod.common.block.LecternBlock
import com.cobblemon.mod.common.block.appliances.*
import com.cobblemon.mod.common.block.MintBlock.MintType
import com.cobblemon.mod.common.block.campfirepot.CampfireBlock
import com.cobblemon.mod.common.block.campfirepot.CampfirePotBlock
@@ -29,7 +28,12 @@ import com.cobblemon.mod.common.block.sign.CobblemonWallHangingSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonWallSignBlock
import com.cobblemon.mod.common.block.tmmachine.TMMachineBlock
import com.cobblemon.mod.common.block.general.MintLitterBlock
import com.cobblemon.mod.common.mixin.invoker.*
import com.cobblemon.mod.common.mixin.invoker.BlocksInvoker
import com.cobblemon.mod.common.mixin.invoker.DoorBlockInvoker
import com.cobblemon.mod.common.mixin.invoker.FireBlockInvoker
import com.cobblemon.mod.common.mixin.invoker.PressurePlateBlockInvoker
import com.cobblemon.mod.common.mixin.invoker.StairsBlockInvoker
import com.cobblemon.mod.common.mixin.invoker.TrapdoorBlockInvoker
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.core.BlockPos
@@ -817,93 +821,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val DISPLAY_CASE = create("display_case", DisplayCaseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F).requiresCorrectToolForDrops()))
//Rotom Blocks
//Oven
@JvmField
val COPPER_OVEN = create("copper_oven", WeatheringCopperOvenBlock(WeatheringCopper.WeatherState.UNAFFECTED, BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_ORANGE).requiresCorrectToolForDrops().strength(3.0f, 6.0f).sound(SoundType.COPPER).pushReaction(PushReaction.BLOCK)))
@JvmField
val EXPOSED_COPPER_OVEN = create("exposed_copper_oven", WeatheringCopperOvenBlock(WeatheringCopper.WeatherState.EXPOSED, BlockBehaviour.Properties.ofFullCopy(COPPER_OVEN).mapColor(MapColor.TERRACOTTA_LIGHT_GRAY)))
@JvmField
val WEATHERED_COPPER_OVEN = create("weathered_copper_oven", WeatheringCopperOvenBlock(WeatheringCopper.WeatherState.WEATHERED, BlockBehaviour.Properties.ofFullCopy(COPPER_OVEN).mapColor(MapColor.WARPED_STEM)))
@JvmField
val OXIDIZED_COPPER_OVEN = create("oxidized_copper_oven", WeatheringCopperOvenBlock(WeatheringCopper.WeatherState.OXIDIZED, BlockBehaviour.Properties.ofFullCopy(COPPER_OVEN).mapColor(MapColor.WARPED_NYLIUM)))
@JvmField
val WAXED_COPPER_OVEN = create("waxed_copper_oven", CopperOvenBlock(BlockBehaviour.Properties.ofFullCopy(COPPER_OVEN)))
@JvmField
val WAXED_EXPOSED_COPPER_OVEN = create("waxed_exposed_copper_oven", CopperOvenBlock(BlockBehaviour.Properties.ofFullCopy(EXPOSED_COPPER_OVEN)))
@JvmField
val WAXED_WEATHERED_COPPER_OVEN = create("waxed_weathered_copper_oven", CopperOvenBlock(BlockBehaviour.Properties.ofFullCopy(WEATHERED_COPPER_OVEN)))
@JvmField
val WAXED_OXIDIZED_COPPER_OVEN = create("waxed_oxidized_copper_oven", CopperOvenBlock(BlockBehaviour.Properties.ofFullCopy(OXIDIZED_COPPER_OVEN)))
//Washer
@JvmField
val COPPER_WASHER = create("copper_washer", WeatheringCopperWasherBlock(WeatheringCopper.WeatherState.UNAFFECTED, BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_ORANGE).requiresCorrectToolForDrops().strength(3.0f, 6.0f).sound(SoundType.COPPER).pushReaction(PushReaction.BLOCK)))
@JvmField
val EXPOSED_COPPER_WASHER = create("exposed_copper_washer", WeatheringCopperWasherBlock(WeatheringCopper.WeatherState.EXPOSED, BlockBehaviour.Properties.ofFullCopy(COPPER_WASHER).mapColor(MapColor.TERRACOTTA_LIGHT_GRAY)))
@JvmField
val WEATHERED_COPPER_WASHER = create("weathered_copper_washer", WeatheringCopperWasherBlock(WeatheringCopper.WeatherState.WEATHERED, BlockBehaviour.Properties.ofFullCopy(COPPER_WASHER).mapColor(MapColor.WARPED_STEM)))
@JvmField
val OXIDIZED_COPPER_WASHER = create("oxidized_copper_washer", WeatheringCopperWasherBlock(WeatheringCopper.WeatherState.OXIDIZED, BlockBehaviour.Properties.ofFullCopy(COPPER_WASHER).mapColor(MapColor.WARPED_NYLIUM)))
@JvmField
val WAXED_COPPER_WASHER = create("waxed_copper_washer", CopperWasherBlock(BlockBehaviour.Properties.ofFullCopy(COPPER_WASHER)))
@JvmField
val WAXED_EXPOSED_COPPER_WASHER = create("waxed_exposed_copper_washer", CopperWasherBlock(BlockBehaviour.Properties.ofFullCopy(EXPOSED_COPPER_WASHER)))
@JvmField
val WAXED_WEATHERED_COPPER_WASHER = create("waxed_weathered_copper_washer", CopperWasherBlock(BlockBehaviour.Properties.ofFullCopy(WEATHERED_COPPER_WASHER)))
@JvmField
val WAXED_OXIDIZED_COPPER_WASHER = create("waxed_oxidized_copper_washer", CopperWasherBlock(BlockBehaviour.Properties.ofFullCopy(OXIDIZED_COPPER_WASHER)))
//Fan
@JvmField
val COPPER_FAN = create("copper_fan", WeatheringCopperFanBlock(WeatheringCopper.WeatherState.UNAFFECTED, BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_ORANGE).requiresCorrectToolForDrops().strength(3.0f, 6.0f).sound(SoundType.COPPER).pushReaction(PushReaction.DESTROY)))
@JvmField
val EXPOSED_COPPER_FAN = create("exposed_copper_fan", WeatheringCopperFanBlock(WeatheringCopper.WeatherState.EXPOSED, BlockBehaviour.Properties.ofFullCopy(COPPER_FAN).mapColor(MapColor.TERRACOTTA_LIGHT_GRAY)))
@JvmField
val WEATHERED_COPPER_FAN = create("weathered_copper_fan", WeatheringCopperFanBlock(WeatheringCopper.WeatherState.WEATHERED, BlockBehaviour.Properties.ofFullCopy(COPPER_FAN).mapColor(MapColor.WARPED_STEM)))
@JvmField
val OXIDIZED_COPPER_FAN = create("oxidized_copper_fan", WeatheringCopperFanBlock(WeatheringCopper.WeatherState.OXIDIZED, BlockBehaviour.Properties.ofFullCopy(COPPER_FAN).mapColor(MapColor.WARPED_NYLIUM)))
@JvmField
val WAXED_COPPER_FAN = create("waxed_copper_fan", CopperFanBlock(BlockBehaviour.Properties.ofFullCopy(COPPER_FAN)))
@JvmField
val WAXED_EXPOSED_COPPER_FAN = create("waxed_exposed_copper_fan", CopperFanBlock(BlockBehaviour.Properties.ofFullCopy(EXPOSED_COPPER_FAN)))
@JvmField
val WAXED_WEATHERED_COPPER_FAN = create("waxed_weathered_copper_fan", CopperFanBlock(BlockBehaviour.Properties.ofFullCopy(WEATHERED_COPPER_FAN)))
@JvmField
val WAXED_OXIDIZED_COPPER_FAN = create("waxed_oxidized_copper_fan", CopperFanBlock(BlockBehaviour.Properties.ofFullCopy(OXIDIZED_COPPER_FAN)))
//Mower
@JvmField
val COPPER_MOWER = create("copper_mower", WeatheringCopperMowerBlock(WeatheringCopper.WeatherState.UNAFFECTED, BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_ORANGE).requiresCorrectToolForDrops().strength(3.0f, 6.0f).sound(SoundType.COPPER).pushReaction(PushReaction.NORMAL)))
@JvmField
val EXPOSED_COPPER_MOWER = create("exposed_copper_mower", WeatheringCopperMowerBlock(WeatheringCopper.WeatherState.EXPOSED, BlockBehaviour.Properties.ofFullCopy(COPPER_MOWER).mapColor(MapColor.TERRACOTTA_LIGHT_GRAY)))
@JvmField
val WEATHERED_COPPER_MOWER = create("weathered_copper_mower", WeatheringCopperMowerBlock(WeatheringCopper.WeatherState.WEATHERED, BlockBehaviour.Properties.ofFullCopy(COPPER_MOWER).mapColor(MapColor.WARPED_STEM)))
@JvmField
val OXIDIZED_COPPER_MOWER = create("oxidized_copper_mower", WeatheringCopperMowerBlock(WeatheringCopper.WeatherState.OXIDIZED, BlockBehaviour.Properties.ofFullCopy(COPPER_MOWER).mapColor(MapColor.WARPED_NYLIUM)))
@JvmField
val WAXED_COPPER_MOWER = create("waxed_copper_mower", CopperMowerBlock(BlockBehaviour.Properties.ofFullCopy(COPPER_MOWER)))
@JvmField
val WAXED_EXPOSED_COPPER_MOWER = create("waxed_exposed_copper_mower", CopperMowerBlock(BlockBehaviour.Properties.ofFullCopy(EXPOSED_COPPER_MOWER)))
@JvmField
val WAXED_WEATHERED_COPPER_MOWER = create("waxed_weathered_copper_mower", CopperMowerBlock(BlockBehaviour.Properties.ofFullCopy(WEATHERED_COPPER_MOWER)))
@JvmField
val WAXED_OXIDIZED_COPPER_MOWER = create("waxed_oxidized_copper_mower", CopperMowerBlock(BlockBehaviour.Properties.ofFullCopy(OXIDIZED_COPPER_MOWER)))
//Fridge
@JvmField
val COPPER_FRIDGE = create("copper_fridge", WeatheringCopperFridgeBlock(WeatheringCopper.WeatherState.UNAFFECTED, BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_ORANGE).requiresCorrectToolForDrops().strength(3.0f, 6.0f).sound(SoundType.COPPER).pushReaction(PushReaction.BLOCK)))
@JvmField
val EXPOSED_COPPER_FRIDGE = create("exposed_copper_fridge", WeatheringCopperFridgeBlock(WeatheringCopper.WeatherState.EXPOSED, BlockBehaviour.Properties.ofFullCopy(COPPER_FRIDGE).mapColor(MapColor.TERRACOTTA_LIGHT_GRAY)))
@JvmField
val WEATHERED_COPPER_FRIDGE = create("weathered_copper_fridge", WeatheringCopperFridgeBlock(WeatheringCopper.WeatherState.WEATHERED, BlockBehaviour.Properties.ofFullCopy(COPPER_FRIDGE).mapColor(MapColor.WARPED_STEM)))
@JvmField
val OXIDIZED_COPPER_FRIDGE = create("oxidized_copper_fridge", WeatheringCopperFridgeBlock(WeatheringCopper.WeatherState.OXIDIZED, BlockBehaviour.Properties.ofFullCopy(COPPER_FRIDGE).mapColor(MapColor.WARPED_NYLIUM)))
@JvmField
val WAXED_COPPER_FRIDGE = create("waxed_copper_fridge", CopperFridgeBlock(BlockBehaviour.Properties.ofFullCopy(COPPER_FRIDGE)))
@JvmField
val WAXED_EXPOSED_COPPER_FRIDGE = create("waxed_exposed_copper_fridge", CopperFridgeBlock(BlockBehaviour.Properties.ofFullCopy(EXPOSED_COPPER_FRIDGE)))
@JvmField
val WAXED_WEATHERED_COPPER_FRIDGE = create("waxed_weathered_copper_fridge", CopperFridgeBlock(BlockBehaviour.Properties.ofFullCopy(WEATHERED_COPPER_FRIDGE)))
@JvmField
val WAXED_OXIDIZED_COPPER_FRIDGE = create("waxed_oxidized_copper_fridge", CopperFridgeBlock(BlockBehaviour.Properties.ofFullCopy(OXIDIZED_COPPER_FRIDGE)))
// TODO after 1.7
// @JvmField
// val INCENSE_SWEET = create("incense_sweet", SweetIncenseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F)))
@@ -932,6 +849,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val GALARICA_NUT_BUSH = this.create("galarica_nut_bush", NutBushBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).randomTicks().noCollission().sound(SoundType.SWEET_BERRY_BUSH).pushReaction(PushReaction.DESTROY)))
@JvmField
val RED_MINT_LEAF_LITTER = this.mintLitter("red_mint_leaf_litter", MapColor.COLOR_RED)
@JvmField
@@ -1009,57 +927,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
SACCHARINE_LOG to STRIPPED_SACCHARINE_LOG,
SACCHARINE_LOG_SLATHERED to STRIPPED_SACCHARINE_LOG
)
/**Same idea as above, but for Copper Oxidation over time (or when inverted, scraping with an axe)*/
fun oxidizingBlocks(): Map<Block, Block> = mapOf(
//Ovens
COPPER_OVEN to EXPOSED_COPPER_OVEN,
EXPOSED_COPPER_OVEN to WEATHERED_COPPER_OVEN,
WEATHERED_COPPER_OVEN to OXIDIZED_COPPER_OVEN,
//Washers
COPPER_WASHER to EXPOSED_COPPER_WASHER,
EXPOSED_COPPER_WASHER to WEATHERED_COPPER_WASHER,
WEATHERED_COPPER_WASHER to OXIDIZED_COPPER_WASHER,
//Fans
COPPER_FAN to EXPOSED_COPPER_FAN,
EXPOSED_COPPER_FAN to WEATHERED_COPPER_FAN,
WEATHERED_COPPER_FAN to OXIDIZED_COPPER_FAN,
//Fridges
COPPER_FRIDGE to EXPOSED_COPPER_FRIDGE,
EXPOSED_COPPER_FRIDGE to WEATHERED_COPPER_FRIDGE,
WEATHERED_COPPER_FRIDGE to OXIDIZED_COPPER_FRIDGE,
//Mowers
COPPER_MOWER to EXPOSED_COPPER_MOWER,
EXPOSED_COPPER_MOWER to WEATHERED_COPPER_MOWER,
WEATHERED_COPPER_MOWER to OXIDIZED_COPPER_MOWER
)
/**Same idea as above, but for waxing blocks with Honeycomb (or unwaxing them with an axe)*/
fun waxingBlocks(): Map<Block, Block> = mapOf(
//Ovens
COPPER_OVEN to WAXED_COPPER_OVEN,
EXPOSED_COPPER_OVEN to WAXED_EXPOSED_COPPER_OVEN,
WEATHERED_COPPER_OVEN to WAXED_WEATHERED_COPPER_OVEN,
OXIDIZED_COPPER_OVEN to WAXED_OXIDIZED_COPPER_OVEN,
//Washers
COPPER_WASHER to WAXED_COPPER_WASHER,
EXPOSED_COPPER_WASHER to WAXED_EXPOSED_COPPER_WASHER,
WEATHERED_COPPER_WASHER to WAXED_WEATHERED_COPPER_WASHER,
OXIDIZED_COPPER_WASHER to WAXED_OXIDIZED_COPPER_WASHER,
//Fans
COPPER_FAN to WAXED_COPPER_FAN,
EXPOSED_COPPER_FAN to WAXED_EXPOSED_COPPER_FAN,
WEATHERED_COPPER_FAN to WAXED_WEATHERED_COPPER_FAN,
OXIDIZED_COPPER_FAN to WAXED_OXIDIZED_COPPER_FAN,
//Fridges
COPPER_FRIDGE to WAXED_COPPER_FRIDGE,
EXPOSED_COPPER_FRIDGE to WAXED_EXPOSED_COPPER_FRIDGE,
WEATHERED_COPPER_FRIDGE to WAXED_WEATHERED_COPPER_FRIDGE,
OXIDIZED_COPPER_FRIDGE to WAXED_OXIDIZED_COPPER_FRIDGE,
//Mowers
COPPER_MOWER to WAXED_COPPER_MOWER,
EXPOSED_COPPER_MOWER to WAXED_EXPOSED_COPPER_MOWER,
WEATHERED_COPPER_MOWER to WAXED_WEATHERED_COPPER_MOWER,
OXIDIZED_COPPER_MOWER to WAXED_OXIDIZED_COPPER_MOWER
)
private fun apricornBlock(name: String, apricorn: Apricorn): ApricornBlock = this.create(name, ApricornBlock(BlockBehaviour.Properties.of().mapColor(apricorn.mapColor()).randomTicks().strength(Blocks.OAK_LOG.defaultDestroyTime(), Blocks.OAK_LOG.explosionResistance).sound(SoundType.WOOD).noOcclusion(), apricorn))
@@ -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
@@ -1517,87 +1518,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val BLANK_TM = this.create("blank_tm", CobblemonItem(Item.Properties()))
@JvmField
val COPPER_OVEN = this.create("copper_oven", BlockItem(CobblemonBlocks.COPPER_OVEN, Item.Properties()))
@JvmField
val EXPOSED_COPPER_OVEN = this.create("exposed_copper_oven", BlockItem(CobblemonBlocks.EXPOSED_COPPER_OVEN, Item.Properties()))
@JvmField
val WEATHERED_COPPER_OVEN = this.create("weathered_copper_oven", BlockItem(CobblemonBlocks.WEATHERED_COPPER_OVEN, Item.Properties()))
@JvmField
val OXIDIZED_COPPER_OVEN = this.create("oxidized_copper_oven", BlockItem(CobblemonBlocks.OXIDIZED_COPPER_OVEN, Item.Properties()))
@JvmField
val WAXED_COPPER_OVEN = this.create("waxed_copper_oven", BlockItem(CobblemonBlocks.WAXED_COPPER_OVEN, Item.Properties()))
@JvmField
val WAXED_EXPOSED_COPPER_OVEN = this.create("waxed_exposed_copper_oven", BlockItem(CobblemonBlocks.WAXED_EXPOSED_COPPER_OVEN, Item.Properties()))
@JvmField
val WAXED_WEATHERED_COPPER_OVEN = this.create("waxed_weathered_copper_oven", BlockItem(CobblemonBlocks.WAXED_WEATHERED_COPPER_OVEN, Item.Properties()))
@JvmField
val WAXED_OXIDIZED_COPPER_OVEN = this.create("waxed_oxidized_copper_oven", BlockItem(CobblemonBlocks.WAXED_OXIDIZED_COPPER_OVEN, Item.Properties()))
@JvmField
val COPPER_WASHER = this.create("copper_washer", BlockItem(CobblemonBlocks.COPPER_WASHER, Item.Properties()))
@JvmField
val EXPOSED_COPPER_WASHER = this.create("exposed_copper_washer", BlockItem(CobblemonBlocks.EXPOSED_COPPER_WASHER, Item.Properties()))
@JvmField
val WEATHERED_COPPER_WASHER = this.create("weathered_copper_washer", BlockItem(CobblemonBlocks.WEATHERED_COPPER_WASHER, Item.Properties()))
@JvmField
val OXIDIZED_COPPER_WASHER = this.create("oxidized_copper_washer", BlockItem(CobblemonBlocks.OXIDIZED_COPPER_WASHER, Item.Properties()))
@JvmField
val WAXED_COPPER_WASHER = this.create("waxed_copper_washer", BlockItem(CobblemonBlocks.WAXED_COPPER_WASHER, Item.Properties()))
@JvmField
val WAXED_EXPOSED_COPPER_WASHER = this.create("waxed_exposed_copper_washer", BlockItem(CobblemonBlocks.WAXED_EXPOSED_COPPER_WASHER, Item.Properties()))
@JvmField
val WAXED_WEATHERED_COPPER_WASHER = this.create("waxed_weathered_copper_washer", BlockItem(CobblemonBlocks.WAXED_WEATHERED_COPPER_WASHER, Item.Properties()))
@JvmField
val WAXED_OXIDIZED_COPPER_WASHER = this.create("waxed_oxidized_copper_washer", BlockItem(CobblemonBlocks.WAXED_OXIDIZED_COPPER_WASHER, Item.Properties()))
@JvmField
val COPPER_FAN = this.create("copper_fan", BlockItem(CobblemonBlocks.COPPER_FAN, Item.Properties()))
@JvmField
val EXPOSED_COPPER_FAN = this.create("exposed_copper_fan", BlockItem(CobblemonBlocks.EXPOSED_COPPER_FAN, Item.Properties()))
@JvmField
val WEATHERED_COPPER_FAN = this.create("weathered_copper_fan", BlockItem(CobblemonBlocks.WEATHERED_COPPER_FAN, Item.Properties()))
@JvmField
val OXIDIZED_COPPER_FAN = this.create("oxidized_copper_fan", BlockItem(CobblemonBlocks.OXIDIZED_COPPER_FAN, Item.Properties()))
@JvmField
val WAXED_COPPER_FAN = this.create("waxed_copper_fan", BlockItem(CobblemonBlocks.WAXED_COPPER_FAN, Item.Properties()))
@JvmField
val WAXED_EXPOSED_COPPER_FAN = this.create("waxed_exposed_copper_fan", BlockItem(CobblemonBlocks.WAXED_EXPOSED_COPPER_FAN, Item.Properties()))
@JvmField
val WAXED_WEATHERED_COPPER_FAN = this.create("waxed_weathered_copper_fan", BlockItem(CobblemonBlocks.WAXED_WEATHERED_COPPER_FAN, Item.Properties()))
@JvmField
val WAXED_OXIDIZED_COPPER_FAN = this.create("waxed_oxidized_copper_fan", BlockItem(CobblemonBlocks.WAXED_OXIDIZED_COPPER_FAN, Item.Properties()))
@JvmField
val COPPER_MOWER = this.create("copper_mower", BlockItem(CobblemonBlocks.COPPER_MOWER, Item.Properties()))
@JvmField
val EXPOSED_COPPER_MOWER = this.create("exposed_copper_mower", BlockItem(CobblemonBlocks.EXPOSED_COPPER_MOWER, Item.Properties()))
@JvmField
val WEATHERED_COPPER_MOWER = this.create("weathered_copper_mower", BlockItem(CobblemonBlocks.WEATHERED_COPPER_MOWER, Item.Properties()))
@JvmField
val OXIDIZED_COPPER_MOWER = this.create("oxidized_copper_mower", BlockItem(CobblemonBlocks.OXIDIZED_COPPER_MOWER, Item.Properties()))
@JvmField
val WAXED_COPPER_MOWER = this.create("waxed_copper_mower", BlockItem(CobblemonBlocks.WAXED_COPPER_MOWER, Item.Properties()))
@JvmField
val WAXED_EXPOSED_COPPER_MOWER = this.create("waxed_exposed_copper_mower", BlockItem(CobblemonBlocks.WAXED_EXPOSED_COPPER_MOWER, Item.Properties()))
@JvmField
val WAXED_WEATHERED_COPPER_MOWER = this.create("waxed_weathered_copper_mower", BlockItem(CobblemonBlocks.WAXED_WEATHERED_COPPER_MOWER, Item.Properties()))
@JvmField
val WAXED_OXIDIZED_COPPER_MOWER = this.create("waxed_oxidized_copper_mower", BlockItem(CobblemonBlocks.WAXED_OXIDIZED_COPPER_MOWER, Item.Properties()))
@JvmField
val COPPER_FRIDGE = this.create("copper_fridge", BlockItem(CobblemonBlocks.COPPER_FRIDGE, Item.Properties()))
@JvmField
val EXPOSED_COPPER_FRIDGE = this.create("exposed_copper_fridge", BlockItem(CobblemonBlocks.EXPOSED_COPPER_FRIDGE, Item.Properties()))
@JvmField
val WEATHERED_COPPER_FRIDGE = this.create("weathered_copper_fridge", BlockItem(CobblemonBlocks.WEATHERED_COPPER_FRIDGE, Item.Properties()))
@JvmField
val OXIDIZED_COPPER_FRIDGE = this.create("oxidized_copper_fridge", BlockItem(CobblemonBlocks.OXIDIZED_COPPER_FRIDGE, Item.Properties()))
@JvmField
val WAXED_COPPER_FRIDGE = this.create("waxed_copper_fridge", BlockItem(CobblemonBlocks.WAXED_COPPER_FRIDGE, Item.Properties()))
@JvmField
val WAXED_EXPOSED_COPPER_FRIDGE = this.create("waxed_exposed_copper_fridge", BlockItem(CobblemonBlocks.WAXED_EXPOSED_COPPER_FRIDGE, Item.Properties()))
@JvmField
val WAXED_WEATHERED_COPPER_FRIDGE = this.create("waxed_weathered_copper_fridge", BlockItem(CobblemonBlocks.WAXED_WEATHERED_COPPER_FRIDGE, Item.Properties()))
@JvmField
val WAXED_OXIDIZED_COPPER_FRIDGE = this.create("waxed_oxidized_copper_fridge", BlockItem(CobblemonBlocks.WAXED_OXIDIZED_COPPER_FRIDGE, Item.Properties()))
// Type Gems
@JvmField
val NORMAL_GEM = this.create("normal_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL))
@@ -1658,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))
@@ -12,7 +12,6 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.item.crafting.CookingPotRecipe
import com.cobblemon.mod.common.item.crafting.CookingPotShapelessRecipe
import com.cobblemon.mod.common.item.crafting.brewingstand.BrewingStandRecipe
import com.cobblemon.mod.common.item.crafting.washer.WasherRecipe
import com.cobblemon.mod.common.platform.PlatformRegistry
import net.minecraft.core.Registry
import net.minecraft.core.registries.BuiltInRegistries
@@ -27,9 +26,7 @@ object CobblemonRecipeSerializers : PlatformRegistry<Registry<RecipeSerializer<*
val COOKING_POT_SHAPELESS: RecipeSerializer<*> = register("cobblemon:cooking_pot_shapeless", CookingPotShapelessRecipe.Serializer())
val BREWING_STAND: RecipeSerializer<*> = register("cobblemon:brewing_stand", BrewingStandRecipe.Serializer())
val WASHER: RecipeSerializer<*> = register("cobblemon:washer", WasherRecipe.Serializer())
override val registry: Registry<RecipeSerializer<*>>
get() = BuiltInRegistries.RECIPE_SERIALIZER
override val resourceKey: ResourceKey<Registry<RecipeSerializer<*>>>
@@ -12,7 +12,6 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.item.crafting.CookingPotRecipe
import com.cobblemon.mod.common.item.crafting.CookingPotShapelessRecipe
import com.cobblemon.mod.common.item.crafting.brewingstand.BrewingStandRecipe
import com.cobblemon.mod.common.item.crafting.washer.WasherRecipe
import com.cobblemon.mod.common.platform.PlatformRegistry
import net.minecraft.core.Registry
import net.minecraft.core.registries.BuiltInRegistries
@@ -27,9 +26,7 @@ object CobblemonRecipeTypes : PlatformRegistry<Registry<RecipeType<*>>, Resource
val COOKING_POT_SHAPELESS: RecipeType<CookingPotShapelessRecipe> = register("cobblemon:cooking_pot_shapeless")
val BREWING_STAND: RecipeType<BrewingStandRecipe> = register("cobblemon:brewing_stand")
val WASHER: RecipeType<WasherRecipe> = register("cobblemon:washer")
override val registry: Registry<RecipeType<*>>
get() = BuiltInRegistries.RECIPE_TYPE
override val resourceKey: ResourceKey<Registry<RecipeType<*>>>
@@ -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")
@@ -1,87 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.appliances
import com.cobblemon.mod.common.util.rotateShape
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.block.*
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.material.Fluids
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
//TODO: All the Fan appliance stuff
//TODO: Able to place the Fan on Walls, models and hitbox are there (blockstate would need edits to support it)
open class CopperFanBlock(settings: Properties) : HorizontalDirectionalBlock(settings), SimpleWaterloggedBlock {
companion object {
val CODEC: MapCodec<CopperFanBlock> = RecordCodecBuilder.mapCodec {
it.group(propertiesCodec()).apply(it, ::CopperFanBlock) }
//val FACE = //TODO: Floor/Wall
val POWER = BlockStateProperties.POWER;
val PUSH = BooleanProperty.create("push")
val FLOOR_HITBOX = Shapes.or(
Shapes.box(0.0625, 0.125, 0.4375, 0.9375, 1.0, 0.5625),
Shapes.box(0.375, 0.0, 0.375, 0.625, 0.125, 0.625))
val WALL_HITBOX = Shapes.box(0.0625, 0.0625, 0.875, 0.9375, 0.9375, 1.0)
}
init {
registerDefaultState(stateDefinition.any()
.setValue(HORIZONTAL_FACING, Direction.NORTH)
.setValue(WATERLOGGED, false)
//.setValue(FACE, AttachFace.FLOOR)
.setValue(POWER, 0)
.setValue(PUSH, true) //If True, fan pushes entities, If False, fan is cosmetic only
)
}
override fun codec(): MapCodec<CopperFanBlock> = CODEC
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(HORIZONTAL_FACING, WATERLOGGED, /*FACE,*/ POWER, PUSH)
}
override fun getFluidState(blockState: BlockState): FluidState? = if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState)
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
if (state.getValue(WATERLOGGED)) {
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
}
return if (state.getValue(HORIZONTAL_FACING).opposite == direction && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
}
override fun getCollisionShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
val direction = state.getValue(HORIZONTAL_FACING)
return rotateShape(Direction.NORTH, direction, FLOOR_HITBOX)
}
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
return getCollisionShape(state, level, pos, context)
}
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
val fluidState = context.level.getFluidState(context.clickedPos)
return super.getStateForPlacement(context)?.setValue(HORIZONTAL_FACING, context.horizontalDirection.opposite)?.setValue(WATERLOGGED, fluidState.type === Fluids.WATER)
}
}
@@ -1,275 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.appliances
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.util.isServerSide
import com.cobblemon.mod.common.util.rotateShape
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.server.level.ServerLevel
import net.minecraft.util.RandomSource
import net.minecraft.world.InteractionHand
import net.minecraft.world.ItemInteractionResult
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.LevelReader
import net.minecraft.world.level.block.*
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityTicker
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf
import net.minecraft.world.level.block.state.properties.EnumProperty
import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.material.Fluids
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
//TODO: Oxidation/Waxing/Scraping not breaking the blocks
//TODO: All the Fridge appliance stuff
open class CopperFridgeBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWaterloggedBlock {
fun getBlockEntity(level: ServerLevel, pos: BlockPos) = level.getBlockEntity(getBottomPos(level, pos)) as CopperFridgeBlockEntity
companion object {
val CODEC: MapCodec<CopperFridgeBlock> = RecordCodecBuilder.mapCodec {
it.group(propertiesCodec()).apply(it, ::CopperFridgeBlock) }
val HALF: EnumProperty<DoubleBlockHalf> = BlockStateProperties.DOUBLE_BLOCK_HALF
val POWERED = BooleanProperty.create("powered")
val FACING = HorizontalDirectionalBlock.FACING
val LOWER_HITBOX = Shapes.or(
Shapes.box(0.0625, 0.0, 0.125, 0.9375, 0.0625, 0.9375),
Shapes.box(0.0, 0.0625, 0.0625, 1.0, 1.0, 1.0))
val UPPER_HITBOX = Shapes.or(
Shapes.box(0.0, 0.0, 0.0625, 1.0, 1.0, 1.0),
Shapes.box(0.25, 0.125, 0.0, 0.375, 0.3125, 0.0625),
Shapes.box(0.625, 0.125, 0.0, 0.75, 0.3125, 0.0625))
val UPPER_COLLISSIONBOX = Shapes.box(0.0, 0.0, 0.0625, 1.0, 1.0, 1.0)
//The handles get ignored by entity collission, because it feels nicer, but still count as part of the hitbox
}
init {
registerDefaultState(stateDefinition.any()
.setValue(HORIZONTAL_FACING, Direction.NORTH)
.setValue(HALF, DoubleBlockHalf.LOWER)
.setValue(WATERLOGGED, false)
.setValue(POWERED, false)
)
}
override fun codec(): MapCodec<CopperFridgeBlock> = CODEC
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(HORIZONTAL_FACING, HALF, WATERLOGGED, POWERED)
}
override fun getFluidState(blockState: BlockState): FluidState? = if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState)
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
if (state.getValue(WATERLOGGED)) {
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
}
val isFridge = neighborState.`is`(this)
val half = state.getValue(HALF)
if (!isFridge && half == DoubleBlockHalf.UPPER && neighborPos == pos.below()) {
return Blocks.AIR.defaultBlockState()
} else if (!isFridge && half == DoubleBlockHalf.LOWER && neighborPos == pos.above()) {
return Blocks.AIR.defaultBlockState()
}
return state
}
override fun getCollisionShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
val direction = state.getValue(HORIZONTAL_FACING)
return if (state.getValue(HALF) == DoubleBlockHalf.LOWER) {
rotateShape(Direction.NORTH, direction, LOWER_HITBOX)
} else {
rotateShape(Direction.NORTH, direction, UPPER_COLLISSIONBOX)
}
}
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
val direction = state.getValue(HORIZONTAL_FACING)
return if (state.getValue(HALF) == DoubleBlockHalf.LOWER) {
rotateShape(Direction.NORTH, direction, LOWER_HITBOX)
} else {
rotateShape(Direction.NORTH, direction, UPPER_HITBOX)
}
}
override fun setPlacedBy(world: Level, pos: BlockPos, state: BlockState, placer: LivingEntity?, itemStack: ItemStack) {
world.setBlock(pos.above(),
state
.setValue(HALF, DoubleBlockHalf.UPPER)
.setValue(WATERLOGGED, world.getFluidState((pos.above())).type == Fluids.WATER)
, 3)
world.blockUpdated(pos, Blocks.AIR)
state.updateNeighbourShapes(world, pos, 3)
}
override fun getStateForPlacement(blockPlaceContext: BlockPlaceContext): BlockState? {
val abovePosition = blockPlaceContext.clickedPos.above()
val world = blockPlaceContext.level
if (world.getBlockState(abovePosition).canBeReplaced(blockPlaceContext) && !world.isOutsideBuildHeight(abovePosition)) {
return defaultBlockState()
.setValue(FACING, blockPlaceContext.horizontalDirection.opposite)
.setValue(HALF, DoubleBlockHalf.LOWER)
.setValue(WATERLOGGED, blockPlaceContext.level.getFluidState(blockPlaceContext.clickedPos).type == Fluids.WATER)
}
return null
}
override fun getRenderShape(state: BlockState) = RenderShape.MODEL
override fun canSurvive(state: BlockState, world: LevelReader, pos: BlockPos): Boolean {
val blockPos = pos.below()
val blockState = world.getBlockState(blockPos)
return if (state.getValue(HALF) == DoubleBlockHalf.LOWER) blockState.isFaceSturdy(world, blockPos, Direction.UP) else blockState.`is`(this)
}
override fun playerWillDestroy(world: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
if (!world.isClientSide && player.isCreative) {
var blockPos: BlockPos = BlockPos.ZERO
var blockState: BlockState = state
val half = state.getValue(HALF)
if (half == DoubleBlockHalf.UPPER && world.getBlockState(pos.below().also { blockPos = it }).also { blockState = it }.`is`(state.block) && blockState.getValue(
HALF
) == DoubleBlockHalf.LOWER) {
val blockState2 = if (blockState.fluidState.`is`(Fluids.WATER)) Blocks.WATER.defaultBlockState() else Blocks.AIR.defaultBlockState()
world.setBlock(blockPos, blockState2, UPDATE_ALL or UPDATE_SUPPRESS_DROPS)
world.levelEvent(player, LevelEvent.PARTICLES_DESTROY_BLOCK, blockPos, getId(blockState))
}
}
return super.playerWillDestroy(world, pos, state, player)
}
override fun useItemOn(
stack: ItemStack,
state: BlockState,
level: Level,
pos: BlockPos,
player: Player,
hand: InteractionHand,
hitResult: BlockHitResult
): ItemInteractionResult {
if (level.isServerSide()) {
val handStack = player.getItemInHand(hand)
val stack = getBlockEntity(level as ServerLevel, pos).theItem
when (stack.item) {
Items.AIR -> {
if (handStack.`is`(Items.WATER_BUCKET)) {
if (!player.isCreative) player.setItemInHand(hand, Items.BUCKET.defaultInstance)
getBlockEntity(level, pos).timer = 100
getBlockEntity(level, pos).theItem = Items.WATER_BUCKET.defaultInstance
return ItemInteractionResult.SUCCESS
} else {
return ItemInteractionResult.FAIL
}
}
Items.WATER_BUCKET -> {
if (handStack.`is`(Items.BUCKET)) {
player.setItemInHand(hand, Items.WATER_BUCKET.defaultInstance)
getBlockEntity(level, pos).theItem = Items.AIR.defaultInstance
getBlockEntity(level, pos).timer = 0
return ItemInteractionResult.SUCCESS
} else {
return ItemInteractionResult.FAIL
}
}
Items.ICE -> {
val facing = state.getValue(HORIZONTAL_FACING)
val spawnPos = getBottomPos(level, pos)
val itemEntity =
ItemEntity(level, spawnPos.x + 0.5, spawnPos.y + 0.5, spawnPos.z + 0.5, Items.ICE.defaultInstance)
itemEntity.deltaMovement = Vec3(facing.stepX.toDouble(), 0.0, facing.stepZ.toDouble()).scale(0.2)
level.addFreshEntity(itemEntity)
getBlockEntity(level, pos).theItem = Items.AIR.defaultInstance
return ItemInteractionResult.SUCCESS
}
}
}
return ItemInteractionResult.SUCCESS
}
override fun getAnalogOutputSignal(state: BlockState, level: Level, pos: BlockPos): Int {
if (level.isClientSide) return super.getAnalogOutputSignal(state, level, pos)
return when (getBlockEntity(level as ServerLevel, pos).theItem.item) {
Items.WATER_BUCKET -> 1
Items.ICE -> 2
else -> 0
}
}
override fun tick(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
(level.getBlockEntity(pos) as CopperFridgeBlockEntity).processTick()
super.tick(state, level, pos, random)
}
override fun newBlockEntity(pos: BlockPos, state: BlockState) = if (state.getValue(HALF) == DoubleBlockHalf.LOWER) CopperFridgeBlockEntity(pos, state) else null
override fun neighborChanged(
state: BlockState,
level: Level,
pos: BlockPos,
neighborBlock: Block,
neighborPos: BlockPos,
movedByPiston: Boolean
) {
if (!level.isClientSide) {
val otherPos = if (state.getValue(HALF) == DoubleBlockHalf.LOWER) pos.above() else pos.below()
val powered = level.hasNeighborSignal(pos) || level.hasNeighborSignal(otherPos)
if (state.getValue(POWERED) != powered) {
level.setBlock(pos, state.setValue(POWERED, powered), 3)
}
val otherState = level.getBlockState(otherPos)
if (otherState.`is`(this) && otherState.getValue(POWERED) != powered) {
level.setBlock(otherPos, otherState.setValue(POWERED, powered), 3)
}
}
super.neighborChanged(state, level, pos, neighborBlock, neighborPos, movedByPiston)
}
override fun <T : BlockEntity> getTicker(
level: Level,
state: BlockState,
blockEntityType: BlockEntityType<T>
): BlockEntityTicker<T>? {
return if (level.isClientSide)
null
else
createTickerHelper(blockEntityType, CobblemonBlockEntities.COPPER_FRIDGE, CopperFridgeBlockEntity.TICKER::tick)
}
fun getBottomPos(level: Level, pos: BlockPos) = if (level.getBlockState(pos).getValue(HALF) == DoubleBlockHalf.UPPER) pos.below() else pos
}
@@ -1,48 +0,0 @@
package com.cobblemon.mod.common.block.appliances
import com.cobblemon.mod.common.CobblemonBlockEntities
import net.minecraft.core.BlockPos
import net.minecraft.world.Container
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
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.ticks.ContainerSingleItem
class CopperFridgeBlockEntity(val pos: BlockPos, val state: BlockState) : BlockEntity(
CobblemonBlockEntities.COPPER_FRIDGE, pos, state
), ContainerSingleItem.BlockContainerSingleItem {
private var item: ItemStack = Items.AIR.defaultInstance
var timer: Int = -1
fun processTick() {
if (level == null) return
if (!level!!.getBlockState(pos).getValue(CopperFridgeBlock.POWERED)) return
if (timer > 0) timer--
if (timer == 0 && item.item == Items.WATER_BUCKET) {
item = Items.ICE.defaultInstance
timer = -1
}
}
override fun getContainerBlockEntity() = this
override fun getTheItem() = item
override fun setTheItem(item: ItemStack) {
this.item = item
}
override fun canTakeItem(target: Container, slot: Int, stack: ItemStack): Boolean {
return stack.item == Items.ICE
}
companion object {
internal val TICKER = BlockEntityTicker<CopperFridgeBlockEntity> { level, pos, state, entity ->
if (!level.isClientSide) {
entity.processTick()
}
}
}
}
@@ -1,98 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.appliances
import com.cobblemon.mod.common.util.rotateShape
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.block.*
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.material.Fluids
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
//TODO: All the Mower appliance stuff
open class CopperMowerBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWaterloggedBlock {
companion object {
val CODEC: MapCodec<CopperMowerBlock> = RecordCodecBuilder.mapCodec {
it.group(propertiesCodec()).apply(it, ::CopperMowerBlock) }
val TRIGGERED = BooleanProperty.create("triggered")
val COLLISIONBOX = Shapes.or(
Shapes.box(0.1875, 0.0, 0.0, 0.8125, 0.375, 0.625),
Shapes.box(0.0625, 0.0, 0.625, 0.9375, 0.625, 1.0),
Shapes.box(0.125, 0.0, 0.1875, 0.875, 0.3125, 0.5),
//The next three lines are just so the handle does have collision, it feels awkward without it but it looks bad as hitbox
Shapes.box(0.375, 0.375, 0.375, 0.625, 0.625, 0.5625),
Shapes.box(0.4375, 0.5625, 0.5, 0.5625, 0.875, 0.625),
Shapes.box(0.25, 0.875, 0.5625, 0.75, 1.0, 0.6875)
)
val HITBOX = Shapes.or(
Shapes.box(0.1875, 0.0, 0.0, 0.8125, 0.375, 0.625),
Shapes.box(0.0625, 0.0, 0.625, 0.9375, 0.625, 1.0),
Shapes.box(0.125, 0.0, 0.1875, 0.875, 0.3125, 0.5)
)
}
init {
registerDefaultState(stateDefinition.any()
.setValue(HORIZONTAL_FACING, Direction.NORTH)
.setValue(WATERLOGGED, false)
.setValue(TRIGGERED, false)
)
}
override fun codec(): MapCodec<CopperMowerBlock> = CODEC
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(HORIZONTAL_FACING, WATERLOGGED, TRIGGERED)
}
override fun getFluidState(blockState: BlockState): FluidState? = if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState)
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
if (state.getValue(WATERLOGGED)) {
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
}
return if (state.getValue(HORIZONTAL_FACING).opposite == direction && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
}
override fun getCollisionShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
val direction = state.getValue(HORIZONTAL_FACING)
return rotateShape(Direction.NORTH, direction, COLLISIONBOX)
}
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
val direction = state.getValue(HORIZONTAL_FACING)
return rotateShape(Direction.NORTH, direction, HITBOX)
}
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
val fluidState = context.level.getFluidState(context.clickedPos)
return super.getStateForPlacement(context)?.setValue(HORIZONTAL_FACING, context.horizontalDirection.opposite)?.setValue(WATERLOGGED, fluidState.type === Fluids.WATER)
}
override fun newBlockEntity(
pos: BlockPos,
state: BlockState
) = CopperMowerBlockEntity(pos, state)
}
@@ -1,12 +0,0 @@
package com.cobblemon.mod.common.block.appliances
import com.cobblemon.mod.common.CobblemonBlockEntities
import net.minecraft.core.BlockPos
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
class CopperMowerBlockEntity(val pos: BlockPos, val state: BlockState) : BlockEntity(
CobblemonBlockEntities.COPPER_MOWER,
pos, state
) {
}
@@ -1,88 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.appliances
import com.cobblemon.mod.common.util.rotateShape
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.util.StringRepresentable
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.block.*
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.block.state.properties.EnumProperty
import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.material.Fluids
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
//TODO: All the Oven appliance stuff - Good Luck Coders
open class CopperOvenBlock(settings: Properties) : HorizontalDirectionalBlock(settings), SimpleWaterloggedBlock {
companion object {
val CODEC: MapCodec<CopperOvenBlock> = RecordCodecBuilder.mapCodec {
it.group(propertiesCodec()).apply(it, ::CopperOvenBlock) }
val EJECTING = BooleanProperty.create("ejecting")
val COOKING = EnumProperty.create("cooking", CookingState::class.java)
val HITBOX = Shapes.box(0.0625, 0.0, 0.3125, 0.9375, 0.625, 0.9375)
}
init {
registerDefaultState(stateDefinition.any()
.setValue(HORIZONTAL_FACING, Direction.NORTH)
.setValue(WATERLOGGED, false)
.setValue(COOKING, CookingState.CLOSED)
.setValue(EJECTING, false)
)
}
enum class CookingState : StringRepresentable {
CLOSED, //Door is closed
OPEN, //Door is open
COOKING; //Door is locked closed and the light level is cranked up, as long as the block is powered by redstone. Acts as closed lid in the original Campfire Pot
override fun getSerializedName(): String = this.name.lowercase()
}
override fun codec(): MapCodec<CopperOvenBlock> = CODEC
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(HORIZONTAL_FACING, WATERLOGGED, COOKING, EJECTING)
}
override fun getFluidState(blockState: BlockState): FluidState? = if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState)
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
if (state.getValue(WATERLOGGED)) {
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
}
return if (state.getValue(HORIZONTAL_FACING).opposite == direction && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
}
override fun getCollisionShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
val direction = state.getValue(HORIZONTAL_FACING)
return rotateShape(Direction.NORTH, direction, HITBOX)
}
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
return getCollisionShape(state, level, pos, context).singleEncompassing()
}
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
val fluidState = context.level.getFluidState(context.clickedPos)
return super.getStateForPlacement(context)?.setValue(HORIZONTAL_FACING, context.horizontalDirection.opposite)?.setValue(WATERLOGGED, fluidState.type === Fluids.WATER)
}
}
@@ -1,124 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.appliances
import com.cobblemon.mod.common.util.isServerSide
import com.cobblemon.mod.common.util.rotateShape
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.InteractionHand
import net.minecraft.world.ItemInteractionResult
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.block.*
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.material.Fluids
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
open class CopperWasherBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWaterloggedBlock {
companion object {
val CODEC: MapCodec<CopperWasherBlock> = RecordCodecBuilder.mapCodec {
it.group(propertiesCodec()).apply(it, ::CopperWasherBlock) }
val POWERED = BooleanProperty.create("powered")
val HITBOX = Shapes.box(0.0625, 0.0, 0.125, 0.9375, 1.0, 1.0)
}
init {
registerDefaultState(stateDefinition.any()
.setValue(HORIZONTAL_FACING, Direction.NORTH)
.setValue(WATERLOGGED, false)
.setValue(POWERED, false)
)
}
override fun codec(): MapCodec<CopperWasherBlock> = CODEC
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(HORIZONTAL_FACING, WATERLOGGED, POWERED)
}
override fun getFluidState(blockState: BlockState): FluidState? = if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState)
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
if (state.getValue(WATERLOGGED)) {
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
}
return if (state.getValue(HORIZONTAL_FACING).opposite == direction && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
}
override fun getCollisionShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
val direction = state.getValue(HORIZONTAL_FACING)
return rotateShape(Direction.NORTH, direction, HITBOX)
}
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
return getCollisionShape(state, level, pos, context).singleEncompassing()
}
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
val fluidState = context.level.getFluidState(context.clickedPos)
return super.getStateForPlacement(context)?.setValue(HORIZONTAL_FACING, context.horizontalDirection.opposite)?.setValue(WATERLOGGED, fluidState.type === Fluids.WATER)
}
override fun newBlockEntity(pos: BlockPos, state: BlockState) = CopperWasherBlockEntity(pos, state)
override fun getRenderShape(state: BlockState) = RenderShape.MODEL
override fun useItemOn(
stack: ItemStack,
state: BlockState,
level: Level,
pos: BlockPos,
player: Player,
hand: InteractionHand,
hitResult: BlockHitResult
): ItemInteractionResult {
val entity = level.getBlockEntity(pos) as CopperWasherBlockEntity
val item = entity.theItem
if (item == ItemStack.EMPTY) {
if (level.isServerSide()) {
entity.theItem = stack.copyWithCount(1)
if (!player.isCreative) stack.shrink(1)
entity.craft()
}
return ItemInteractionResult.SUCCESS
} else {
if (level.isServerSide()) {
val facing = state.getValue(HORIZONTAL_FACING)
val itemEntity =
ItemEntity(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, entity.theItem)
itemEntity.deltaMovement = Vec3(facing.stepX.toDouble(), 0.0, facing.stepZ.toDouble()).scale(0.2)
level.addFreshEntity(itemEntity)
entity.theItem = ItemStack.EMPTY
}
return ItemInteractionResult.SUCCESS
}
}
}
@@ -1,36 +0,0 @@
package com.cobblemon.mod.common.block.appliances
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonRecipeTypes
import com.cobblemon.mod.common.item.crafting.washer.WasherInput
import net.minecraft.core.BlockPos
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.ticks.ContainerSingleItem
class CopperWasherBlockEntity(val pos: BlockPos, val state: BlockState): BlockEntity(
CobblemonBlockEntities.COPPER_WASHER,
pos, state
), ContainerSingleItem.BlockContainerSingleItem {
private var item: ItemStack = ItemStack.EMPTY
override fun getContainerBlockEntity() = this
override fun getTheItem() = item
override fun setTheItem(item: ItemStack) { this.item = item }
fun craft() {
val level = level ?: return
if (level.isClientSide || theItem.isEmpty) return
val input = WasherInput(theItem.copyWithCount(1))
val recipe = level.recipeManager
.getRecipeFor(CobblemonRecipeTypes.WASHER, input, level)
.orElse(null) ?: return
theItem = recipe.value().assemble(input, level.registryAccess())
setChanged()
level.sendBlockUpdated(worldPosition, blockState, blockState, 3)
}
}
@@ -1,31 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.appliances
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.util.RandomSource
import net.minecraft.world.level.block.WeatheringCopper
import net.minecraft.world.level.block.state.BlockState
class WeatheringCopperFanBlock(val weatherState: WeatheringCopper.WeatherState, properties: Properties) :
CopperFanBlock(properties), WeatheringCopper {
override fun randomTick(state: BlockState?, level: ServerLevel?, pos: BlockPos?, random: RandomSource?) {
this.changeOverTime(state, level, pos, random)
}
override fun isRandomlyTicking(state: BlockState): Boolean {
return WeatheringCopper.getNext(state.getBlock()).isPresent()
}
override fun getAge(): WeatheringCopper.WeatherState {
return this.weatherState
}
}
@@ -1,58 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.appliances
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.util.RandomSource
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.WeatheringCopper
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.properties.BlockStateProperties
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf
class WeatheringCopperFridgeBlock(val weatherState: WeatheringCopper.WeatherState, properties: Properties) :
CopperFridgeBlock(properties), WeatheringCopper {
override fun randomTick(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
if (state.getValue(HALF) == DoubleBlockHalf.LOWER) {
this.changeOverTime(state, level, pos, random)
}
}
override fun changeOverTime(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
val upperPos = pos.above()
val upperState = level.getBlockState(upperPos)
if (upperState.block != this || upperState.getValue(HALF) != DoubleBlockHalf.UPPER) {
return
}
if (random.nextFloat() < 0.05688889F) {
getNextState(state, level, pos, random).ifPresent { nextState ->
val nextLowerState = nextState.setValue(HALF, DoubleBlockHalf.LOWER)
val nextUpperState = nextState
.setValue(HALF, DoubleBlockHalf.UPPER)
.setValue(FACING, upperState.getValue(FACING))
.setValue(BlockStateProperties.WATERLOGGED, upperState.getValue(BlockStateProperties.WATERLOGGED))
.setValue(POWERED, upperState.getValue(POWERED))
level.setBlock(pos, nextLowerState, Block.UPDATE_CLIENTS)
level.setBlock(upperPos, nextUpperState, Block.UPDATE_ALL)
}
}
}
override fun isRandomlyTicking(state: BlockState): Boolean {
return WeatheringCopper.getNext(state.block).isPresent
}
override fun getAge(): WeatheringCopper.WeatherState {
return this.weatherState
}
}
@@ -1,31 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.appliances
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.util.RandomSource
import net.minecraft.world.level.block.WeatheringCopper
import net.minecraft.world.level.block.state.BlockState
class WeatheringCopperMowerBlock(val weatherState: WeatheringCopper.WeatherState, properties: Properties) :
CopperMowerBlock(properties), WeatheringCopper {
override fun randomTick(state: BlockState?, level: ServerLevel?, pos: BlockPos?, random: RandomSource?) {
this.changeOverTime(state, level, pos, random)
}
override fun isRandomlyTicking(state: BlockState): Boolean {
return WeatheringCopper.getNext(state.getBlock()).isPresent()
}
override fun getAge(): WeatheringCopper.WeatherState {
return this.weatherState
}
}
@@ -1,31 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.appliances
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.util.RandomSource
import net.minecraft.world.level.block.WeatheringCopper
import net.minecraft.world.level.block.state.BlockState
class WeatheringCopperOvenBlock(val weatherState: WeatheringCopper.WeatherState, properties: Properties) :
CopperOvenBlock(properties), WeatheringCopper {
override fun randomTick(state: BlockState?, level: ServerLevel?, pos: BlockPos?, random: RandomSource?) {
this.changeOverTime(state, level, pos, random)
}
override fun isRandomlyTicking(state: BlockState): Boolean {
return WeatheringCopper.getNext(state.getBlock()).isPresent()
}
override fun getAge(): WeatheringCopper.WeatherState {
return this.weatherState
}
}
@@ -1,31 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.appliances
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.util.RandomSource
import net.minecraft.world.level.block.WeatheringCopper
import net.minecraft.world.level.block.state.BlockState
class WeatheringCopperWasherBlock(val weatherState: WeatheringCopper.WeatherState, properties: Properties) :
CopperWasherBlock(properties), WeatheringCopper {
override fun randomTick(state: BlockState?, level: ServerLevel?, pos: BlockPos?, random: RandomSource?) {
this.changeOverTime(state, level, pos, random)
}
override fun isRandomlyTicking(state: BlockState): Boolean {
return WeatheringCopper.getNext(state.getBlock()).isPresent()
}
override fun getAge(): WeatheringCopper.WeatherState {
return this.weatherState
}
}
@@ -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
@@ -516,17 +518,7 @@ object CobblemonClient {
CobblemonBlocks.POTTED_WATMEL_BERRY,
CobblemonBlocks.POTTED_WEPEAR_BERRY,
CobblemonBlocks.POTTED_WIKI_BERRY,
CobblemonBlocks.POTTED_YACHE_BERRY,
CobblemonBlocks.SCOPE_LENS,
CobblemonBlocks.COPPER_FAN,
CobblemonBlocks.EXPOSED_COPPER_FAN,
CobblemonBlocks.WEATHERED_COPPER_FAN,
CobblemonBlocks.OXIDIZED_COPPER_FAN,
CobblemonBlocks.WAXED_COPPER_FAN,
CobblemonBlocks.WAXED_EXPOSED_COPPER_FAN,
CobblemonBlocks.WAXED_WEATHERED_COPPER_FAN,
CobblemonBlocks.WAXED_OXIDIZED_COPPER_FAN
CobblemonBlocks.POTTED_YACHE_BERRY
)
this.createBoatModelLayers()
@@ -549,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
@@ -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
}
@@ -1,16 +0,0 @@
package com.cobblemon.mod.common.item.crafting.washer
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.Ingredient
import net.minecraft.world.item.crafting.RecipeInput
class WasherInput(
val item: ItemStack = ItemStack.EMPTY
) : RecipeInput {
override fun getItem(index: Int): ItemStack? {
return if (index == 0) item else null
}
override fun size() = 1
}
@@ -1,79 +0,0 @@
package com.cobblemon.mod.common.item.crafting.washer
import com.cobblemon.mod.common.CobblemonRecipeSerializers
import com.cobblemon.mod.common.CobblemonRecipeTypes
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.component.DataComponents
import net.minecraft.core.HolderLookup
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.StreamCodec
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.Ingredient
import net.minecraft.world.item.crafting.Recipe
import net.minecraft.world.item.crafting.RecipeSerializer
import net.minecraft.world.level.Level
class WasherRecipe(
val input: Ingredient,
val result: ItemStack,
val preserveInputComponents: Boolean = false,
val removeDyedColor: Boolean = false
) : Recipe<WasherInput> {
override fun getType() = CobblemonRecipeTypes.WASHER
override fun canCraftInDimensions(width: Int, height: Int) = true
override fun assemble(input: WasherInput, registries: HolderLookup.Provider): ItemStack {
val output = if (preserveInputComponents) {
input.item.transmuteCopy(result.item, result.count)
} else {
result.copy()
}
if (removeDyedColor) {
output.remove(DataComponents.DYED_COLOR)
}
return output
}
override fun getSerializer() = CobblemonRecipeSerializers.WASHER
override fun getResultItem(registries: HolderLookup.Provider): ItemStack = result.copy()
override fun matches(input: WasherInput, level: Level) =
this.input.test(input.item) && (!removeDyedColor || input.item.has(DataComponents.DYED_COLOR))
class Serializer : RecipeSerializer<WasherRecipe> {
companion object {
val CODEC: MapCodec<WasherRecipe> = RecordCodecBuilder.mapCodec { instance ->
instance.group(
Ingredient.CODEC.fieldOf("input").forGetter { recipe -> recipe.input },
ItemStack.STRICT_CODEC.fieldOf("result").forGetter { recipe -> recipe.result },
Codec.BOOL.optionalFieldOf("preserve_input_components", false)
.forGetter { recipe -> recipe.preserveInputComponents },
Codec.BOOL.optionalFieldOf("remove_dyed_color", false)
.forGetter { recipe -> recipe.removeDyedColor }
).apply(instance, ::WasherRecipe)
}
}
val STREAM_CODEC: StreamCodec<RegistryFriendlyByteBuf, WasherRecipe> =
StreamCodec.of(::toNetwork, ::fromNetwork)
private fun fromNetwork(buffer: RegistryFriendlyByteBuf): WasherRecipe {
val input = Ingredient.CONTENTS_STREAM_CODEC.decode(buffer)
val result = ItemStack.STREAM_CODEC.decode(buffer)
val preserveInputComponents = buffer.readBoolean()
val removeDyedColor = buffer.readBoolean()
return WasherRecipe(input, result, preserveInputComponents, removeDyedColor)
}
private fun toNetwork(buffer: RegistryFriendlyByteBuf, recipe: WasherRecipe) {
Ingredient.CONTENTS_STREAM_CODEC.encode(buffer, recipe.input)
ItemStack.STREAM_CODEC.encode(buffer, recipe.result)
buffer.writeBoolean(recipe.preserveInputComponents)
buffer.writeBoolean(recipe.removeDyedColor)
}
override fun codec() = CODEC
override fun streamCodec() = STREAM_CODEC
}
}
@@ -570,47 +570,6 @@ object CobblemonItemGroups {
entries.accept(CobblemonItems.PINK_PLAQUE)
CobblemonItems.campfire_pots.forEach(entries::accept)
entries.accept(CobblemonItems.COPPER_OVEN)
entries.accept(CobblemonItems.COPPER_WASHER)
entries.accept(CobblemonItems.COPPER_FAN)
entries.accept(CobblemonItems.COPPER_MOWER)
entries.accept(CobblemonItems.COPPER_FRIDGE)
entries.accept(CobblemonItems.EXPOSED_COPPER_OVEN)
entries.accept(CobblemonItems.EXPOSED_COPPER_WASHER)
entries.accept(CobblemonItems.EXPOSED_COPPER_FAN)
entries.accept(CobblemonItems.EXPOSED_COPPER_MOWER)
entries.accept(CobblemonItems.EXPOSED_COPPER_FRIDGE)
entries.accept(CobblemonItems.WEATHERED_COPPER_OVEN)
entries.accept(CobblemonItems.WEATHERED_COPPER_WASHER)
entries.accept(CobblemonItems.WEATHERED_COPPER_FAN)
entries.accept(CobblemonItems.WEATHERED_COPPER_MOWER)
entries.accept(CobblemonItems.WEATHERED_COPPER_FRIDGE)
entries.accept(CobblemonItems.OXIDIZED_COPPER_OVEN)
entries.accept(CobblemonItems.OXIDIZED_COPPER_WASHER)
entries.accept(CobblemonItems.OXIDIZED_COPPER_FAN)
entries.accept(CobblemonItems.OXIDIZED_COPPER_MOWER)
entries.accept(CobblemonItems.OXIDIZED_COPPER_FRIDGE)
entries.accept(CobblemonItems.WAXED_COPPER_OVEN)
entries.accept(CobblemonItems.WAXED_COPPER_WASHER)
entries.accept(CobblemonItems.WAXED_COPPER_FAN)
entries.accept(CobblemonItems.WAXED_COPPER_MOWER)
entries.accept(CobblemonItems.WAXED_COPPER_FRIDGE)
entries.accept(CobblemonItems.WAXED_EXPOSED_COPPER_OVEN)
entries.accept(CobblemonItems.WAXED_EXPOSED_COPPER_WASHER)
entries.accept(CobblemonItems.WAXED_EXPOSED_COPPER_FAN)
entries.accept(CobblemonItems.WAXED_EXPOSED_COPPER_MOWER)
entries.accept(CobblemonItems.WAXED_EXPOSED_COPPER_FRIDGE)
entries.accept(CobblemonItems.WAXED_WEATHERED_COPPER_OVEN)
entries.accept(CobblemonItems.WAXED_WEATHERED_COPPER_WASHER)
entries.accept(CobblemonItems.WAXED_WEATHERED_COPPER_FAN)
entries.accept(CobblemonItems.WAXED_WEATHERED_COPPER_MOWER)
entries.accept(CobblemonItems.WAXED_WEATHERED_COPPER_FRIDGE)
entries.accept(CobblemonItems.WAXED_OXIDIZED_COPPER_OVEN)
entries.accept(CobblemonItems.WAXED_OXIDIZED_COPPER_WASHER)
entries.accept(CobblemonItems.WAXED_OXIDIZED_COPPER_FAN)
entries.accept(CobblemonItems.WAXED_OXIDIZED_COPPER_MOWER)
entries.accept(CobblemonItems.WAXED_OXIDIZED_COPPER_FRIDGE)
}
private fun consumableEntries(displayContext: ItemDisplayParameters, entries: Output) {
@@ -986,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)
}
}
@@ -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": {
@@ -1,469 +0,0 @@
{
"variants": {
"facing=east,power=0": {
"model": "cobblemon:block/copper_fan_unpowered",
"y": 90
},
"facing=north,power=0": {
"model": "cobblemon:block/copper_fan_unpowered"
},
"facing=south,power=0": {
"model": "cobblemon:block/copper_fan_unpowered",
"y": 180
},
"facing=west,power=0": {
"model": "cobblemon:block/copper_fan_unpowered",
"y": 270
},
"facing=east,power=1,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 90
},
"facing=north,power=1,push=false": {
"model": "cobblemon:block/copper_fan_decorative"
},
"facing=south,power=1,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 180
},
"facing=west,power=1,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 270
},
"facing=east,power=1,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 90
},
"facing=north,power=1,push=true": {
"model": "cobblemon:block/copper_fan_pushing"
},
"facing=south,power=1,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 180
},
"facing=west,power=1,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 270
},
"facing=east,power=2,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 90
},
"facing=north,power=2,push=false": {
"model": "cobblemon:block/copper_fan_decorative"
},
"facing=south,power=2,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 180
},
"facing=west,power=2,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 270
},
"facing=east,power=2,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 90
},
"facing=north,power=2,push=true": {
"model": "cobblemon:block/copper_fan_pushing"
},
"facing=south,power=2,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 180
},
"facing=west,power=2,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 270
},
"facing=east,power=3,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 90
},
"facing=north,power=3,push=false": {
"model": "cobblemon:block/copper_fan_decorative"
},
"facing=south,power=3,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 180
},
"facing=west,power=3,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 270
},
"facing=east,power=3,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 90
},
"facing=north,power=3,push=true": {
"model": "cobblemon:block/copper_fan_pushing"
},
"facing=south,power=3,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 180
},
"facing=west,power=3,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 270
},
"facing=east,power=4,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 90
},
"facing=north,power=4,push=false": {
"model": "cobblemon:block/copper_fan_decorative"
},
"facing=south,power=4,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 180
},
"facing=west,power=4,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 270
},
"facing=east,power=4,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 90
},
"facing=north,power=4,push=true": {
"model": "cobblemon:block/copper_fan_pushing"
},
"facing=south,power=4,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 180
},
"facing=west,power=4,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 270
},
"facing=east,power=5,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 90
},
"facing=north,power=5,push=false": {
"model": "cobblemon:block/copper_fan_decorative"
},
"facing=south,power=5,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 180
},
"facing=west,power=5,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 270
},
"facing=east,power=5,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 90
},
"facing=north,power=5,push=true": {
"model": "cobblemon:block/copper_fan_pushing"
},
"facing=south,power=5,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 180
},
"facing=west,power=5,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 270
},
"facing=east,power=6,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 90
},
"facing=north,power=6,push=false": {
"model": "cobblemon:block/copper_fan_decorative"
},
"facing=south,power=6,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 180
},
"facing=west,power=6,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 270
},
"facing=east,power=6,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 90
},
"facing=north,power=6,push=true": {
"model": "cobblemon:block/copper_fan_pushing"
},
"facing=south,power=6,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 180
},
"facing=west,power=6,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 270
},
"facing=east,power=7,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 90
},
"facing=north,power=7,push=false": {
"model": "cobblemon:block/copper_fan_decorative"
},
"facing=south,power=7,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 180
},
"facing=west,power=7,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 270
},
"facing=east,power=7,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 90
},
"facing=north,power=7,push=true": {
"model": "cobblemon:block/copper_fan_pushing"
},
"facing=south,power=7,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 180
},
"facing=west,power=7,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 270
},
"facing=east,power=8,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=8,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=8,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=8,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=8,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=8,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=8,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=8,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=9,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=9,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=9,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=9,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=9,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=9,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=9,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=9,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=10,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=10,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=10,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=10,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=10,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=10,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=10,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=10,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=11,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=11,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=11,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=11,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=11,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=11,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=11,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=11,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=12,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=12,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=12,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=12,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=12,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=12,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=12,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=12,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=13,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=13,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=13,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=13,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=13,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=13,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=13,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=13,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=14,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=14,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=14,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=14,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=14,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=14,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=14,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=14,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=15,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=15,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=15,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=15,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=15,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=15,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=15,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=15,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
}
}
}
@@ -1,34 +0,0 @@
{
"variants": {
"facing=east,half=lower": {
"model": "cobblemon:block/copper_fridge_bottom",
"y": 90
},
"facing=north,half=lower": {
"model": "cobblemon:block/copper_fridge_bottom"
},
"facing=south,half=lower": {
"model": "cobblemon:block/copper_fridge_bottom",
"y": 180
},
"facing=west,half=lower": {
"model": "cobblemon:block/copper_fridge_bottom",
"y": 270
},
"facing=east,half=upper": {
"model": "cobblemon:block/copper_fridge_top",
"y": 90
},
"facing=north,half=upper": {
"model": "cobblemon:block/copper_fridge_top"
},
"facing=south,half=upper": {
"model": "cobblemon:block/copper_fridge_top",
"y": 180
},
"facing=west,half=upper": {
"model": "cobblemon:block/copper_fridge_top",
"y": 270
}
}
}
@@ -1,19 +0,0 @@
{
"variants": {
"facing=east": {
"model": "cobblemon:block/copper_mower",
"y": 90
},
"facing=north": {
"model": "cobblemon:block/copper_mower"
},
"facing=south": {
"model": "cobblemon:block/copper_mower",
"y": 180
},
"facing=west": {
"model": "cobblemon:block/copper_mower",
"y": 270
}
}
}
@@ -1,64 +0,0 @@
{
"variants": {
"facing=east,cooking=closed,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed",
"y": 90
},
"facing=north,cooking=closed,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed"
},
"facing=south,cooking=closed,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed",
"y": 180
},
"facing=west,cooking=closed,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed",
"y": 270
},
"facing=east,cooking=open,ejecting=false": {
"model": "cobblemon:block/copper_oven_open",
"y": 90
},
"facing=north,cooking=open,ejecting=false": {
"model": "cobblemon:block/copper_oven_open"
},
"facing=south,cooking=open,ejecting=false": {
"model": "cobblemon:block/copper_oven_open",
"y": 180
},
"facing=west,cooking=open,ejecting=false": {
"model": "cobblemon:block/copper_oven_open",
"y": 270
},
"facing=east,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed",
"y": 90
},
"facing=north,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed"
},
"facing=south,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed",
"y": 180
},
"facing=west,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed",
"y": 270
},
"facing=east,ejecting=true": {
"model": "cobblemon:block/copper_oven_open",
"y": 90
},
"facing=north,ejecting=true": {
"model": "cobblemon:block/copper_oven_open"
},
"facing=south,ejecting=true": {
"model": "cobblemon:block/copper_oven_open",
"y": 180
},
"facing=west,ejecting=true": {
"model": "cobblemon:block/copper_oven_open",
"y": 270
}
}
}
@@ -1,19 +0,0 @@
{
"variants": {
"facing=east": {
"model": "cobblemon:block/copper_washer",
"y": 90
},
"facing=north": {
"model": "cobblemon:block/copper_washer"
},
"facing=south": {
"model": "cobblemon:block/copper_washer",
"y": 180
},
"facing=west": {
"model": "cobblemon:block/copper_washer",
"y": 270
}
}
}
@@ -1,469 +0,0 @@
{
"variants": {
"facing=east,power=0": {
"model": "cobblemon:block/exposed_copper_fan_unpowered",
"y": 90
},
"facing=north,power=0": {
"model": "cobblemon:block/exposed_copper_fan_unpowered"
},
"facing=south,power=0": {
"model": "cobblemon:block/exposed_copper_fan_unpowered",
"y": 180
},
"facing=west,power=0": {
"model": "cobblemon:block/exposed_copper_fan_unpowered",
"y": 270
},
"facing=east,power=1,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 90
},
"facing=north,power=1,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative"
},
"facing=south,power=1,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 180
},
"facing=west,power=1,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 270
},
"facing=east,power=1,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 90
},
"facing=north,power=1,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing"
},
"facing=south,power=1,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 180
},
"facing=west,power=1,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 270
},
"facing=east,power=2,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 90
},
"facing=north,power=2,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative"
},
"facing=south,power=2,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 180
},
"facing=west,power=2,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 270
},
"facing=east,power=2,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 90
},
"facing=north,power=2,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing"
},
"facing=south,power=2,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 180
},
"facing=west,power=2,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 270
},
"facing=east,power=3,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 90
},
"facing=north,power=3,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative"
},
"facing=south,power=3,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 180
},
"facing=west,power=3,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 270
},
"facing=east,power=3,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 90
},
"facing=north,power=3,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing"
},
"facing=south,power=3,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 180
},
"facing=west,power=3,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 270
},
"facing=east,power=4,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 90
},
"facing=north,power=4,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative"
},
"facing=south,power=4,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 180
},
"facing=west,power=4,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 270
},
"facing=east,power=4,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 90
},
"facing=north,power=4,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing"
},
"facing=south,power=4,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 180
},
"facing=west,power=4,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 270
},
"facing=east,power=5,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 90
},
"facing=north,power=5,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative"
},
"facing=south,power=5,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 180
},
"facing=west,power=5,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 270
},
"facing=east,power=5,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 90
},
"facing=north,power=5,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing"
},
"facing=south,power=5,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 180
},
"facing=west,power=5,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 270
},
"facing=east,power=6,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 90
},
"facing=north,power=6,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative"
},
"facing=south,power=6,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 180
},
"facing=west,power=6,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 270
},
"facing=east,power=6,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 90
},
"facing=north,power=6,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing"
},
"facing=south,power=6,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 180
},
"facing=west,power=6,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 270
},
"facing=east,power=7,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 90
},
"facing=north,power=7,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative"
},
"facing=south,power=7,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 180
},
"facing=west,power=7,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative",
"y": 270
},
"facing=east,power=7,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 90
},
"facing=north,power=7,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing"
},
"facing=south,power=7,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 180
},
"facing=west,power=7,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing",
"y": 270
},
"facing=east,power=8,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=8,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong"
},
"facing=south,power=8,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=8,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=8,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=8,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong"
},
"facing=south,power=8,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=8,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=9,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=9,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong"
},
"facing=south,power=9,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=9,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=9,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=9,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong"
},
"facing=south,power=9,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=9,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=10,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=10,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong"
},
"facing=south,power=10,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=10,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=10,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=10,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong"
},
"facing=south,power=10,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=10,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=11,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=11,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong"
},
"facing=south,power=11,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=11,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=11,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=11,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong"
},
"facing=south,power=11,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=11,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=12,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=12,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong"
},
"facing=south,power=12,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=12,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=12,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=12,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong"
},
"facing=south,power=12,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=12,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=13,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=13,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong"
},
"facing=south,power=13,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=13,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=13,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=13,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong"
},
"facing=south,power=13,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=13,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=14,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=14,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong"
},
"facing=south,power=14,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=14,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=14,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=14,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong"
},
"facing=south,power=14,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=14,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=15,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=15,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong"
},
"facing=south,power=15,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=15,push=false": {
"model": "cobblemon:block/exposed_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=15,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=15,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong"
},
"facing=south,power=15,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=15,push=true": {
"model": "cobblemon:block/exposed_copper_fan_pushing_strong",
"y": 270
}
}
}
@@ -1,34 +0,0 @@
{
"variants": {
"facing=east,half=lower": {
"model": "cobblemon:block/exposed_copper_fridge_bottom",
"y": 90
},
"facing=north,half=lower": {
"model": "cobblemon:block/exposed_copper_fridge_bottom"
},
"facing=south,half=lower": {
"model": "cobblemon:block/exposed_copper_fridge_bottom",
"y": 180
},
"facing=west,half=lower": {
"model": "cobblemon:block/exposed_copper_fridge_bottom",
"y": 270
},
"facing=east,half=upper": {
"model": "cobblemon:block/exposed_copper_fridge_top",
"y": 90
},
"facing=north,half=upper": {
"model": "cobblemon:block/exposed_copper_fridge_top"
},
"facing=south,half=upper": {
"model": "cobblemon:block/exposed_copper_fridge_top",
"y": 180
},
"facing=west,half=upper": {
"model": "cobblemon:block/exposed_copper_fridge_top",
"y": 270
}
}
}
@@ -1,19 +0,0 @@
{
"variants": {
"facing=east": {
"model": "cobblemon:block/exposed_copper_mower",
"y": 90
},
"facing=north": {
"model": "cobblemon:block/exposed_copper_mower"
},
"facing=south": {
"model": "cobblemon:block/exposed_copper_mower",
"y": 180
},
"facing=west": {
"model": "cobblemon:block/exposed_copper_mower",
"y": 270
}
}
}
@@ -1,64 +0,0 @@
{
"variants": {
"facing=east,cooking=closed,ejecting=false": {
"model": "cobblemon:block/exposed_copper_oven_closed",
"y": 90
},
"facing=north,cooking=closed,ejecting=false": {
"model": "cobblemon:block/exposed_copper_oven_closed"
},
"facing=south,cooking=closed,ejecting=false": {
"model": "cobblemon:block/exposed_copper_oven_closed",
"y": 180
},
"facing=west,cooking=closed,ejecting=false": {
"model": "cobblemon:block/exposed_copper_oven_closed",
"y": 270
},
"facing=east,cooking=open,ejecting=false": {
"model": "cobblemon:block/exposed_copper_oven_open",
"y": 90
},
"facing=north,cooking=open,ejecting=false": {
"model": "cobblemon:block/exposed_copper_oven_open"
},
"facing=south,cooking=open,ejecting=false": {
"model": "cobblemon:block/exposed_copper_oven_open",
"y": 180
},
"facing=west,cooking=open,ejecting=false": {
"model": "cobblemon:block/exposed_copper_oven_open",
"y": 270
},
"facing=east,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/exposed_copper_oven_closed",
"y": 90
},
"facing=north,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/exposed_copper_oven_closed"
},
"facing=south,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/exposed_copper_oven_closed",
"y": 180
},
"facing=west,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/exposed_copper_oven_closed",
"y": 270
},
"facing=east,ejecting=true": {
"model": "cobblemon:block/exposed_copper_oven_open",
"y": 90
},
"facing=north,ejecting=true": {
"model": "cobblemon:block/exposed_copper_oven_open"
},
"facing=south,ejecting=true": {
"model": "cobblemon:block/exposed_copper_oven_open",
"y": 180
},
"facing=west,ejecting=true": {
"model": "cobblemon:block/exposed_copper_oven_open",
"y": 270
}
}
}
@@ -1,19 +0,0 @@
{
"variants": {
"facing=east": {
"model": "cobblemon:block/exposed_copper_washer",
"y": 90
},
"facing=north": {
"model": "cobblemon:block/exposed_copper_washer"
},
"facing=south": {
"model": "cobblemon:block/exposed_copper_washer",
"y": 180
},
"facing=west": {
"model": "cobblemon:block/exposed_copper_washer",
"y": 270
}
}
}
@@ -1,469 +0,0 @@
{
"variants": {
"facing=east,power=0": {
"model": "cobblemon:block/oxidized_copper_fan_unpowered",
"y": 90
},
"facing=north,power=0": {
"model": "cobblemon:block/oxidized_copper_fan_unpowered"
},
"facing=south,power=0": {
"model": "cobblemon:block/oxidized_copper_fan_unpowered",
"y": 180
},
"facing=west,power=0": {
"model": "cobblemon:block/oxidized_copper_fan_unpowered",
"y": 270
},
"facing=east,power=1,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 90
},
"facing=north,power=1,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative"
},
"facing=south,power=1,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 180
},
"facing=west,power=1,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 270
},
"facing=east,power=1,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 90
},
"facing=north,power=1,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing"
},
"facing=south,power=1,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 180
},
"facing=west,power=1,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 270
},
"facing=east,power=2,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 90
},
"facing=north,power=2,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative"
},
"facing=south,power=2,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 180
},
"facing=west,power=2,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 270
},
"facing=east,power=2,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 90
},
"facing=north,power=2,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing"
},
"facing=south,power=2,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 180
},
"facing=west,power=2,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 270
},
"facing=east,power=3,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 90
},
"facing=north,power=3,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative"
},
"facing=south,power=3,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 180
},
"facing=west,power=3,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 270
},
"facing=east,power=3,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 90
},
"facing=north,power=3,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing"
},
"facing=south,power=3,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 180
},
"facing=west,power=3,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 270
},
"facing=east,power=4,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 90
},
"facing=north,power=4,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative"
},
"facing=south,power=4,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 180
},
"facing=west,power=4,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 270
},
"facing=east,power=4,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 90
},
"facing=north,power=4,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing"
},
"facing=south,power=4,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 180
},
"facing=west,power=4,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 270
},
"facing=east,power=5,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 90
},
"facing=north,power=5,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative"
},
"facing=south,power=5,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 180
},
"facing=west,power=5,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 270
},
"facing=east,power=5,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 90
},
"facing=north,power=5,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing"
},
"facing=south,power=5,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 180
},
"facing=west,power=5,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 270
},
"facing=east,power=6,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 90
},
"facing=north,power=6,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative"
},
"facing=south,power=6,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 180
},
"facing=west,power=6,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 270
},
"facing=east,power=6,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 90
},
"facing=north,power=6,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing"
},
"facing=south,power=6,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 180
},
"facing=west,power=6,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 270
},
"facing=east,power=7,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 90
},
"facing=north,power=7,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative"
},
"facing=south,power=7,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 180
},
"facing=west,power=7,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative",
"y": 270
},
"facing=east,power=7,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 90
},
"facing=north,power=7,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing"
},
"facing=south,power=7,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 180
},
"facing=west,power=7,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing",
"y": 270
},
"facing=east,power=8,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=8,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong"
},
"facing=south,power=8,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=8,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=8,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=8,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong"
},
"facing=south,power=8,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=8,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=9,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=9,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong"
},
"facing=south,power=9,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=9,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=9,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=9,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong"
},
"facing=south,power=9,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=9,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=10,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=10,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong"
},
"facing=south,power=10,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=10,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=10,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=10,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong"
},
"facing=south,power=10,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=10,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=11,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=11,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong"
},
"facing=south,power=11,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=11,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=11,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=11,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong"
},
"facing=south,power=11,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=11,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=12,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=12,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong"
},
"facing=south,power=12,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=12,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=12,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=12,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong"
},
"facing=south,power=12,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=12,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=13,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=13,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong"
},
"facing=south,power=13,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=13,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=13,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=13,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong"
},
"facing=south,power=13,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=13,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=14,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=14,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong"
},
"facing=south,power=14,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=14,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=14,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=14,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong"
},
"facing=south,power=14,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=14,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=15,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=15,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong"
},
"facing=south,power=15,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=15,push=false": {
"model": "cobblemon:block/oxidized_copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=15,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=15,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong"
},
"facing=south,power=15,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=15,push=true": {
"model": "cobblemon:block/oxidized_copper_fan_pushing_strong",
"y": 270
}
}
}
@@ -1,34 +0,0 @@
{
"variants": {
"facing=east,half=lower": {
"model": "cobblemon:block/oxidized_copper_fridge_bottom",
"y": 90
},
"facing=north,half=lower": {
"model": "cobblemon:block/oxidized_copper_fridge_bottom"
},
"facing=south,half=lower": {
"model": "cobblemon:block/oxidized_copper_fridge_bottom",
"y": 180
},
"facing=west,half=lower": {
"model": "cobblemon:block/oxidized_copper_fridge_bottom",
"y": 270
},
"facing=east,half=upper": {
"model": "cobblemon:block/oxidized_copper_fridge_top",
"y": 90
},
"facing=north,half=upper": {
"model": "cobblemon:block/oxidized_copper_fridge_top"
},
"facing=south,half=upper": {
"model": "cobblemon:block/oxidized_copper_fridge_top",
"y": 180
},
"facing=west,half=upper": {
"model": "cobblemon:block/oxidized_copper_fridge_top",
"y": 270
}
}
}
@@ -1,19 +0,0 @@
{
"variants": {
"facing=east": {
"model": "cobblemon:block/oxidized_copper_mower",
"y": 90
},
"facing=north": {
"model": "cobblemon:block/oxidized_copper_mower"
},
"facing=south": {
"model": "cobblemon:block/oxidized_copper_mower",
"y": 180
},
"facing=west": {
"model": "cobblemon:block/oxidized_copper_mower",
"y": 270
}
}
}
@@ -1,64 +0,0 @@
{
"variants": {
"facing=east,cooking=closed,ejecting=false": {
"model": "cobblemon:block/oxidized_copper_oven_closed",
"y": 90
},
"facing=north,cooking=closed,ejecting=false": {
"model": "cobblemon:block/oxidized_copper_oven_closed"
},
"facing=south,cooking=closed,ejecting=false": {
"model": "cobblemon:block/oxidized_copper_oven_closed",
"y": 180
},
"facing=west,cooking=closed,ejecting=false": {
"model": "cobblemon:block/oxidized_copper_oven_closed",
"y": 270
},
"facing=east,cooking=open,ejecting=false": {
"model": "cobblemon:block/oxidized_copper_oven_open",
"y": 90
},
"facing=north,cooking=open,ejecting=false": {
"model": "cobblemon:block/oxidized_copper_oven_open"
},
"facing=south,cooking=open,ejecting=false": {
"model": "cobblemon:block/oxidized_copper_oven_open",
"y": 180
},
"facing=west,cooking=open,ejecting=false": {
"model": "cobblemon:block/oxidized_copper_oven_open",
"y": 270
},
"facing=east,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/oxidized_copper_oven_closed",
"y": 90
},
"facing=north,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/oxidized_copper_oven_closed"
},
"facing=south,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/oxidized_copper_oven_closed",
"y": 180
},
"facing=west,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/oxidized_copper_oven_closed",
"y": 270
},
"facing=east,ejecting=true": {
"model": "cobblemon:block/oxidized_copper_oven_open",
"y": 90
},
"facing=north,ejecting=true": {
"model": "cobblemon:block/oxidized_copper_oven_open"
},
"facing=south,ejecting=true": {
"model": "cobblemon:block/oxidized_copper_oven_open",
"y": 180
},
"facing=west,ejecting=true": {
"model": "cobblemon:block/oxidized_copper_oven_open",
"y": 270
}
}
}
@@ -1,19 +0,0 @@
{
"variants": {
"facing=east": {
"model": "cobblemon:block/oxidized_copper_washer",
"y": 90
},
"facing=north": {
"model": "cobblemon:block/oxidized_copper_washer"
},
"facing=south": {
"model": "cobblemon:block/oxidized_copper_washer",
"y": 180
},
"facing=west": {
"model": "cobblemon:block/oxidized_copper_washer",
"y": 270
}
}
}
@@ -1,469 +0,0 @@
{
"variants": {
"facing=east,power=0": {
"model": "cobblemon:block/copper_fan_unpowered",
"y": 90
},
"facing=north,power=0": {
"model": "cobblemon:block/copper_fan_unpowered"
},
"facing=south,power=0": {
"model": "cobblemon:block/copper_fan_unpowered",
"y": 180
},
"facing=west,power=0": {
"model": "cobblemon:block/copper_fan_unpowered",
"y": 270
},
"facing=east,power=1,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 90
},
"facing=north,power=1,push=false": {
"model": "cobblemon:block/copper_fan_decorative"
},
"facing=south,power=1,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 180
},
"facing=west,power=1,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 270
},
"facing=east,power=1,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 90
},
"facing=north,power=1,push=true": {
"model": "cobblemon:block/copper_fan_pushing"
},
"facing=south,power=1,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 180
},
"facing=west,power=1,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 270
},
"facing=east,power=2,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 90
},
"facing=north,power=2,push=false": {
"model": "cobblemon:block/copper_fan_decorative"
},
"facing=south,power=2,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 180
},
"facing=west,power=2,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 270
},
"facing=east,power=2,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 90
},
"facing=north,power=2,push=true": {
"model": "cobblemon:block/copper_fan_pushing"
},
"facing=south,power=2,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 180
},
"facing=west,power=2,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 270
},
"facing=east,power=3,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 90
},
"facing=north,power=3,push=false": {
"model": "cobblemon:block/copper_fan_decorative"
},
"facing=south,power=3,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 180
},
"facing=west,power=3,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 270
},
"facing=east,power=3,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 90
},
"facing=north,power=3,push=true": {
"model": "cobblemon:block/copper_fan_pushing"
},
"facing=south,power=3,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 180
},
"facing=west,power=3,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 270
},
"facing=east,power=4,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 90
},
"facing=north,power=4,push=false": {
"model": "cobblemon:block/copper_fan_decorative"
},
"facing=south,power=4,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 180
},
"facing=west,power=4,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 270
},
"facing=east,power=4,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 90
},
"facing=north,power=4,push=true": {
"model": "cobblemon:block/copper_fan_pushing"
},
"facing=south,power=4,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 180
},
"facing=west,power=4,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 270
},
"facing=east,power=5,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 90
},
"facing=north,power=5,push=false": {
"model": "cobblemon:block/copper_fan_decorative"
},
"facing=south,power=5,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 180
},
"facing=west,power=5,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 270
},
"facing=east,power=5,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 90
},
"facing=north,power=5,push=true": {
"model": "cobblemon:block/copper_fan_pushing"
},
"facing=south,power=5,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 180
},
"facing=west,power=5,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 270
},
"facing=east,power=6,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 90
},
"facing=north,power=6,push=false": {
"model": "cobblemon:block/copper_fan_decorative"
},
"facing=south,power=6,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 180
},
"facing=west,power=6,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 270
},
"facing=east,power=6,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 90
},
"facing=north,power=6,push=true": {
"model": "cobblemon:block/copper_fan_pushing"
},
"facing=south,power=6,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 180
},
"facing=west,power=6,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 270
},
"facing=east,power=7,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 90
},
"facing=north,power=7,push=false": {
"model": "cobblemon:block/copper_fan_decorative"
},
"facing=south,power=7,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 180
},
"facing=west,power=7,push=false": {
"model": "cobblemon:block/copper_fan_decorative",
"y": 270
},
"facing=east,power=7,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 90
},
"facing=north,power=7,push=true": {
"model": "cobblemon:block/copper_fan_pushing"
},
"facing=south,power=7,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 180
},
"facing=west,power=7,push=true": {
"model": "cobblemon:block/copper_fan_pushing",
"y": 270
},
"facing=east,power=8,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=8,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=8,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=8,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=8,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=8,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=8,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=8,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=9,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=9,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=9,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=9,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=9,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=9,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=9,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=9,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=10,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=10,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=10,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=10,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=10,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=10,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=10,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=10,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=11,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=11,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=11,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=11,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=11,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=11,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=11,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=11,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=12,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=12,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=12,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=12,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=12,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=12,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=12,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=12,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=13,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=13,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=13,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=13,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=13,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=13,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=13,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=13,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=14,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=14,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=14,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=14,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=14,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=14,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=14,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=14,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
},
"facing=east,power=15,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 90
},
"facing=north,power=15,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong"
},
"facing=south,power=15,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 180
},
"facing=west,power=15,push=false": {
"model": "cobblemon:block/copper_fan_decorative_strong",
"y": 270
},
"facing=east,power=15,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 90
},
"facing=north,power=15,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong"
},
"facing=south,power=15,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 180
},
"facing=west,power=15,push=true": {
"model": "cobblemon:block/copper_fan_pushing_strong",
"y": 270
}
}
}
@@ -1,34 +0,0 @@
{
"variants": {
"facing=east,half=lower": {
"model": "cobblemon:block/copper_fridge_bottom",
"y": 90
},
"facing=north,half=lower": {
"model": "cobblemon:block/copper_fridge_bottom"
},
"facing=south,half=lower": {
"model": "cobblemon:block/copper_fridge_bottom",
"y": 180
},
"facing=west,half=lower": {
"model": "cobblemon:block/copper_fridge_bottom",
"y": 270
},
"facing=east,half=upper": {
"model": "cobblemon:block/copper_fridge_top",
"y": 90
},
"facing=north,half=upper": {
"model": "cobblemon:block/copper_fridge_top"
},
"facing=south,half=upper": {
"model": "cobblemon:block/copper_fridge_top",
"y": 180
},
"facing=west,half=upper": {
"model": "cobblemon:block/copper_fridge_top",
"y": 270
}
}
}
@@ -1,19 +0,0 @@
{
"variants": {
"facing=east": {
"model": "cobblemon:block/copper_mower",
"y": 90
},
"facing=north": {
"model": "cobblemon:block/copper_mower"
},
"facing=south": {
"model": "cobblemon:block/copper_mower",
"y": 180
},
"facing=west": {
"model": "cobblemon:block/copper_mower",
"y": 270
}
}
}
@@ -1,64 +0,0 @@
{
"variants": {
"facing=east,cooking=closed,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed",
"y": 90
},
"facing=north,cooking=closed,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed"
},
"facing=south,cooking=closed,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed",
"y": 180
},
"facing=west,cooking=closed,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed",
"y": 270
},
"facing=east,cooking=open,ejecting=false": {
"model": "cobblemon:block/copper_oven_open",
"y": 90
},
"facing=north,cooking=open,ejecting=false": {
"model": "cobblemon:block/copper_oven_open"
},
"facing=south,cooking=open,ejecting=false": {
"model": "cobblemon:block/copper_oven_open",
"y": 180
},
"facing=west,cooking=open,ejecting=false": {
"model": "cobblemon:block/copper_oven_open",
"y": 270
},
"facing=east,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed",
"y": 90
},
"facing=north,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed"
},
"facing=south,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed",
"y": 180
},
"facing=west,cooking=cooking,ejecting=false": {
"model": "cobblemon:block/copper_oven_closed",
"y": 270
},
"facing=east,ejecting=true": {
"model": "cobblemon:block/copper_oven_open",
"y": 90
},
"facing=north,ejecting=true": {
"model": "cobblemon:block/copper_oven_open"
},
"facing=south,ejecting=true": {
"model": "cobblemon:block/copper_oven_open",
"y": 180
},
"facing=west,ejecting=true": {
"model": "cobblemon:block/copper_oven_open",
"y": 270
}
}
}
Loaded 100 of 425 files, more files were not shown because too many files have changed in this diff. Show more