Compare commits

..
4256 changed files with 22847 additions and 281560 deletions

No files matched your search

+34 -144
View File
@@ -8,53 +8,32 @@
- Added big roots, generating from cave ceiling dirt and sometimes spreading into energy roots.
- Added trading between players. Press R while looking at another player, you'll figure it out.
- Added pasture blocks, used to let your PC Pokémon roam around your base.
- Added 69 berry trees. Some are found in village farms, some from planting different berries close to each other.
- Added medicine brewing using medicinal leeks and berries in brewing stands.
- Added Pokémon cries when in battles and being sent out.
- Added support for scale in animations.
- Added support for jump keyframes (i.e. pre and post keyframes)
- Added AI for Nosepass to point towards world spawn when idle.
- Made Nosepass point towards world spawn while idle.
- Added structure spawn conditions
- Added Pokémon cry system for battles and sending out.
- Added cries for Gen 1-9 starters and their evolutions.
- Added recipes for Berry Juice, Heal Powder, Remedy, Fine Remedy, Superb Remedy, Revive, Max Revive, HP Up, Protein, Iron, Calcium, Zinc, Carbos, PP Up, PP Max, Pasture, Medicinal Leek to Magenta Dye, Roasted Leek, Leek and Potato Stew, Braised Vivichoke, Vivichoke Dip, Mulch Base, Growth Mulch, Surprise Mulch, Coarse Mulch, Humid Mulch, Rich Mulch, Loamy Mulch, Peat Mulch, Sandy Mulch, X Attack, X Defense, X Special Attack, X Special Defense, X Speed, X Accuracy, Dire Hit, Guard Spec, Power Anklet, Power Band, Power Belt, Power Bracelet, Power Lens, Power Weight, Bright Powder, Destiny Knot
- Added cries for Farfetchd, Galarian Farfetched, Rookidee, Corvisquire, Corviknight, Caterpie, Metapod, Butterfree, Weedle, Kakuna, Beedrill, Pidgey, Pidgeotto, Pidgeot, Rattata, Raticate, Clefairy, Clefable, Tauros, Eevee, Vaporeon, Flareon, Jolteon, Chatot, Darmanitan, Darumaka, Lucario, Mimikyu, Quagsire, Riolu, Wooper, Cleffa, Miltank, Buneary, Lopunny, Wooloo, Dubwool.
- Added cries for Chatot, Darmanitan, Darumaka, Lucario, Mimikyu, Quagsire, Riolu, Wooper, Caterpie, Metapod, Butterfree, Weedle, Kakuna, Beedrill, Pidgey, Pidgeotto, Pidgeot, Rattata, Raticate, Cleffa, Clefairy, Clefable, Tauros, Miltank, Buneary, Lopunny, Wooloo, Dubwool.
- Added recipes for Berry Juice, Heal Powder, Remedy, Fine Remedy, Superb Remedy, Revive, Max Revive, HP Up, Protein, Iron, Calcium, Zinc, Carbos, PP Up, PP Max, Pasture, Medicinal Leek to Magenta Dye, Roasted Leek, Leek and Potato Stew, Braised Vivichoke, Vivichoke Dip, Mulch Base, Growth Mulch, Surprise Mulch, Coarse Mulch, Humid Mulch, Rich Mulch, Loamy Mulch, Peat Mulch, Sandy Mulch, Health Feather, Muscle Feather, Resist Feather, Genius Feather, Clever Feather, Genius Feather
- Added a shoulder mount for Mimikyu
- Added Advancement trigger for defeating Pokémon and collecting varieties of Pokémon.
- Added sleep animations to Arcanine, Jigglypuff, Wigglytuff, Vulpix and Ninetales.
- Added flying placeholder animations to Pidgey, Pidgeotto, Pidgeot, Golbat, Crobat, Scyther, Scizor, Zapdos, Moltres, Articuno, Dragonite, Rowlet, Dartrix, and Decidueye.
- Added loot to various vanilla chest loot tables (Link Cable in Ancient Cities, Woodland Mansions, End Cities, and Igloos, Vivichoke Seeds in Jungle Temples, Dungeons, and Plains, Savanna, Snowy, and Taiga Villages, and all 7 Apricorn Sprouts in Desert, Plains, Savanna, Snowy, and Taiga Villages, as well as the Bonus Chest, which can also have 5 of the basic Poké Ball types)
- Added loot to various vanilla chest loot tables (Link Cable in Ancient Cities, Woodland Mansions, End Cities, and Igloos, Vivichoke Seeds in Jungle Temples, Dungeons, and Plains, Savanna, Snowy, and Taiga Villages, and all 7 Apricorn Sprouts in Desert, Plains, Savanna, Snowy, and Taiga Villages, as well as the Bonus Chest)
- Added the `/teststore <player> <store> <properties>` command allowing command block/mcfunction users to query a party, PC or both for Pokémon matching specific properties and returning the match count, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.teststore` if a permission mod is present.
- Added the `/querylearnset <player> <slot> <move>` command allowing command block/mcfunction users to query a party slot and check if the Pokémon can learn a specific move returning a 1 if yes otherwise 0, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.querylearnset` if a permission mod is present.
- Added the `/testpcslot <player> <slot> <properties>` command allowing command block/mcfunction users to query a pc slot and check if the Pokémon matches specific properties returning a 1 if yes otherwise 0, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.testpcslot` if a permission mod is present.
- Added the `/testpartyslot <player> <slot> <properties>` command allowing command block/mcfunction users to query a party slot and check if the Pokémon matches a specific properties returning a 1 if yes otherwise 0, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.testpartyslot` if a permission mod is present.
- Added the `/clearparty <player>` command for emptying a player's party.
- Added the `/clearpc <player>` command for emptying a player's PC.
- Added the `/pokemonrestart <reset_starters>` and the `/pokemonrestartother <player> <reset_starters>` command allowing command block/mcfunction users to reset a players Pokémon data.
- Added support for "isBattle" and "isTouchingWater" properties on resource pack Pokémon poses. This allows your custom Pokémon to be posed differently when in battle.
- Added support for "isVisible" on a transformed part on resource pack Pokémon poses. This allows your custom Pokémon to have bones disappear in specific poses, such as hiding Greninja's throwing star when not in a battle pose.
- Added a "doShinyStarters" gamerule
- Added the `doPokemonLoot` gamerule to toggle Pokémon dropping items/exp on death.
- Added support for battle music. Sounds can be added to the ``battle.pvp.default`` and ``battle.pvw.default`` sound events.
- Added ability activation announcement when in battle.
- Added Auspicious Armor and Malicious Armor, which can be used to evolve Charcadet into Armarouge or Ceruledge respectively.
- Added animations for Wailord.
- Added Cherry Torterra variant.
- Added 2 new face spots for Spinda.
- Added Forretress Shulker variant.
- Added 'enabled' optional property on model layers, allowing later variations to disable previously-defined layers. See [this issue](https://gitlab.com/cable-mc/cobblemon/-/issues/335) for how this looks.
### Pokémon Added
#### Gen 2
- Chikorita
- Bayleef
- Meganium
- Totodile
- Croconaw
- Feraligatr
- Cyndaquil
- Quilava
- Typhlosion
- Spinarak
- Ariados
- Shuckle
@@ -68,12 +47,6 @@
- Flaaffy
- Ampharos
- Sudowoodo
- Snubbull
- Granbull
- Phanpy
- Donphan
- Teddiursa
- Ursaring
#### Gen 3
@@ -100,22 +73,11 @@
- Chimecho
- Barboach
- Whiscash
- Volbeat
- Illumise
- Zigzagoon
- Linoone
- Ralts
- Kirlia
- Gardevoir
- Nincada
- Ninjask
- Shedinja
- Beldum
- Metang
- Metagross
#### Gen 4
- Cherubi (Planned)
- Cherrim (Planned)
- Carnivine
- Shinx
- Luxio
@@ -128,12 +90,6 @@
- Chatot
- Combee
- Vespiquen
- Buizel
- Floatzel
- Starly
- Staravia
- Staraptor
- Gallade
#### Gen 5
@@ -144,59 +100,38 @@
- Venepede
- Whirlpede
- Scolipede
- Yamask
- Cofagrigus
- Patrat
- Watchog
- Yamask (Planned)
- Cofagrigus (Planned)
- Patrat (Planned)
- Watchog (Planned)
- Lillipup
- Herdier
- Stoutland
- Cottonee
- Whimsicott
- Pidove
- Tranquill
- Unfezant
- Timburr
- Gurdurr
- Conkeldurr
#### Gen 6
- Scatterbug
- Spewpa
- Vivillion
- Skrelp
- Dragalge
- Bunnelby
- Diggersby
- Phantump
- Trevenant
- Fletchling
- Fletchinder
- Talonflame
- Skrelp (Planned)
- Dragalge (Planned)
- Bunnelby (Planned)
- Diggersby (Planned)
#### Gen 7
- Wishiwashi
- Wishiwashi (Planned)
- Cutiefly
- Ribombee
- Stufful
- Bewear
- Comfey
- Alolan Exeggutor
- Alolan Raichu
- Alolan Meowth
- Alolan Persian
- Komala
- Wimpod
- Golisopod
- Crabrawler
- Crabominable
#### Gen 8
- Arrokuda
- Barraskewda
- Arrokuda (Planned)
- Barraskewda (Planned)
- Nickit
- Thievul
- Falinks
@@ -205,28 +140,12 @@
- Rookidee
- Corvisquire
- Corviknight
- Galarian Ponyta
- Galarian Rapidash
- Yamper
- Boltund
- Galarian Zigzagoon
- Galarian Linoone
- Obstagoon
- Galarian Meowth
- Perrserker
- Ursaluna
#### Gen 9
- Sprigatitto
- Floragato
- Meowscarada
- Fuecoco
- Crocalor
- Skeledirge
- Quaxly
- Quaxwell
- Quaquaval
- Flittle
- Espathra
- Garganacl
@@ -236,29 +155,20 @@
- Ceruledge
- Cetoddle
- Cetitan
- Shroodle
- Grafaiai
- Shroodle (Planned)
- Grafaiai (Planned)
- Tandemaus
- Maushold
- Varoom
- Revavroom
- Squawkabilly
- Squawkabilly (Planned)
- Glimmet
- Glimmora
- Annihilape
- Tinkatink
- Tinkatuff
- Tinkaton
- Maschiff
- Mabosstiff
- Lechonk
- Oinkologne
- Paldean Wooper
- Clodsire
### Changes
- Updated model and textures of Spiritomb, Koffing, Weezing, Kadabra, Alakazam, Emolga, Oshawott, Doduo, Dodrio, Dratini, Dragonair, Dragonite, Sneasel, Weavile, Gyarados, Hitmonlee, Hitmonchan, Chesnaught, Spinda, Mamoswine, Steelix, Midreavus, Mismagius, Buneary, Lopunny, Golduck, Meowth, Persian, Fennekin, Braixen, Delphox, Snivy, Servine, Serperior, Ratatta, Raticate, Nidorina, Nidoqueen, NidoranM, Nidoking, Riolu, Lucario, Haunter, Mankey, Primeape, Mew, Mewtwo, Arcanine, Magnemite, Magneton, Magnezone, Exeggcute, Exeggutor, Elekid, Electabuzz, Electivire, Pichu, Pikachu, Raichu, Gengar, Wooper, Drowzee, Hypno, Aerodactyl, Spearow, Fearow, Lickitung, Pidgey, Pidgeotto, Pidgeot, Scyther, Scizor, Kleavor, Popplio, Brionne, Primarina, Torchic, Combusken, Blaziken, Aerodacyl, Scyther, Scizor, Kleavor, Lickitung, Lickilicky, Happiny, Chansey and Blissey.
- Updated animations for Steelix, Turtwig, Grotle, Torterra, Ponyta, Rapidash, Piplup, Prinplup, Empoleon, Drowzee, Hypno, Farfetch'd, Exeggcute, Exeggutor, Bidoof, Chimecho, Lickytung, Lickilicky, Popplio, Brionne, Luvdisc, Chimchar, Monferno, Infernape, Sobble, Drizzile, Inteleon, Greninja, Heatmor, Aerodactyl, Ditto, Lotad, Lombre, Ludicolo, Pumpkaboo, Gourgeist.
- Updated model and textures of Arcanine, Magnemite, Magneton, Magnezone, Exeggcute, Exeggutor, Farfetchd, Elekid, Electabuzz, Electivire, Pichu, Pikachu, Raichu, Gengar, Wooper Drowzee, Hypno, Aerodactyl, Spearow, Fearow, Lickitung, Pidgeotto, Pidgeot, Scyther, Scizor, Kleavor, Popplio, Brionne, Primarina, Torchic, Combusken, Blaziken, Aerodacyl, Scyther, Scizor, Kleavor, Lickitung, Lickilicky, Happiny, Chansey, Pidgey, Pidgeotto, Pidgeot, Spearow, Fearow, Drowzee, Hypno, Arcanine, Magnemite, Magneton, Magnezone, Exeggcute, Exeggutor, Farfetchd, Elekid, Electabuzz, Electivire, Pichu, Pikachu, Raichu, and Gengar.
- Updated models of Sceptile and Shuckle.
- Updated sprites for EV medicines, the rare candy, and the apricorn door item.
- Updated textures for apricorn doors and all the evolution stone ores.
- Updated Apricorn Leaves color.
@@ -270,30 +180,23 @@
- Cobblemon items can now all have their own tooltips via resourcepacks, to add a tooltip simply add a lang entry like "item.cobblemon.{item_id}.tooltip", if you want to add multiple tooltip lines you can do so with "item.cobblemon.{item_id}.tooltip_1" and upwards.
- Updated texture of Weedle.
- Updated shiny texture of Dwebble and Crustle.
- Updated Animations for Piplup, Prinplup, Empoleon, Drowzee, Hypno, Farfetch'd, Exeggcute, Exeggutor, Bidoof, Chimecho, Lickytung, Lickilicky, Popplio, Brionne, Luvdisc, Chimchar, Monferno, Infernape, Sobble, Drizzile, Inteleon, Greninja, Heatmor, Aerodactyl, Ditto, Lotad, Lombre, Ludicolo, Pumpkaboo, Gourgeist.
- Reorganised the advancements recipes folder
- Pokéedit command now supports IVs and EVs.
- Pokeedit command now supports IVs and EVs.
- Reorganised creative categories
- Pokémon can now wander into non-solid blocks such as foliage
- Pokéballs now despawn after 30 seconds
- Pokemon can now wander into non-solid blocks such as foliage
- Pokeballs now despawn after 30 seconds
- Dive Balls will now have the same motion speed underwater as if they were thrown in the air.
- Hardcoded potion shoulder effects have been removed, you can now use any potion vanilla or otherwise with the parameters you'd like, for more information see the [Datapack & Resourcepack Creators](#datapack-&-resourcepack-creators) section.
- Clicking categories of the Stat sub-section or the party reorder button in the Summary screen will now produce a click sound.
- Updated PC Recipe.
- Improved Pokémon AI and movement.
- Friendship will slowly increase when Pokémon are shoulder-mounted.
- Master balls are now unable to be burned when dropped into fire/lava.
- Pokémon will appear red when hurt, like regular entities, except when they're fainting.
- Ponyta and Rapidash now have animated textures.
- Pokémon's air meter no longer depletes while battling underwater.
- Sleeping partially restores PP of Pokémon
- Item interaction evolutions and held item requirements now support NBT by creating an object JSON containing the key ``item`` for what used to be the existing condition support and a ``nbt`` key for the NBT format, this is the string [format](https://minecraft.fandom.com/wiki/NBT_format) expected in commands. Existing data does not need to be updated.
- Shoulder mounts now match the shoulder position a bit more accurately when sneaking.
- Poison Heal will now cause poisoned Pokémon to heal outside of battle.
### Fixes
- Fixed spawning moon phase dependent Pokémon only when the moon phase is wrong
- Fixed large Pokémon spawning partially inside walls where they suffocate.
- Fixed messages for entry hazards, screens, weather, damage, healing, Tailwind, Perish Song, Destiny Bond, Shed Skin, Uproar, Forewarn, Disguise, Arena Trap, Yawn, Curse, Clamp, Whirlpool, Liquid Ooze, Miracle Eye, Safeguard, Magic Bounce, Lock On, Focus Energy, Confusion, and more.
- Fixed messages for entry hazards, screens, Tailwind, Perish Song, Destiny Bond, Shed Skin, Uproar, Forewarn, Disguise, Arena Trap, Yawn, Curse, Clamp, Whirlpool, Liquid Ooze, Miracle Eye and Safeguard.
- Fixed Porygon not evolving with an Upgrade.
- Fixed super sized Pumpkaboo not having any moves.
- Fixed Infernape look animation.
@@ -319,30 +222,18 @@
- Fixed particle animations not running when a Pokémon is off-screen.
- Fixed Pokémon variants and layers not rendering correctly when shouldered and playing on a dedicated server, existing shoulders affected will need to be retrieved and shouldered again.
- Shoulder effects will now stay applied through situations that remove potion effects such as drinking milk.
- Fixed Shedinja evolving to use the consumed Pokeball and removed the held item.
- Fixed Shedninja evolving to use the consumed Pokeball and removed the held item.
- Fixed non-existent species in spawn pool file causing random species to spawn.
- Fixed shearing Pokémon dropping 0-2 wool instead of 1-3.
- Fixed some alignment issues in the stat hexagon of the summary menu.
- Fixed capture calculations not applying ball bonuses entirely correctly.
- Fixed Shedinja healing above 1 HP.
- Fixed battles soft-locking when consecutive Pokémon faint on switch-in.
- Fixed timing and color of battle window messages.
- Fixed players being able to trade, battle and let out their Pokémon while in spectator mode.
- Fixed Galarian Yamask not being able to evolve and by proxy the ``damage_taken`` evolution requirement.
- Fixed Bisharp not being able to evolve and by proxy the ``defeat`` evolution requirement.
- Fixed White-Striped Basculin not being able to evolve and by proxy the ``recoil`` evolution requirement.
- Fixed Primeape, Qwilfish and Stantler not being able to evolve and by proxy the ``use_move`` evolution requirement.
- Fixed Bramblin, Pawmo, and Rellor not being able to evolve and by proxy the ``blocks_traveled`` evolution requirement.
- Fixed displayName property in spawn files not doing what it's meant to do.
### Developer
- Added SpawnEvent, ThrownPokeballHitEvent, PokemonSentEvent, PokemonRecalledEvent.
- Added BattleFledEvent, BattleStartedEvent, BattleFaintedEvent.
- Added persistent NBT property inside Pokémon to store quick and simple data.
- Added SpawnEvent
- Added persistent NBT property inside Pokemon to store quick and simple data.
- Species and FormData have had their evolutions, pre-evolution and labels properties exposed. It is still recommended to work using a Pokémon instance when possible.
- Added capture check to BattleVictoryEvent.
- Added capture check to BattleVictoryEvent
- Added ThownPokeballHitEvent
- The various hardcoded potion shoulder effects have been removed, make use of PotionBaseEffect.
- Added ContextManager for tracking causes and contexts of conditions created during a battle. See BattleContext for types of conditions that are tracked.
### Datapack & Resourcepack Creators
- All potion related shoulder effects have had their IDs changed, they now all share the same type being `potion_effect` and use the vanilla Potion data [parameters](https://minecraft.fandom.com/wiki/Potion#Item_data), take for example the default Pidgey asset:
@@ -356,7 +247,6 @@
"showIcon": false
}
```
- Renamed the ``walked_steps`` evolution requirement to ``blocks_traveled``.
## [1.3.1 (March 31st, 2023)](#1-3-1)
@@ -1,13 +1,3 @@
/*
*
* * 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/.
*
*/
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
val accessWidenerFile = "src/main/resources/cobblemon-common.accesswidener"
@@ -23,8 +13,6 @@ plugins {
repositories {
mavenCentral()
//JEI
maven("https://maven.blamejared.com/")
}
license {
@@ -67,4 +55,4 @@ tasks {
withType<Jar> {
from(rootProject.file("LICENSE"))
}
}
}
-11
View File
@@ -1,13 +1,3 @@
/*
*
* * 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/.
*
*/
plugins {
id("cobblemon.base-conventions")
id("cobblemon.publish-conventions")
@@ -28,7 +18,6 @@ dependencies {
modImplementation(libs.fabricLoader)
modApi(libs.molang)
compileOnlyApi(libs.jeiApi)
// For Showdown
modCompileOnly(libs.graal)
@@ -1,16 +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.bridges;
import net.minecraft.structure.processor.StructureProcessor;
public interface StructureProcessorListBridge {
void append(StructureProcessor processor);
void clear();
}
@@ -8,8 +8,6 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pose;
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityModel;
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
@@ -40,7 +38,7 @@ public interface Bone {
/**
* Renders a particular bone in relation to a particular entity.
*
* @param context The render context this bone is being drawn for.
* @param entity The entity this bone is being drawn for. This parameter need not be available and can be null
* @param stack The current [MatrixStack] for the renderer
* @param buffer The vertex consumer deployed by the renderer
* @param packedLight The light level to be applied to the bone
@@ -52,8 +50,8 @@ public interface Bone {
*
* @since 1.4.0
*/
void render(
RenderContext context,
<T extends Entity> void render(
@Nullable T entity,
MatrixStack stack,
VertexConsumer buffer,
int packedLight,
@@ -1,44 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.block.PreEmptsExplosion;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.explosion.Explosion;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(value = Explosion.class)
public class ExplosionMixin {
@Shadow @Final private World world;
@Inject(
method = "affectWorld",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/block/BlockState;getBlock()Lnet/minecraft/block/Block;"
),
locals = LocalCapture.CAPTURE_FAILSOFT
)
public void cobblemon$whenExploded(boolean particles, CallbackInfo ci, boolean bl, ObjectArrayList<?> objectArrayList, boolean bl2, ObjectListIterator<?> var5, BlockPos blockPos, BlockState blockState) {
if (blockState.getBlock() instanceof PreEmptsExplosion preExplosionBlock) {
preExplosionBlock.whenExploded(world, blockState, blockPos);
}
}
}
@@ -1,52 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.api.tags.CobblemonBiomeTags;
import com.cobblemon.mod.common.music.CustomMusic;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.sound.MusicSound;
import net.minecraft.world.biome.Biome;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@Mixin(MinecraftClient.class)
public class MinecraftClientMixin {
@Shadow
public ClientPlayerEntity player;
@Inject(method = "getMusicType", at = @At("RETURN"), cancellable = true)
private void cobblemon$getMusicType(CallbackInfoReturnable<MusicSound> cir) {
if (this.player != null) {
RegistryEntry<Biome> playerBiome = this.player.getWorld().getBiome(this.player.getBlockPos());
List<MusicSound> possibleTracks = new ArrayList<>();
possibleTracks.add(cir.getReturnValue());
// Custom BiomeTag check to add music to certain biomes
if (playerBiome.isIn(CobblemonBiomeTags.IS_ABYSS)) {
possibleTracks.add(CustomMusic.DEEP_DARK);
}
cir.setReturnValue(possibleTracks.get(new Random().nextInt(possibleTracks.size())));
}
}
}
@@ -8,9 +8,7 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityModel;
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone;
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
@@ -38,7 +36,7 @@ public abstract class ModelPartMixin implements Bone {
}
@Override
public void render(RenderContext context, MatrixStack stack, VertexConsumer buffer, int packedLight, int packedOverlay, float r, float g, float b, float a) {
public <T extends Entity> void render(T entity, MatrixStack stack, VertexConsumer buffer, int packedLight, int packedOverlay, float r, float g, float b, float a) {
this.render(stack, buffer, packedLight, packedOverlay, r, g, b, a);
}
@@ -1,32 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.mojang.datafixers.util.Pair;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.structure.pool.StructurePool;
import net.minecraft.structure.pool.StructurePoolElement;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.List;
@Mixin(StructurePool.class)
public interface StructurePoolAccessor {
@Accessor("elementCounts")
List<Pair<StructurePoolElement, Integer>> getElementCounts();
@Mutable
@Accessor("elementCounts")
void setElementCounts(List<Pair<StructurePoolElement, Integer>> elementCounts);
@Accessor("elements")
ObjectArrayList<StructurePoolElement> getElements();
}
@@ -1,217 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.world.CobblemonStructureIDs;
import net.minecraft.registry.Registry;
import net.minecraft.structure.PoolStructurePiece;
import net.minecraft.structure.StructureTemplateManager;
import net.minecraft.structure.pool.*;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.random.Random;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.HeightLimitView;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.gen.noise.NoiseConfig;
import org.apache.commons.lang3.mutable.MutableObject;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.*;
@Mixin(StructurePoolBasedGenerator.StructurePoolGenerator.class)
public abstract class StructurePoolGeneratorMixin {
@Final
@Mutable
@Shadow Deque<StructurePoolBasedGenerator.ShapedPoolStructurePiece> structurePieces;
Map<String, Integer> generatedStructureGroupCounts;
private static final Map<String, Integer> structureMaxes;
private static final Map<Identifier, Set<String>> structureGroups = new HashMap<>();
static {
String pokecenter = "pokecenter";
String berry = "berry_farm";
structureGroups.put(CobblemonStructureIDs.PLAINS_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.DESERT_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.SAVANNA_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.SNOWY_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.TAIGA_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.SAVANNA_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.SAVANNA_BERRY_LARGE, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.PLAINS_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.PLAINS_BERRY_LARGE, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.SNOWY_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.SNOWY_BERRY_LARGE, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.DESERT_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.DESERT_BERRY_LARGE, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.TAIGA_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.TAIGA_BERRY_LARGE, Set.of(berry));
//Mapped using location string as key
Map<String, Integer> aMap = new HashMap<>();
aMap.put(pokecenter, 1);
aMap.put(berry, 2);
// aMap.put(CobblemonStructureIDs.PLAINS_LONG_PATH, 3);
// aMap.put(CobblemonStructureIDs.DESERT_LONG_PATH, 3);
// aMap.put(CobblemonStructureIDs.SAVANNA_LONG_PATH, 3);
// aMap.put(CobblemonStructureIDs.SNOWY_LONG_PATH, 3);
// aMap.put(CobblemonStructureIDs.TAIGA_LONG_PATH, 3);
structureMaxes = Collections.unmodifiableMap(aMap);
}
public Set<String> getGroups(Identifier structureIdentifier) {
return structureGroups.getOrDefault(structureIdentifier, Set.of());
}
public boolean hasReachedMaximum(Identifier structureIdentifier) {
Set<String> groups = getGroups(structureIdentifier);
for (String group : groups) {
int count = generatedStructureGroupCounts.getOrDefault(group, 0);
if (count >= structureMaxes.getOrDefault(group, Integer.MAX_VALUE)) {
return true;
}
}
return false;
}
public void incrementStructureCount(Identifier structureIdentifier) {
Set<String> groups = getGroups(structureIdentifier);
for (String group : groups) {
generatedStructureGroupCounts.put(group, generatedStructureGroupCounts.getOrDefault(group, 0) + 1);
}
}
@Inject(method = "<init>", at = @At("RETURN"))
private void onStructurePoolGeneratorCreation(Registry<StructurePool> registry, int maxSize, ChunkGenerator chunkGenerator, StructureTemplateManager structureTemplateManager, List<? super PoolStructurePiece> children, Random random, CallbackInfo ci) {
generatedStructureGroupCounts = new HashMap<>();
}
@ModifyVariable(method = "generatePiece", at = @At("STORE"), ordinal = 1)
private Iterator<StructurePoolElement> reduceStructurePoolElementIterator(Iterator<StructurePoolElement> iterator) {
List<StructurePoolElement> reducedList = new ArrayList<>();
while (iterator.hasNext()) {
StructurePoolElement structure = iterator.next();
Identifier structurePieceLocationKey = getCobblemonOnlyLocation(structure);
if (structurePieceLocationKey == null) {
reducedList.add(structure);
continue;
}
if (!hasReachedMaximum(structurePieceLocationKey)) {
reducedList.add(structure);
}
}
return reducedList.iterator();
}
@ModifyVariable(method = "generatePiece", at = @At("STORE"), ordinal = 1)
private PoolStructurePiece injected(PoolStructurePiece poolStructurePiece) {
Identifier structureLocationKey = getCobblemonOnlyLocation(poolStructurePiece.getPoolElement());
if (structureLocationKey != null) {
incrementStructureCount(structureLocationKey);
}
return poolStructurePiece;
}
// This doesn't SEEM necessary? It appears to work without.
@Inject(method = "generatePiece", at = @At("HEAD"))
private void beforeGeneratePiece(PoolStructurePiece piece, MutableObject<VoxelShape> pieceShape, int minY, boolean modifyBoundingBox, HeightLimitView world, NoiseConfig noiseConfig, CallbackInfo ci) {
// Identifier structureLocationKey = getCobblemonOnlyLocation(piece.getPoolElement());
//
// if (structureLocationKey != null) {
// Integer currentlyGenerated = generatedStructureCounts.get(structureLocationKey);
// if (currentlyGenerated == null) currentlyGenerated = 0;
// generatedStructureCounts.put(structureLocationKey, currentlyGenerated + 1);
// }
//
// List<StructurePoolBasedGenerator.ShapedPoolStructurePiece> reducedStructurePiecesList = structurePieces.stream().toList();
//
// for (Identifier maxStructureLocationKey : structureMaxes.keySet()) {
// Integer maxAllowed = structureMaxes.get(maxStructureLocationKey);
//
// Integer currentlyGenerated = generatedStructureCounts.get(maxStructureLocationKey);
// if (currentlyGenerated == null) currentlyGenerated = 0;
// if (currentlyGenerated < maxAllowed) {
// continue;
// }
//
// //Already have max so need to remove
// reducedStructurePiecesList = reducedStructurePiecesList.stream()
// .filter(shapedStructurePiece -> {
// Identifier locationKey = getCobblemonOnlyLocation(shapedStructurePiece.piece.getPoolElement());
// if (locationKey == null) {
// return true;
// }
//
// return !locationKey.equals(maxStructureLocationKey);
// })
// .collect(Collectors.toList());
// }
//
// structurePieces = new ArrayDeque<>(reducedStructurePiecesList);
}
private static Identifier getCobblemonOnlyLocation(StructurePoolElement structurePoolElement) {
Identifier location = getLocationIfAvailable(structurePoolElement);
if (location == null) return null;
if (!location.getNamespace().equals("cobblemon")) return null;
return location;
}
private static Identifier getLocationIfAvailable(StructurePoolElement structurePoolElement) {
if (structurePoolElement instanceof LegacySinglePoolElement legacySinglePoolElement) {
if (legacySinglePoolElement.location.left().isEmpty()) return null;
return legacySinglePoolElement.location.left().get();
} else if (structurePoolElement instanceof SinglePoolElement singlePoolElement) {
if (singlePoolElement.location.left().isEmpty()) return null;
return singlePoolElement.location.left().get();
} else {
return null;
}
}
// private List<StructurePoolElement> removeInstanceOfLocationKeyFrom(List<StructurePoolElement> structureList, Integer allowedNumberOfInstances, Identifier locationKey) {
// List<StructurePoolElement> reducedList = new ArrayList<>();
// int instancesFound = 0;
//
// for (StructurePoolElement structurePoolElement: structureList) {
// Identifier structureKey = getCobblemonOnlyLocation(structurePoolElement);
// if (structureKey == null || !structureKey.equals(locationKey)) {
// reducedList.add(structurePoolElement);
// continue;
// }
//
// if (instancesFound >= allowedNumberOfInstances) {
// continue;
// }
//
// reducedList.add(structurePoolElement);
// instancesFound++;
// }
//
// return reducedList;
// }
}
@@ -1,43 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.bridges.StructureProcessorListBridge;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.structure.processor.StructureProcessor;
import net.minecraft.structure.processor.StructureProcessorList;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(StructureProcessorList.class)
public class StructureProcessorListMixin implements StructureProcessorListBridge {
@Final
@Mutable
@Shadow
private List<StructureProcessor> list;
@Override
public void append(StructureProcessor processor) {
List<StructureProcessor> mutable = new ArrayList<>(this.list);
mutable.add(processor);
this.list = ImmutableList.copyOf(mutable);
}
@Override
public void clear() {
this.list = ImmutableList.of();
}
}
@@ -18,9 +18,7 @@ import com.cobblemon.mod.common.api.drop.ItemDropEntry
import com.cobblemon.mod.common.api.events.CobblemonEvents.BATTLE_VICTORY
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
import com.cobblemon.mod.common.api.events.CobblemonEvents.EVOLUTION_COMPLETE
import com.cobblemon.mod.common.api.events.CobblemonEvents.LEVEL_UP_EVENT
import com.cobblemon.mod.common.api.events.CobblemonEvents.POKEMON_CAPTURED
import com.cobblemon.mod.common.api.events.CobblemonEvents.TRADE_COMPLETED
import com.cobblemon.mod.common.api.net.serializers.PoseTypeDataSerializer
import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
import com.cobblemon.mod.common.api.net.serializers.Vec3DataSerializer
@@ -63,7 +61,12 @@ import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.ShowdownThread
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.command.argument.*
import com.cobblemon.mod.common.command.argument.MoveArgumentType
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
import com.cobblemon.mod.common.command.argument.PokemonArgumentType
import com.cobblemon.mod.common.command.argument.PokemonPropertiesArgumentType
import com.cobblemon.mod.common.command.argument.PokemonStoreArgumentType
import com.cobblemon.mod.common.command.argument.SpawnBucketArgumentType
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.config.LastChangedVersion
import com.cobblemon.mod.common.config.constraint.IntConstraint
@@ -88,10 +91,25 @@ import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.trade.TradeManager
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifDedicatedServer
import com.cobblemon.mod.common.util.isLaterVersion
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.removeAmountIf
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.UUID
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
import net.minecraft.client.MinecraftClient
import net.minecraft.command.argument.serialize.ConstantArgumentSerializer
import net.minecraft.entity.data.TrackedDataHandlerRegistry
@@ -101,15 +119,6 @@ import net.minecraft.registry.RegistryKey
import net.minecraft.util.WorldSavePath
import net.minecraft.world.World
import org.apache.logging.log4j.LogManager
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.*
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
object Cobblemon {
const val MODID = "cobblemon"
@@ -303,8 +312,6 @@ object Cobblemon {
}
}
}
LEVEL_UP_EVENT.subscribe { AdvancementHandler.onLevelUp(it) }
TRADE_COMPLETED.subscribe { AdvancementHandler.onTradeCompleted(it) }
BagItems.observable.subscribe {
LOGGER.info("Starting dummy Showdown battle to force it to pre-load data.")
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
import com.cobblemon.mod.common.block.entity.PCBlockEntity
import com.cobblemon.mod.common.block.entity.PokemonPastureBlockEntity
@@ -28,7 +27,6 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
val HEALING_MACHINE: BlockEntityType<HealingMachineBlockEntity> = this.create("healing_machine", BlockEntityType.Builder.create(::HealingMachineBlockEntity, CobblemonBlocks.HEALING_MACHINE).build(null))
@JvmField
val PC: BlockEntityType<PCBlockEntity> = this.create("pc", BlockEntityType.Builder.create(::PCBlockEntity, CobblemonBlocks.PC).build(null))
val BERRY = this.create("berry", BlockEntityType.Builder.create(::BerryBlockEntity, *CobblemonBlocks.berries().values.toTypedArray()).build(null))
@JvmField
val PASTURE: BlockEntityType<PokemonPastureBlockEntity> = this.create("pasture", BlockEntityType.Builder.create(::PokemonPastureBlockEntity, CobblemonBlocks.PASTURE).build(null))
@@ -17,8 +17,6 @@ import com.cobblemon.mod.common.mixin.invoker.StairsBlockInvoker
import com.cobblemon.mod.common.mixin.invoker.TrapdoorBlockInvoker
import com.cobblemon.mod.common.mixin.invoker.*
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.block.BerryBlock
import net.minecraft.block.*
import net.minecraft.block.piston.PistonBehavior
import net.minecraft.entity.effect.StatusEffects
@@ -27,7 +25,6 @@ import net.minecraft.registry.Registry
import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.util.Identifier
import net.minecraft.util.math.intprovider.UniformIntProvider
@Suppress("SameParameterValue", "HasPlatformType", "MemberVisibilityCanBePrivate", "unused")
@@ -230,87 +227,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
private fun apricornBlock(name: String, apricorn: Apricorn): ApricornBlock = this.create(name, ApricornBlock(AbstractBlock.Settings.create().mapColor(apricorn.mapColor()).ticksRandomly().strength(Blocks.OAK_LOG.hardness, Blocks.OAK_LOG.blastResistance).sounds(BlockSoundGroup.WOOD).nonOpaque(), apricorn))
private val berries = mutableMapOf<Identifier, BerryBlock>()
val AGUAV_BERRY = this.berryBlock("aguav")
val APICOT_BERRY = this.berryBlock("apicot")
val ASPEAR_BERRY = this.berryBlock("aspear")
val BABIRI_BERRY = this.berryBlock("babiri")
val BELUE_BERRY = this.berryBlock("belue")
val BLUK_BERRY = this.berryBlock("bluk")
val CHARTI_BERRY = this.berryBlock("charti")
val CHERI_BERRY = this.berryBlock("cheri")
val CHESTO_BERRY = this.berryBlock("chesto")
val CHILAN_BERRY = this.berryBlock("chilan")
val CHOPLE_BERRY = this.berryBlock("chople")
val COBA_BERRY = this.berryBlock("coba")
val COLBUR_BERRY = this.berryBlock("colbur")
val CORNN_BERRY = this.berryBlock("cornn")
val CUSTAP_BERRY = this.berryBlock("custap")
val DURIN_BERRY = this.berryBlock("durin")
val ENIGMA_BERRY = this.berryBlock("enigma")
val FIGY_BERRY = this.berryBlock("figy")
val GANLON_BERRY = this.berryBlock("ganlon")
val GREPA_BERRY = this.berryBlock("grepa")
val HABAN_BERRY = this.berryBlock("haban")
val HONDEW_BERRY = this.berryBlock("hondew")
val HOPO_BERRY = this.berryBlock("hopo")
val IAPAPA_BERRY = this.berryBlock("iapapa")
val JABOCA_BERRY = this.berryBlock("jaboca")
val KASIB_BERRY = this.berryBlock("kasib")
val KEBIA_BERRY = this.berryBlock("kebia")
val KEE_BERRY = this.berryBlock("kee")
val KELPSY_BERRY = this.berryBlock("kelpsy")
val LANSAT_BERRY = this.berryBlock("lansat")
val LEPPA_BERRY = this.berryBlock("leppa")
val LIECHI_BERRY = this.berryBlock("liechi")
val LUM_BERRY = this.berryBlock("lum")
val MAGO_BERRY = this.berryBlock("mago")
val MAGOST_BERRY = this.berryBlock("magost")
val MARANGA_BERRY = this.berryBlock("maranga")
val MICLE_BERRY = this.berryBlock("micle")
val NANAB_BERRY = this.berryBlock("nanab")
val NOMEL_BERRY = this.berryBlock("nomel")
val OCCA_BERRY = this.berryBlock("occa")
val ORAN_BERRY = this.berryBlock("oran")
val PAMTRE_BERRY = this.berryBlock("pamtre")
val PASSHO_BERRY = this.berryBlock("passho")
val PAYAPA_BERRY = this.berryBlock("payapa")
val PECHA_BERRY = this.berryBlock("pecha")
val PERSIM_BERRY = this.berryBlock("persim")
val PETAYA_BERRY = this.berryBlock("petaya")
val PINAP_BERRY = this.berryBlock("pinap")
val POMEG_BERRY = this.berryBlock("pomeg")
val QUALOT_BERRY = this.berryBlock("qualot")
val RABUTA_BERRY = this.berryBlock("rabuta")
val RAWST_BERRY = this.berryBlock("rawst")
val RAZZ_BERRY = this.berryBlock("razz")
val RINDO_BERRY = this.berryBlock("rindo")
val ROSELI_BERRY = this.berryBlock("roseli")
val ROWAP_BERRY = this.berryBlock("rowap")
val SALAC_BERRY = this.berryBlock("salac")
val SHUCA_BERRY = this.berryBlock("shuca")
val SITRUS_BERRY = this.berryBlock("sitrus")
val SPELON_BERRY = this.berryBlock("spelon")
val STARF_BERRY = this.berryBlock("starf")
val TAMATO_BERRY = this.berryBlock("tamato")
val TANGA_BERRY = this.berryBlock("tanga")
val TOUGA_BERRY = this.berryBlock("touga")
val WACAN_BERRY = this.berryBlock("wacan")
val WATMEL_BERRY = this.berryBlock("watmel")
val WEPEAR_BERRY = this.berryBlock("wepear")
val WIKI_BERRY = this.berryBlock("wiki")
val YACHE_BERRY = this.berryBlock("yache")
fun berries() = this.berries.toMap()
private fun berryBlock(name: String): BerryBlock {
val identifier = cobblemonResource("${name}_berry")
val block = this.create(identifier.path, BerryBlock(identifier, AbstractBlock.Settings.copy(Blocks.WHEAT).dynamicBounds().sounds(BlockSoundGroup.CROP)))
this.berries[identifier] = block
return block
}
/**
* Helper method for creating logs
* copied over from Vanilla
@@ -332,4 +248,4 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
val block = BlocksInvoker.createLeavesBlock(BlockSoundGroup.GRASS)
return this.create(name, block)
}
}
}
@@ -44,7 +44,6 @@ object CobblemonCommands {
TestPcSlotCommand.register(dispatcher)
TestPartySlotCommand.register(dispatcher)
ClearPartyCommand.register(dispatcher)
ClearPCCommand.register(dispatcher)
PokemonRestartCommand.register(dispatcher)
}
}
@@ -13,27 +13,18 @@ import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.MintBlock.MintType
import com.cobblemon.mod.common.item.*
import com.cobblemon.mod.common.item.battle.DireHitItem
import com.cobblemon.mod.common.item.battle.GuardSpecItem
import com.cobblemon.mod.common.item.battle.XStatItem
import com.cobblemon.mod.common.item.berry.FriendshipRaisingBerryItem
import com.cobblemon.mod.common.item.berry.HealingBerryItem
import com.cobblemon.mod.common.item.berry.PPRestoringBerryItem
import com.cobblemon.mod.common.item.berry.PortionHealingBerryItem
import com.cobblemon.mod.common.item.berry.StatusCuringBerryItem
import com.cobblemon.mod.common.item.berry.VolatileCuringBerryItem
import com.cobblemon.mod.common.item.interactive.*
import com.cobblemon.mod.common.item.interactive.PotionItem
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.block.Block
import net.minecraft.block.ComposterBlock
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.effect.StatusEffectInstance
import net.minecraft.entity.effect.StatusEffects
import net.minecraft.item.*
@@ -41,8 +32,6 @@ import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.util.Identifier
import net.minecraft.world.World
@Suppress("unused")
object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<Item>>, Item>() {
@@ -231,82 +220,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField val PEAT_BLOCK = noSettingsItem("peat_block")
@JvmField val RAZOR_CLAW = noSettingsItem("razor_claw")
@JvmField val RAZOR_FANG = noSettingsItem("razor_fang")
@JvmField val AUSPICIOUS_ARMOR = heldItem("auspicious_armor")
@JvmField val MALICIOUS_ARMOR = heldItem("malicious_armor")
private val berries = mutableMapOf<Identifier, BerryItem>()
// Plants
@JvmField val ORAN_BERRY = berryItem("oran", HealingBerryItem(CobblemonBlocks.ORAN_BERRY) { CobblemonMechanics.berries.oranRestoreAmount })
@JvmField val CHERI_BERRY = berryItem("cheri", StatusCuringBerryItem(CobblemonBlocks.CHERI_BERRY, Statuses.PARALYSIS))
@JvmField val CHESTO_BERRY = berryItem("chesto", StatusCuringBerryItem(CobblemonBlocks.CHESTO_BERRY, Statuses.SLEEP))
@JvmField val PECHA_BERRY = berryItem("pecha", StatusCuringBerryItem(CobblemonBlocks.PECHA_BERRY, Statuses.POISON, Statuses.POISON_BADLY))
@JvmField val RAWST_BERRY = berryItem("rawst", StatusCuringBerryItem(CobblemonBlocks.RAWST_BERRY, Statuses.BURN))
@JvmField val ASPEAR_BERRY = berryItem("aspear", StatusCuringBerryItem(CobblemonBlocks.ASPEAR_BERRY, Statuses.FROZEN))
@JvmField val PERSIM_BERRY = berryItem("persim", VolatileCuringBerryItem(CobblemonBlocks.PERSIM_BERRY, "confusion"))
@JvmField val RAZZ_BERRY = berryItem("razz", CobblemonBlocks.RAZZ_BERRY)
@JvmField val BLUK_BERRY = berryItem("bluk", CobblemonBlocks.BLUK_BERRY)
@JvmField val NANAB_BERRY = berryItem("nanab", CobblemonBlocks.NANAB_BERRY)
@JvmField val WEPEAR_BERRY = berryItem("wepear", CobblemonBlocks.WEPEAR_BERRY)
@JvmField val PINAP_BERRY = berryItem("pinap", CobblemonBlocks.PINAP_BERRY)
@JvmField val OCCA_BERRY = berryItem("occa", CobblemonBlocks.OCCA_BERRY)
@JvmField val PASSHO_BERRY = berryItem("passho", CobblemonBlocks.PASSHO_BERRY)
@JvmField val WACAN_BERRY = berryItem("wacan", CobblemonBlocks.WACAN_BERRY)
@JvmField val RINDO_BERRY = berryItem("rindo", CobblemonBlocks.RINDO_BERRY)
@JvmField val YACHE_BERRY = berryItem("yache", CobblemonBlocks.YACHE_BERRY)
@JvmField val CHOPLE_BERRY = berryItem("chople", CobblemonBlocks.CHOPLE_BERRY)
@JvmField val KEBIA_BERRY = berryItem("kebia", CobblemonBlocks.KEBIA_BERRY)
@JvmField val SHUCA_BERRY = berryItem("shuca", CobblemonBlocks.SHUCA_BERRY)
@JvmField val COBA_BERRY = berryItem("coba", CobblemonBlocks.COBA_BERRY)
@JvmField val PAYAPA_BERRY = berryItem("payapa", CobblemonBlocks.PAYAPA_BERRY)
@JvmField val TANGA_BERRY = berryItem("tanga", CobblemonBlocks.TANGA_BERRY)
@JvmField val CHARTI_BERRY = berryItem("charti", CobblemonBlocks.CHARTI_BERRY)
@JvmField val KASIB_BERRY = berryItem("kasib", CobblemonBlocks.KASIB_BERRY)
@JvmField val HABAN_BERRY = berryItem("haban", CobblemonBlocks.HABAN_BERRY)
@JvmField val COLBUR_BERRY = berryItem("colbur", CobblemonBlocks.COLBUR_BERRY)
@JvmField val BABIRI_BERRY = berryItem("babiri", CobblemonBlocks.BABIRI_BERRY)
@JvmField val CHILAN_BERRY = berryItem("chilan", CobblemonBlocks.CHILAN_BERRY)
@JvmField val ROSELI_BERRY = berryItem("roseli", CobblemonBlocks.ROSELI_BERRY)
@JvmField val LEPPA_BERRY = berryItem("leppa", PPRestoringBerryItem(CobblemonBlocks.LEPPA_BERRY) { CobblemonMechanics.berries.ppRestoreAmount })
@JvmField val LUM_BERRY = berryItem("lum", StatusCuringBerryItem(CobblemonBlocks.LUM_BERRY))
@JvmField val FIGY_BERRY = berryItem("figy", PortionHealingBerryItem(CobblemonBlocks.FIGY_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
@JvmField val WIKI_BERRY = berryItem("wiki", PortionHealingBerryItem(CobblemonBlocks.WIKI_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
@JvmField val MAGO_BERRY = berryItem("mago", PortionHealingBerryItem(CobblemonBlocks.MAGO_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
@JvmField val AGUAV_BERRY = berryItem("aguav", PortionHealingBerryItem(CobblemonBlocks.AGUAV_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
@JvmField val IAPAPA_BERRY = berryItem("iapapa", PortionHealingBerryItem(CobblemonBlocks.IAPAPA_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
@JvmField val SITRUS_BERRY = berryItem("sitrus", HealingBerryItem(CobblemonBlocks.SITRUS_BERRY) { CobblemonMechanics.berries.sitrusHealAmount })
@JvmField val TOUGA_BERRY = berryItem("touga", CobblemonBlocks.TOUGA_BERRY)
@JvmField val CORNN_BERRY = berryItem("cornn", CobblemonBlocks.CORNN_BERRY)
@JvmField val MAGOST_BERRY = berryItem("magost", CobblemonBlocks.MAGOST_BERRY)
@JvmField val RABUTA_BERRY = berryItem("rabuta", CobblemonBlocks.RABUTA_BERRY)
@JvmField val NOMEL_BERRY = berryItem("nomel", CobblemonBlocks.NOMEL_BERRY)
@JvmField val ENIGMA_BERRY = berryItem("enigma", CobblemonBlocks.ENIGMA_BERRY)
@JvmField val POMEG_BERRY = berryItem("pomeg", FriendshipRaisingBerryItem(CobblemonBlocks.POMEG_BERRY, Stats.HP))
@JvmField val KELPSY_BERRY = berryItem("kelpsy", FriendshipRaisingBerryItem(CobblemonBlocks.KELPSY_BERRY, Stats.ATTACK))
@JvmField val QUALOT_BERRY = berryItem("qualot", FriendshipRaisingBerryItem(CobblemonBlocks.QUALOT_BERRY, Stats.DEFENCE))
@JvmField val HONDEW_BERRY = berryItem("hondew", FriendshipRaisingBerryItem(CobblemonBlocks.HONDEW_BERRY, Stats.SPECIAL_ATTACK))
@JvmField val GREPA_BERRY = berryItem("grepa", FriendshipRaisingBerryItem(CobblemonBlocks.GREPA_BERRY, Stats.SPECIAL_DEFENCE))
@JvmField val TAMATO_BERRY = berryItem("tamato", FriendshipRaisingBerryItem(CobblemonBlocks.TAMATO_BERRY, Stats.SPEED))
@JvmField val SPELON_BERRY = berryItem("spelon", CobblemonBlocks.SPELON_BERRY)
@JvmField val PAMTRE_BERRY = berryItem("pamtre", CobblemonBlocks.PAMTRE_BERRY)
@JvmField val WATMEL_BERRY = berryItem("watmel", CobblemonBlocks.WATMEL_BERRY)
@JvmField val DURIN_BERRY = berryItem("durin", CobblemonBlocks.DURIN_BERRY)
@JvmField val BELUE_BERRY = berryItem("belue", CobblemonBlocks.BELUE_BERRY)
@JvmField val KEE_BERRY = berryItem("kee", CobblemonBlocks.KEE_BERRY)
@JvmField val MARANGA_BERRY = berryItem("maranga", CobblemonBlocks.MARANGA_BERRY)
@JvmField val HOPO_BERRY = berryItem("hopo", PPRestoringBerryItem(CobblemonBlocks.HOPO_BERRY) { CobblemonMechanics.berries.ppRestoreAmount })
@JvmField val LIECHI_BERRY = berryItem("liechi", CobblemonBlocks.LIECHI_BERRY)
@JvmField val GANLON_BERRY = berryItem("ganlon", CobblemonBlocks.GANLON_BERRY)
@JvmField val SALAC_BERRY = berryItem("salac", CobblemonBlocks.SALAC_BERRY)
@JvmField val PETAYA_BERRY = berryItem("petaya", CobblemonBlocks.PETAYA_BERRY)
@JvmField val APICOT_BERRY = berryItem("apicot", CobblemonBlocks.APICOT_BERRY)
@JvmField val LANSAT_BERRY = berryItem("lansat", CobblemonBlocks.LANSAT_BERRY)
@JvmField val STARF_BERRY = berryItem("starf", CobblemonBlocks.STARF_BERRY)
@JvmField val MICLE_BERRY = berryItem("micle", CobblemonBlocks.MICLE_BERRY)
@JvmField val CUSTAP_BERRY = berryItem("custap", CobblemonBlocks.CUSTAP_BERRY)
@JvmField val JABOCA_BERRY = berryItem("jaboca", CobblemonBlocks.JABOCA_BERRY)
@JvmField val ROWAP_BERRY = berryItem("rowap", CobblemonBlocks.ROWAP_BERRY)
@JvmField val BERRY_JUICE = this.create("berry_juice", BerryJuiceItem())
// ToDo enable me when malicious armor goes in the game
//@JvmField val AUSPICIOUS_ARMOR = heldItem("auspicious_armor")
// Medicine
@JvmField
@@ -340,18 +255,14 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val BRAISED_VIVICHOKE = create("braised_vivichoke", Item(Item.Settings().maxCount(1).food(FoodComponent.Builder().hunger(6).saturationModifier(0.6f).build())))
@JvmField
val VIVICHOKE_DIP = create("vivichoke_dip", object : StewItem(Settings().maxCount(1)
val VIVICHOKE_DIP = create("vivichoke_dip", StewItem(Item.Settings().maxCount(1)
.food(FoodComponent.Builder()
.hunger(10)
.saturationModifier(1.2F)
.statusEffect(StatusEffectInstance(StatusEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsing(stack: ItemStack?, world: World?, user: LivingEntity?): ItemStack {
user?.clearStatusEffects()
return super.finishUsing(stack, world, user)
}
})
.statusEffect(StatusEffectInstance(StatusEffects.ABSORPTION, 600, 0), 1F)
.build()
)
))
@JvmField
val ENERGY_ROOT = create("energy_root", EnergyRootItem(CobblemonBlocks.ENERGY_ROOT, Item.Settings().food(FoodComponent.Builder().hunger(1).snack().saturationModifier(0.2f).build())))
@JvmField
@@ -633,18 +544,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val DESTINY_KNOT = heldItem("destiny_knot")
@JvmField
val EVERSTONE = heldItem("everstone")
@JvmField
val POWER_ANKLET = heldItem("power_anklet")
@JvmField
val POWER_BAND = heldItem("power_band")
@JvmField
val POWER_BELT = heldItem("power_belt")
@JvmField
val POWER_BRACER = heldItem("power_bracer")
@JvmField
val POWER_LENS = heldItem("power_lens")
@JvmField
val POWER_WEIGHT = heldItem("power_weight")
// Mulch
@JvmField
@@ -674,8 +573,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
private fun noSettingsItem(name: String): CobblemonItem = this.create(name, CobblemonItem(Item.Settings()))
fun berries() = this.berries.toMap()
private fun mulchItem(name: String, mulchVariant: MulchVariant): MulchItem = this.create(name, MulchItem(mulchVariant))
private fun pokeBallItem(pokeBall: PokeBall): PokeBallItem {
@@ -696,17 +593,4 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
}
)
private fun berryItem(name: String, berryBlock: BerryBlock): BerryItem {
val finalName = "${name}_berry"
val item = this.create(finalName, BerryItem(berryBlock))
this.berries[cobblemonResource(finalName)] = item
return item
}
private fun berryItem(name: String, berryItem: BerryItem): BerryItem {
val finalName = "${name}_berry"
val item = this.create(finalName, berryItem)
this.berries[cobblemonResource(finalName)] = item
return item
}
}
}
@@ -11,8 +11,6 @@ package com.cobblemon.mod.common
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.mechanics.BerriesMechanic
import com.cobblemon.mod.common.mechanics.PotionsMechanic
import com.cobblemon.mod.common.mechanics.RemediesMechanic
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.cobblemonResource
@@ -32,19 +30,11 @@ object CobblemonMechanics : DataRegistry {
.create()
var remedies = RemediesMechanic()
var berries = BerriesMechanic()
var potions = PotionsMechanic()
override fun sync(player: ServerPlayerEntity) {}
override fun reload(manager: ResourceManager) {
remedies = loadMechanic(manager, "remedies", RemediesMechanic::class.java)
berries = loadMechanic(manager, "berries", BerriesMechanic::class.java)
potions = loadMechanic(manager, "potions", PotionsMechanic::class.java)
}
private fun <T> loadMechanic(manager: ResourceManager, name: String, clazz: Class<T>): T {
manager.getResourceOrThrow(cobblemonResource("mechanics/$name.json")).inputStream.use {
return gson.fromJson(it.reader(), clazz)
manager.getResourceOrThrow(cobblemonResource("mechanics/remedies.json")).inputStream.use {
remedies = gson.fromJson(it.reader(), RemediesMechanic::class.java)
}
}
}
@@ -13,14 +13,14 @@ import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
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
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
import com.cobblemon.mod.common.client.net.callback.move.OpenMoveCallbackHandler
import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandler
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
import com.cobblemon.mod.common.client.net.pasture.ClosePastureHandler
import com.cobblemon.mod.common.client.net.pasture.OpenPastureHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
@@ -56,7 +56,6 @@ import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPac
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.PropertiesCompletionRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
@@ -274,7 +273,6 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler)
this.createClientBound(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
// Effects
this.createClientBound(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler)
@@ -78,19 +78,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
@JvmField
val MEDICINE_SPRAY_USE = this.create("medicine_spray.use")
@JvmField
val BERRY_HARVEST = this.create("berry.harvest")
@JvmField
val BERRY_EAT = this.create("berry.eat")
@JvmField
val MULCH_PLACE = this.create("mulch.place")
@JvmField
val MULCH_REMOVE = this.create("mulch.remove")
@JvmField
val MUSIC_DEEP_DARK = this.create("music.deep_dark")
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.of(cobblemonResource(name)))
}
@@ -47,13 +47,13 @@ object CobblemonCriteria {
val EVOLVE_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("pokemon_evolved"),
EvolvePokemonCriterionCondition::class.java
SimpleCountableCriterionCondition::class.java
)
)
val WIN_BATTLE = this.create(
BattleCountableCriterionTrigger(
SimpleCriterionTrigger(
cobblemonResource("battles_won"),
BattleCountableCriterionCondition::class.java
SimpleCountableCriterionCondition::class.java
)
)
@@ -71,41 +71,6 @@ object CobblemonCriteria {
)
)
val POKEMON_INTERACT = this.create(
SimpleCriterionTrigger(
cobblemonResource("pokemon_interact"),
PokemonInteractCriterion::class.java
)
)
val PARTY_CHECK = this.create(
SimpleCriterionTrigger(
cobblemonResource("party"),
PartyCheckCriterion::class.java
)
)
val LEVEL_UP = this.create(
SimpleCriterionTrigger(
cobblemonResource("level_up"),
LevelUpCriterionCondition::class.java
)
)
val PASTURE_USE = this.create(
SimpleCriterionTrigger(
cobblemonResource("pasture_use"),
PickStarterCriterionCondition::class.java
)
)
val TRADE_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("trade_pokemon"),
TradePokemonCriterionCondition::class.java
)
)
private fun <T : Criterion<*>> create(criteria: T): T = Cobblemon.implementation.registerCriteria(criteria)
}
@@ -1,67 +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.advancement.criterion
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.util.toJsonArray
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
open class BattleCountableCriterionTrigger(identifier: Identifier, criterionClass: Class<BattleCountableCriterionCondition>) : SimpleCriterionTrigger<BattleCountableContext, BattleCountableCriterionCondition>(identifier, criterionClass) {
}
class BattleCountableCriterionCondition(id: Identifier, predicate: LootContextPredicate) : CountableCriterionCondition<BattleCountableContext>(id, predicate) {
private var battleTypes = mutableListOf("any")
override fun fromJson(json: JsonObject) {
super.fromJson(json)
if(!json.get("battle_types").isJsonNull) {
battleTypes.clear()
json.get("battle_types").asJsonArray.asList().forEach() {
battleTypes.add(it.asString)
}
}
}
override fun toJson(json: JsonObject) {
super.toJson(json)
json.add("battle_types", battleTypes.toJsonArray())
}
override fun matches(player: ServerPlayerEntity, context: BattleCountableContext): Boolean {
var typeCheck = false
val advancementData = Cobblemon.playerData.get(player).advancementData
if (battleTypes.isEmpty() || battleTypes.contains("any")) {
typeCheck = true
}
if (battleTypes.contains("pvp")) {
typeCheck = context.battle.isPvP
context.times = advancementData.totalPvPBattleVictoryCount
}
if (battleTypes.contains("pvw")) {
typeCheck = context.battle.isPvW
context.times = advancementData.totalPvWBattleVictoryCount
}
if (battleTypes.contains("pvn")) {
typeCheck = context.battle.isPvN
context.times = advancementData.totalPvWBattleVictoryCount
}
if (battleTypes.size > 1) {
context.times = advancementData.totalBattleVictoryCount
}
return context.times >= count && typeCheck
}
}
open class BattleCountableContext(times : Int, var battle : PokemonBattle) : CountableContext(times)
@@ -34,6 +34,6 @@ class CaughtPokemonCriterionCondition(id: Identifier, predicate: LootContextPred
}
override fun matches(player: ServerPlayerEntity, context: CountablePokemonTypeContext): Boolean {
return super.matches(player, context) && (context.type == type || type == "any")
return super.matches(player, context) && context.type == type
}
}
@@ -1,38 +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.advancement.criterion
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
class EvolvePokemonCriterionCondition(id: Identifier, entity: LootContextPredicate) : CountableCriterionCondition<EvolvePokemonContext>(id, entity) {
var species = "any"
var evolution = "any"
override fun toJson(json: JsonObject) {
super.toJson(json)
json.addProperty("species", species)
json.addProperty("evolution", evolution)
}
override fun fromJson(json: JsonObject) {
super.fromJson(json)
species = json.get("species")?.asString ?: "any"
evolution = json.get("evolution")?.asString ?: "any"
}
override fun matches(player: ServerPlayerEntity, context: EvolvePokemonContext): Boolean {
return context.times >= count && (context.species == species.asIdentifierDefaultingNamespace() || species == "any") &&
(context.evolution == evolution.asIdentifierDefaultingNamespace() || evolution == "any")
}
}
open class EvolvePokemonContext(val species : Identifier, val evolution : Identifier, times: Int) : CountableContext(times)
@@ -1,38 +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.advancement.criterion
import com.cobblemon.mod.common.pokemon.Pokemon
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
class LevelUpCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<LevelUpContext>(id, entity) {
var level = 0
var evolved = true
override fun toJson(json: JsonObject) {
json.addProperty("level", level)
json.addProperty("has_evolved", evolved)
}
override fun fromJson(json: JsonObject) {
level = json.get("level")?.asInt ?: 0
evolved = json.get("has_evolved")?.asBoolean ?: true
}
override fun matches(player: ServerPlayerEntity, context: LevelUpContext): Boolean {
val preEvo = context.pokemon.preEvolution == null
val hasEvolution = !context.pokemon.evolutions.none()
val evolutionCheck = !(preEvo == hasEvolution)
return level == context.level && evolutionCheck == evolved
}
}
open class LevelUpContext(var level : Int, var pokemon : Pokemon)
@@ -1,54 +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.advancement.criterion
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.party
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
class PartyCheckCriterion(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<PartyCheckContext>(id, entity){
val party = mutableListOf<Identifier>()
override fun toJson(json: JsonObject) {
json.add("party", JsonArray(party.size).also {
party.forEach { pokemon -> it.add(pokemon.toString()) }
})
}
override fun fromJson(json: JsonObject) {
party.clear()
json.getAsJsonArray("party").forEach { element ->
party.add(element.asString.asIdentifierDefaultingNamespace())
}
}
override fun matches(player: ServerPlayerEntity, context: PartyCheckContext): Boolean {
val playerParty = player.party()
val matches = mutableListOf<Identifier>()
party.forEach {
if (it == "any".asIdentifierDefaultingNamespace()) {
matches.add(it)
}
}
val partyCount = playerParty.count()
if (matches.containsAll(party) && party.size == partyCount && matches.size == partyCount) return true
playerParty.iterator().forEach {
if (party.contains(it.species.resourceIdentifier)) {
matches.add(it.species.resourceIdentifier)
}
}
return matches.containsAll(party) && matches.size == partyCount
}
}
open class PartyCheckContext(val party : PlayerPartyStore)
@@ -1,34 +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.advancement.criterion
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
open class PokemonInteractContext(var type: Identifier, var item : Identifier)
class PokemonInteractCriterion(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<PokemonInteractContext>(id, entity) {
var type = "any"
var item = "any"
override fun toJson(json: JsonObject) {
json.addProperty("type", type)
json.addProperty("item", item)
}
override fun fromJson(json: JsonObject) {
type = json.get("type")?.asString ?: "any"
item = json.get("item")?.asString ?: "any"
}
override fun matches(player: ServerPlayerEntity, context: PokemonInteractContext): Boolean {
return (context.type == type.asIdentifierDefaultingNamespace() || type == "any") && (context.item == item.asIdentifierDefaultingNamespace() || item == "any")
}
}
@@ -1,47 +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.advancement.criterion
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
class TradePokemonCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<TradePokemonContext>(id, entity) {
var traded = "any"
var received = "any"
var tradedHeldItem = "any"
var receivedHeldItem = "any"
override fun toJson(json: JsonObject) {
json.addProperty("traded", traded)
json.addProperty("received", received)
json.addProperty("traded_held_item", tradedHeldItem)
json.addProperty("received_held_item", receivedHeldItem)
}
override fun fromJson(json: JsonObject) {
traded = json.get("traded")?.asString ?: "any"
received = json.get("received")?.asString ?: "any"
tradedHeldItem = json.get("traded_held_item")?.asString ?: "minecraft:air"
receivedHeldItem = json.get("received_held_item")?.asString ?: "minecraft:air"
}
override fun matches(player: ServerPlayerEntity, context: TradePokemonContext): Boolean {
val heldItem1 = context.traded.heldItem().item.registryEntry.registryKey().value
val heldItem2 = context.received.heldItem().item.registryEntry.registryKey().value
return (context.traded.species.resourceIdentifier == traded.asIdentifierDefaultingNamespace() || traded == "any") &&
(context.received.species.resourceIdentifier == received.asIdentifierDefaultingNamespace() || received == "any") &&
(heldItem1 == tradedHeldItem.asIdentifierDefaultingNamespace() || heldItem1 == "minecraft:air".asIdentifierDefaultingNamespace()) &&
(heldItem2 == receivedHeldItem.asIdentifierDefaultingNamespace() || heldItem2 == "minecraft:air".asIdentifierDefaultingNamespace())
}
}
open class TradePokemonContext(val traded: Pokemon, val received: Pokemon)
@@ -34,20 +34,22 @@ import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
import com.cobblemon.mod.common.battles.interpreter.ContextManager
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.net.messages.client.battle.BattleEndPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleMusicPacket
import com.cobblemon.mod.common.pokemon.evolution.progress.DefeatEvolutionProgress
import com.cobblemon.mod.common.pokemon.evolution.requirements.DefeatRequirement
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.getPlayer
import java.io.File
import net.minecraft.entity.Entity
import java.util.UUID
import java.util.concurrent.ConcurrentLinkedQueue
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import java.io.File
import kotlin.io.path.Path
import kotlin.io.path.writeLines
import net.minecraft.server.network.ServerPlayerEntity
/**
* Individual battle instance
@@ -60,7 +62,7 @@ open class PokemonBattle(
val side1: BattleSide,
val side2: BattleSide
) {
/** Whether logging will be silenced for this battle. */
/** Whether or not logging will be silenced for this battle. */
var mute = true
init {
@@ -209,16 +211,11 @@ open class PokemonBattle(
faintedPokemons.forEach { faintedPokemon ->
for (opponentPokemon in opponentNonFaintedPokemons) {
val facedFainted = opponentPokemon.facedOpponents.contains(faintedPokemon)
val defeatProgress = DefeatEvolutionProgress()
val pokemon = opponentPokemon.effectedPokemon
if (facedFainted) {
pokemon.evolutions.forEach { evolution ->
evolution.requirements.filterIsInstance<DefeatRequirement>().forEach { defeatRequirement ->
if (defeatRequirement.target.matches(faintedPokemon.effectedPokemon)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DefeatEvolutionProgress && it.currentProgress().target == defeatRequirement.target }) { DefeatEvolutionProgress() }
progress.updateProgress(DefeatEvolutionProgress.Progress(defeatRequirement.target, progress.currentProgress().amount + 1))
}
}
}
if (facedFainted && defeatProgress.shouldKeep(pokemon)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DefeatEvolutionProgress && it.currentProgress().target.matches(faintedPokemon.effectedPokemon) }) { defeatProgress }
progress.updateProgress(DefeatEvolutionProgress.Progress(progress.currentProgress().target, progress.currentProgress().amount + 1))
}
val multiplier = when {
// ToDo when Exp. All is implement if enabled && !facedFainted return 2.0, probably should be a configurable value too, this will have priority over the Exp. Share
@@ -1,109 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.berry.spawncondition.BerrySpawnCondition
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.status.Status
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.adapters.CobblemonStatTypeAdapter
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import java.awt.Color
import net.minecraft.predicate.NumberRange
import net.minecraft.registry.RegistryKeys
import net.minecraft.registry.tag.TagKey
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.math.Box
import net.minecraft.util.math.Vec3d
import net.minecraft.world.biome.Biome
/**
* The data registry for [Berry].
*
* @author Licious
* @since November 28th, 2022
*/
object Berries : JsonDataRegistry<Berry> {
override val id: Identifier = cobblemonResource("berries")
override val type: ResourceType = ResourceType.SERVER_DATA
override val observable = SimpleObservable<Berries>()
override val gson = GsonBuilder()
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(MulchVariant::class.java, MulchVariantAdapter)
.registerTypeAdapter(NumberRange.FloatRange::class.java, FloatNumberRangeAdapter)
.registerTypeAdapter(Status::class.java, StatusAdapter)
.registerTypeAdapter(TypeToken.getParameterized(Collection::class.java, Box::class.java).type, BoxCollectionAdapter)
.registerTypeAdapter(Box::class.java, BoxAdapter)
.registerTypeAdapter(Vec3d::class.java, VerboseVec3dAdapter)
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.registerTypeAdapter(GrowthFactor::class.java, CobblemonGrowthFactorAdapter)
.registerTypeAdapter(IntRange::class.java, VerboseIntRangeAdapter)
.registerTypeAdapter(Color::class.java, LiteralHexColorAdapter)
.registerTypeAdapter(Stat::class.java, CobblemonStatTypeAdapter)
.registerTypeAdapter(TypeToken.getParameterized(TagKey::class.java, Biome::class.java).type, TagKeyAdapter(RegistryKeys.BIOME))
.registerTypeAdapter(BerrySpawnCondition::class.java, CobblemonBerrySpawnConditionAdapter)
.create()
override val typeToken: TypeToken<Berry> = TypeToken.get(Berry::class.java)
override val resourcePath = "berries"
private val berries = hashMapOf<Identifier, Berry>()
val PECHA
get() = this.getByName("")
override fun reload(data: Map<Identifier, Berry>) {
this.berries.clear()
data.forEach { (identifier, berry) ->
try {
berry.identifier = identifier
berry.validate()
this.berries[identifier] = berry
} catch (e: Exception) {
Cobblemon.LOGGER.error("Skipped loading the {} berry", identifier, e)
}
}
Cobblemon.LOGGER.info("Loaded {} berries", this.berries.size)
this.observable.emit(this)
}
override fun sync(player: ServerPlayerEntity) {
BerryRegistrySyncPacket(this.all()).sendToPlayer(player)
}
fun all() = this.berries.values.toList()
/**
* Gets a berry if loaded.
*
* @param identifier The identifier of the berry.
* @return The [Berry] if loaded otherwise null.
*/
fun getByIdentifier(identifier: Identifier): Berry? = this.berries[identifier]
/**
* Gets a berry if loaded.
*
* @param name The path of the identifier of the berry under the [Cobblemon.MODID] namespace.
* @return The [Berry] if loaded otherwise null.
*/
fun getByName(name: String): Berry? = this.getByIdentifier(cobblemonResource(name))
}
@@ -1,349 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.berry.spawncondition.BerrySpawnCondition
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.pokemon.Nature
import com.cobblemon.mod.common.util.readBox
import com.cobblemon.mod.common.util.writeBox
import com.google.gson.annotations.SerializedName
import java.awt.Color
import java.util.EnumSet
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.client.model.ModelPart
import net.minecraft.entity.LivingEntity
import net.minecraft.network.PacketByteBuf
import net.minecraft.registry.tag.TagKey
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
import net.minecraft.util.math.Vec3d
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.World
import net.minecraft.world.biome.Biome
/**
* Represents the data behind a berry.
*
* @property identifier The [Identifier] of this berry.
* @property baseYield The [IntRange] possible for the berry tree before [bonusYield] is calculated.
* @property preferredBiomeTags The [TagKey]s of the berries preffered biomes. Determines spawning and yield
* @property growthTime The [IntRange] possible in minutes for how long the berry tree takes to grow.
* @property refreshRate The [IntRange] possible in minutes for how long the berry tree takes to regrow its berries once it has been harvested.
* @depreciated @property lifeCycles The [IntRange] possible for the berry to live for between harvests.
* @property favoriteMulches The types of mulches that benefit this berries yield
* @property growthFactors An array of [GrowthFactor]s that will affect this berry. The client is not aware of these.
* @property mutations A map of the partner berry as the key and the value as the resulting mutation with this berry.
* @property growthPoints A collection of [GrowthPoint]s for the berry flowers and fruit.
* @property sproutShapeBoxes A collection of [Box]es that make up the tree [VoxelShape] during the sprouting stages.
* @property matureShapeBoxes A collection of [Box]es that make up the tree [VoxelShape] during the mature stages.
* @property flavors The [Flavor] values.
* @property tintIndexes Determines tints at specific indexes if any.
* @property flowerModelIdentifier The [Identifier] for the model of the berry in flower form.
* @property flowerTexture The [Identifier] for the texture of the berry in flower form. This is resolved into a [ModelPart] on the client.
* @property fruitModelIdentifier The [Identifier] for the model of the berry in flower form. This is resolved into a [ModelPart] on the client.
* @property fruitTexture The [Identifier] for the texture of the berry in flower form.
*
* @throws IllegalArgumentException if the any yield range argument is not a positive range.
*/
class Berry(
identifier: Identifier,
val baseYield: IntRange,
val preferredBiomeTags: List<TagKey<Biome>>,
val growthTime: IntRange,
val refreshRate: IntRange,
//val lifeCycles: IntRange,
val favoriteMulches: EnumSet<MulchVariant>,
val growthFactors: Collection<GrowthFactor>,
val spawnConditions: List<BerrySpawnCondition>,
var growthPoints: Array<GrowthPoint>,
val randomizedGrowthPoints: Boolean = true,
val mutations: Map<Identifier, Identifier>,
@SerializedName("sproutShape")
private val sproutShapeBoxes: Collection<Box>,
@SerializedName("matureShape")
private val matureShapeBoxes: Collection<Box>,
private val flavors: Map<Flavor, Int>,
val tintIndexes: Map<Int, Color>,
@SerializedName("flowerModel")
val flowerModelIdentifier: Identifier,
val flowerTexture: Identifier,
@SerializedName("fruitModel")
val fruitModelIdentifier: Identifier,
val fruitTexture: Identifier,
val weight: Float
) {
@Transient
var identifier: Identifier = identifier
internal set
@Transient
private lateinit var shapedFlower: HashMap<Int, VoxelShape>
@Transient
private lateinit var shapedFruit: HashMap<Int, VoxelShape>
/**
* The [VoxelShape] of the tree during the sprouting stages.
*/
@Transient
lateinit var sproutShape: VoxelShape
private set
/**
* The [VoxelShape] of the tree during the mature stages.
*/
@Transient
lateinit var matureShape: VoxelShape
private set
init {
this.validate()
}
/**
* Finds the [BerryItem] that uses this berry for data.
*
* @return The [BerryItem] if existing.
*/
fun item(): BerryItem? = CobblemonItems.berries()[this.identifier]
/**
* Finds the [BerryBlock] that uses this berry for data.
*
* @return The [BerryBlock] if existing.
*/
fun block(): BerryBlock? = CobblemonBlocks.berries()[this.identifier]
/**
* Query the value of a certain flavor.
*
* @param flavor The [Flavor] being queried.
* @return The value if any or 0.
*/
fun flavor(flavor: Flavor): Int = this.flavors[flavor] ?: 0
fun dislikedBy(nature: Nature): Boolean {
val dislikedFlavor = nature.dislikedFlavor ?: return false
return flavor(dislikedFlavor) > 0
}
/**
* Calculates the yield for a berry tree being planted or replanted after a life cycle.
* Triggers [BerryYieldCalculationEvent].
*
* @param world The [World] the tree is present in.
* @param state The [BlockState] of the tree.
* @param pos The [BlockPos] of the tree.
* @param placer The [LivingEntity] tending to the tree, if any.
* @return The total berry stack count.
*/
fun calculateYield(world: World, state: BlockState, pos: BlockPos, placer: LivingEntity? = null): Int {
val base = this.baseYield.random()
val bonus = this.bonusYield(world, state, pos)
var yield = base + bonus.first
val event = BerryYieldCalculationEvent(this, world, state, pos, placer, yield, bonus.second)
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
if (BerryBlock.getMulch(state) == MulchVariant.RICH) {
yield = yield.times(1.2).coerceAtMost(maxYield().toDouble()).toInt()
treeEntity.decrementMulchDuration(world, pos, state)
}
CobblemonEvents.BERRY_YIELD.post(event) { yield = it.yield }
return yield
//Just for testing the renderer
//return growthPoints.size
}
/**
* Calculates and returns the minimum possible yield by summing [baseYield] and [growthFactors] minimum values.
*
* @return The minimum possible yield.
*/
fun minYield() = this.baseYield.first + this.growthFactors.sumOf { it.minYield() }
/**
* Calculates and returns the maximum possible yield by summing [baseYield] and [growthFactors] max values.
*
* @return The maximum possible yield.
*/
fun maxYield() = this.baseYield.last + this.growthFactors.sumOf { it.maxYield() }
/**
* Checks if this berry can mutate with the given [partner].
* For the berry the mutations may produce use [mutationWith].
*
* @param partner The [Berry] being checked as a possible mutation partner.
* @return If a mutation is possible.
*/
fun canMutateWith(partner: Berry) = this.mutationWith(partner) != null
/**
* Query for the possible mutation with the given [partner].
*
* @param partner The [Berry] being mutated with.
* @return The resulting [Berry] if the mutation is possible.
*/
fun mutationWith(partner: Berry): Berry? {
val berryIdentifier = this.mutations[partner.identifier] ?: return null
return Berries.getByIdentifier(berryIdentifier)
}
/**
* Query for a possible partner to mutate into the given [resulting].
*
* @param resulting The [Berry] the mutation should result in.
* @return The [Berry] that mutates with this berry into the given [resulting] if possible.
*/
fun partnerForMutation(resulting: Berry): Berry? {
return this.mutations.firstNotNullOfOrNull { (partner, result) ->
if (result == resulting.identifier)
Berries.getByIdentifier(partner)
else
null
}
}
// A cheat since gson doesn't invoke init block
internal fun validate() {
if (this.baseYield.first < 0 || this.baseYield.last < 0) {
throw IllegalArgumentException("A berry base yield must be a positive range")
}
if (this.growthTime.first < 0 || this.growthTime.last < 0) {
throw IllegalArgumentException("The growth time must be a positive range")
}
if (this.refreshRate.first < 0 || this.refreshRate.last < 0) {
throw IllegalArgumentException("The refresh rate must be a positive range")
}
/*
if (this.lifeCycles.first < 0 || this.lifeCycles.last < 0) {
throw IllegalArgumentException("A berry life cycle must be a positive range")
}
*/
this.growthFactors.forEach { it.validateArguments() }
val maxYield = this.maxYield()
if (this.growthPoints.size < maxYield) {
throw IllegalArgumentException("Anchor points must have enough elements for the max possible yield of $maxYield you've provided ${this.growthPoints.size} points")
}
this.shapedFlower = hashMapOf()
this.shapedFruit = hashMapOf()
this.sproutShape = this.createAndUniteShapes(this.sproutShapeBoxes)
this.matureShape = this.createAndUniteShapes(this.matureShapeBoxes)
}
internal fun encode(buffer: PacketByteBuf) {
buffer.writeIdentifier(this.identifier)
buffer.writeInt(this.baseYield.first)
buffer.writeInt(this.baseYield.last)
buffer.writeEnumSet(favoriteMulches, MulchVariant::class.java)
//buffer.writeInt(this.lifeCycles.first)
//buffer.writeInt(this.lifeCycles.last)
buffer.writeInt(this.growthTime.first)
buffer.writeInt(this.growthTime.last)
buffer.writeInt(this.refreshRate.first)
buffer.writeInt(this.refreshRate.last)
buffer.writeCollection(this.growthPoints.toList()) { writer, value ->
writer.writeDouble(value.position.x)
writer.writeDouble(value.position.y)
writer.writeDouble(value.position.z)
writer.writeDouble(value.rotation.x)
writer.writeDouble(value.rotation.y)
writer.writeDouble(value.rotation.z)
}
buffer.writeBoolean(this.randomizedGrowthPoints)
buffer.writeMap(this.mutations, { writer, key -> writer.writeIdentifier(key) }, { writer, value -> writer.writeIdentifier(value) })
buffer.writeCollection(this.sproutShapeBoxes) { writer, value ->
writer.writeBox(value)
}
buffer.writeCollection(this.matureShapeBoxes) { writer, value ->
writer.writeBox(value)
}
buffer.writeMap(this.flavors, { writer, key -> writer.writeEnumConstant(key) }, { writer, value -> writer.writeInt(value) })
buffer.writeMap(this.tintIndexes, { writer, key -> writer.writeInt(key) }, { writer, value -> writer.writeInt(value.rgb) })
buffer.writeIdentifier(this.flowerModelIdentifier)
buffer.writeIdentifier(this.flowerTexture)
buffer.writeIdentifier(this.fruitModelIdentifier)
buffer.writeIdentifier(this.fruitTexture)
}
/**
* Calculates the bonus yield for the berry tree.
*
* @param world The [World] the tree is present in.
* @param state The [BlockState] of the tree.
* @param pos The [BlockPos] of the tree.
* @return The bonus yield, the growth factors that passed.
*/
private fun bonusYield(world: World, state: BlockState, pos: BlockPos): Pair<Int, Collection<GrowthFactor>> {
var bonus = 0
val passed = arrayListOf<GrowthFactor>()
val mulchVariant = BerryBlock.getMulch(state)
val hasBiomeMulch = mulchVariant in favoriteMulches
this.growthFactors.forEach { factor ->
if (factor.isValid(world, state, pos)) {
bonus += factor.yield()
passed += factor
}
else if (hasBiomeMulch) {
bonus += factor.yield()
}
}
return bonus to passed
}
private fun createAndUniteShapes(boxes: Collection<Box>): VoxelShape {
var shape: VoxelShape? = null
boxes.forEach { box ->
shape = if (shape == null) {
Block.createCuboidShape(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ)
} else {
VoxelShapes.union(shape, Block.createCuboidShape(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ))
}
}
return shape ?: VoxelShapes.fullCube()
}
companion object {
internal fun decode(buffer: PacketByteBuf): Berry {
val identifier = buffer.readIdentifier()
val baseYield = IntRange(buffer.readInt(), buffer.readInt())
val favMulchs = buffer.readEnumSet(MulchVariant::class.java)
//val lifeCycles = IntRange(buffer.readInt(), buffer.readInt())
val growthTime = IntRange(buffer.readInt(), buffer.readInt())
val refreshRate = IntRange(buffer.readInt(), buffer.readInt())
val growthPoints = buffer.readList { reader ->
GrowthPoint(Vec3d(reader.readDouble(), reader.readDouble(), reader.readDouble()), Vec3d(reader.readDouble(), reader.readDouble(), reader.readDouble()))
}.toTypedArray()
val randomizedGrowthPoints = buffer.readBoolean()
val mutations = buffer.readMap({ reader -> reader.readIdentifier() }, { reader -> reader.readIdentifier() })
val sproutShapeBoxes = buffer.readList { it.readBox() }
val matureShapeBoxes = buffer.readList { it.readBox() }
val flavors = buffer.readMap({ reader -> reader.readEnumConstant(Flavor::class.java) }, { reader -> reader.readInt() })
val tintIndexes = buffer.readMap({ reader -> reader.readInt() }, { reader -> Color(reader.readInt()) })
val flowerModelIdentifier = buffer.readIdentifier()
val flowerTexture = buffer.readIdentifier()
val fruitModelIdentifier = buffer.readIdentifier()
val fruitTexture = buffer.readIdentifier()
return Berry(identifier, baseYield, emptyList(), growthTime, refreshRate, favMulchs, emptySet(), emptyList(), growthPoints, randomizedGrowthPoints, mutations, sproutShapeBoxes, matureShapeBoxes, flavors, tintIndexes, flowerModelIdentifier, flowerTexture, fruitModelIdentifier, fruitTexture, 0F)
}
}
}
@@ -1,47 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.world.feature.BerryGroveFeature
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.world.biome.Biome
object BerryHelper {
private val CACHE_LOADER = object : CacheLoader<RegistryEntry<Biome>, List<BerryBlock>>() {
override fun load(key: RegistryEntry<Biome>): List<BerryBlock> {
return naturalBerries.filter { berryBlock ->
val berry = berryBlock.berry()
berry?.spawnConditions?.any { it.canSpawn(berry, key) } ?: false
}
}
}
private val naturalBerries = CobblemonBlocks.berries().values.filter {
(it.berry()?.spawnConditions?.size ?: 0) > 0
}
private val validBerryCache: LoadingCache<RegistryEntry<Biome>, List<BerryBlock>> = CacheBuilder.newBuilder()
.maximumSize(4)
.build(CACHE_LOADER)
fun getBerriesForBiome(biome: RegistryEntry<Biome>): List<BerryBlock> {
return validBerryCache.get(biome)
}
fun getNaturallyGeneratingBerries(): List<BerryBlock> {
return naturalBerries
}
}
@@ -1,26 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry
/**
* Represents the different flavors associated with berries and other Pokémon consumables.
* See the [Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Flavor) article for more information.
*
* @author Licious
* @since November 28th, 2022
*/
enum class Flavor {
SPICY,
DRY,
SWEET,
BITTER,
SOUR
}
@@ -1,61 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import net.minecraft.world.WorldView
/**
* Represents a condition that impacts extra berry yield.
*
* @author Licious
* @since December 2nd, 2022
*/
interface GrowthFactor {
/**
* Validates the arguments contained in this factor, this is invoked during loading.
*
* @throws IllegalArgumentException If an argument isn't valid.
*/
fun validateArguments()
/**
* Checks if this factor should yield a bonus.
*
* @param world The [WorldView] the berry tree is in.
* @param state The [BlockState] of the berry tree.
* @param pos The [BlockPos] of the berry tree.
* @return If the bonus should activate.
*/
fun isValid(world: WorldView, state: BlockState, pos: BlockPos): Boolean
/**
* Resolves the amount of bonus berries to grow if [isValid] was true.
*
* @return The amount of extra berries to grow.
*/
fun yield(): Int
/**
* Returns the minimum possible yield for this factor.
*
* @return The minimum possible yield for this factor.
*/
fun minYield(): Int
/**
* Returns the maximum possible yield for this factor.
*
* @return The maximum possible yield for this factor.
*/
fun maxYield(): Int
}
@@ -1,25 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry
import net.minecraft.util.math.Vec3d
/**
* Represents a position in a berry tree where a berry will flower then fruit.
*
* @property position The coordinates of the berry in the tree block.
* @property rotation The degree value for the rotation each axis.
*
* @author Licious
* @since December 19th, 2022
*/
data class GrowthPoint(
val position: Vec3d,
val rotation: Vec3d
)
@@ -1,35 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry.adapter
import com.cobblemon.mod.common.api.berry.GrowthFactor
import com.cobblemon.mod.common.util.adapters.CobblemonGrowthFactorAdapter
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import net.minecraft.util.Identifier
import kotlin.reflect.KClass
/**
* A type adapter for [GrowthFactor]s.
* For the default implementation see [CobblemonGrowthFactorAdapter].
*
* @author Licious
* @since December 2nd, 2022
*/
interface GrowthFactorAdapter : JsonDeserializer<GrowthFactor>, JsonSerializer<GrowthFactor> {
/**
* Register a [GrowthFactor] to be used by this adapter.
*
* @param type The [KClass] of the [GrowthFactor].
* @param identifier The expected [Identifier] in the parsed JSON.
*/
fun register(type: KClass<out GrowthFactor>, identifier: Identifier)
}
@@ -1,34 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry.adapter
import com.cobblemon.mod.common.api.berry.GrowthFactor
import com.cobblemon.mod.common.api.berry.spawncondition.BerrySpawnCondition
import com.cobblemon.mod.common.util.adapters.CobblemonBerrySpawnConditionAdapter
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import net.minecraft.util.Identifier
import kotlin.reflect.KClass
/**
* A type adapter for [GrowthFactor]s.
* For the default implementation see [CobblemonBerrySpawnConditionAdapter].
*
* @author Apion
* @since August 14, 2023
*/
interface SpawnConditionAdapter : JsonDeserializer<BerrySpawnCondition>, JsonSerializer<BerrySpawnCondition> {
/**
* Register a [BerrySpawnCondition] to be used by this adapter.
*
* @param type The [KClass] of the [BerrySpawnCondition].
* @param identifier The expected [Identifier] in the parsed JSON.
*/
fun register(type: KClass<out BerrySpawnCondition>, identifier: Identifier)
}
@@ -1,30 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry.spawncondition
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.util.math.random.Random
import net.minecraft.world.biome.Biome
class AllBiomeCondition(val minGroveSize: Int, val maxGroveSize: Int) : BerrySpawnCondition{
override fun canSpawn(berry: Berry, biome: RegistryEntry<Biome>): Boolean {
return true
}
override fun getGroveSize(random: Random): Int {
return random.nextBetween(minGroveSize, maxGroveSize)
}
companion object {
val ID = cobblemonResource("all_biome")
}
}
@@ -1,20 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry.spawncondition
import com.cobblemon.mod.common.api.berry.Berry
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.util.math.random.Random
import net.minecraft.world.biome.Biome
interface BerrySpawnCondition {
fun getGroveSize(random: Random): Int
fun canSpawn(berry: Berry, biome: RegistryEntry<Biome>): Boolean
}
@@ -1,32 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry.spawncondition
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.util.math.random.Random
import net.minecraft.world.biome.Biome
import kotlin.math.min
class PreferredBiomeCondition(val minGroveSize: Int, val maxGroveSize: Int) : BerrySpawnCondition{
override fun getGroveSize(random: Random): Int {
return random.nextBetween(minGroveSize, maxGroveSize)
}
override fun canSpawn(berry: Berry, biome: RegistryEntry<Biome>): Boolean {
val preferredBiomeTags = berry.preferredBiomeTags
return preferredBiomeTags.any {biome.isIn(it)}
}
companion object {
val ID = cobblemonResource("preferred_biome")
}
}
@@ -13,10 +13,8 @@ import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.moves.Move
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.lang
import java.util.UUID
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.MutableText
/**
* Used for opening a party select screen for players which routes to move selection when they choose a Pokémon.
@@ -32,7 +30,6 @@ object PartyMoveSelectCallbacks {
@JvmOverloads
fun create(
player: ServerPlayerEntity,
partyTitle: MutableText = lang("ui.party"),
pokemon: List<Pair<PartySelectPokemonDTO, List<MoveSelectDTO>>>,
cancel: (ServerPlayerEntity) -> Unit = {},
handler: (ServerPlayerEntity, pokemonIndex: Int, PartySelectPokemonDTO, moveIndex: Int, MoveSelectDTO) -> Unit
@@ -43,13 +40,12 @@ object PartyMoveSelectCallbacks {
handler = handler
)
callbacks[player.uuid] = callback
player.sendPacket(OpenPartyMoveCallbackPacket(callback.uuid, partyTitle, callback.pokemon))
player.sendPacket(OpenPartyMoveCallbackPacket(callback.uuid, callback.pokemon))
}
@JvmOverloads
fun createFromPokemon(
player: ServerPlayerEntity,
partyTitle: MutableText = lang("ui.party"),
pokemon: List<Pokemon>,
moves: (Pokemon) -> List<Move> = { it.moveSet.getMoves() },
canSelectPokemon: (Pokemon) -> Boolean = { true },
@@ -66,7 +62,6 @@ object PartyMoveSelectCallbacks {
create(
player = player,
partyTitle = partyTitle,
pokemon = pokemonList,
cancel = cancel,
handler = { _, pkIndex, _, moveIndex, _ ->
@@ -12,10 +12,10 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.PokemonPropertyExtractor
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.lang
import java.util.UUID
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NbtCompound
@@ -36,7 +36,7 @@ object PartySelectCallbacks {
@JvmOverloads
fun create(
player: ServerPlayerEntity,
title: Text = lang("ui.party"),
title: Text = "".text(),
pokemon: List<PartySelectPokemonDTO>,
cancel: (ServerPlayerEntity) -> Unit = {},
handler: (ServerPlayerEntity, index: Int) -> Unit
@@ -61,14 +61,12 @@ object PartySelectCallbacks {
@JvmOverloads
fun createBattleSelect(
player: ServerPlayerEntity,
title: Text = lang("ui.party"),
pokemon: List<BattlePokemon>,
canSelect: (BattlePokemon) -> Boolean,
cancel: (ServerPlayerEntity) -> Unit = {},
handler: (BattlePokemon) -> Unit
) = create(
player = player,
title = title,
cancel = cancel,
pokemon = pokemon.map { pk -> PartySelectPokemonDTO(pk.effectedPokemon).also { it.enabled = canSelect(pk) } }
) { _, index -> handler(pokemon[index]) }
@@ -76,14 +74,12 @@ object PartySelectCallbacks {
@JvmOverloads
fun createFromPokemon(
player: ServerPlayerEntity,
title: Text = lang("ui.party"),
pokemon: List<Pokemon>,
canSelect: (Pokemon) -> Boolean,
cancel: (ServerPlayerEntity) -> Unit = {},
handler: (Pokemon) -> Unit
) = create(
player = player,
title = title,
cancel = cancel,
pokemon = pokemon.map { pk -> PartySelectPokemonDTO(pk).also { it.enabled = canSelect(pk) } }
) { _, index -> handler(pokemon[index]) }
@@ -122,8 +118,7 @@ class PartySelectCallback(
open class PartySelectPokemonDTO(
val pokemonProperties: PokemonProperties,
val heldItem: ItemStack = ItemStack.EMPTY,
var currentHealth: Int,
var maxHealth: Int,
var hpRatio: Float = 1F,
var enabled: Boolean
) {
@JvmOverloads
@@ -137,24 +132,21 @@ open class PartySelectPokemonDTO(
PokemonPropertyExtractor.STATUS
),
heldItem = pokemon.heldItemNoCopy(),
currentHealth = pokemon.currentHealth,
maxHealth = pokemon.hp,
hpRatio = pokemon.currentHealth / pokemon.hp.toFloat(),
enabled = enabled
)
constructor(buffer: PacketByteBuf): this(
pokemonProperties = PokemonProperties().loadFromNBT(buffer.readNbt() as NbtCompound),
heldItem = buffer.readItemStack(),
currentHealth = buffer.readInt(),
maxHealth = buffer.readInt(),
hpRatio = buffer.readFloat(),
enabled = buffer.readBoolean()
)
fun writeToBuffer(buffer: PacketByteBuf) {
buffer.writeNbt(pokemonProperties.saveToNBT())
buffer.writeItemStack(heldItem)
buffer.writeInt(currentHealth)
buffer.writeInt(maxHealth)
buffer.writeFloat(hpRatio)
buffer.writeBoolean(enabled)
}
}
@@ -46,9 +46,6 @@ interface JsonDataRegistry<T> : DataRegistry {
override fun reload(manager: ResourceManager) {
val data = hashMapOf<Identifier, T>()
manager.findResources(this.resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
if (identifier.namespace == "pixelmon") {
return@forEach
}
resource.inputStream.use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = Identifier(identifier.namespace, File(identifier.path).nameWithoutExtension)
@@ -13,10 +13,6 @@ import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
import com.cobblemon.mod.common.api.events.battles.BattleStartedPostEvent
import com.cobblemon.mod.common.api.events.battles.BattleStartedPreEvent
import com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent
import com.cobblemon.mod.common.api.events.berry.BerryHarvestEvent
import com.cobblemon.mod.common.api.events.berry.BerryMutationOfferEvent
import com.cobblemon.mod.common.api.events.berry.BerryMutationResultEvent
import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
import com.cobblemon.mod.common.api.events.entity.PokemonEntityLoadEvent
import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveEvent
@@ -24,8 +20,6 @@ import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveToWorldEvent
import com.cobblemon.mod.common.api.events.entity.SpawnEvent
import com.cobblemon.mod.common.api.events.farming.ApricornHarvestEvent
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent
import com.cobblemon.mod.common.api.events.pokeball.PokeBallCaptureCalculatedEvent
import com.cobblemon.mod.common.api.events.pokeball.PokemonCatchRateEvent
import com.cobblemon.mod.common.api.events.pokeball.ThrownPokeballHitEvent
import com.cobblemon.mod.common.api.events.pokemon.*
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionAcceptedEvent
@@ -65,10 +59,6 @@ object CobblemonEvents {
@JvmField
val THROWN_POKEBALL_HIT = CancelableObservable<ThrownPokeballHitEvent>()
@JvmField
val POKEMON_CATCH_RATE = EventObservable<PokemonCatchRateEvent>()
@JvmField
val POKE_BALL_CAPTURE_CALCULATED = EventObservable<PokeBallCaptureCalculatedEvent>()
@JvmField
val POKEMON_CAPTURED = EventObservable<PokemonCapturedEvent>()
// @JvmField
// val EGG_HATCH = EventObservable<HatchEggEvent>()
@@ -90,9 +80,6 @@ object CobblemonEvents {
@JvmField
val POKEMON_RECALLED = EventObservable<PokemonRecalledEvent>()
@JvmField
val TRADE_COMPLETED = EventObservable<TradeCompletedEvent>()
@JvmField
val LEVEL_UP_EVENT = EventObservable<LevelUpEvent>()
@@ -133,15 +120,6 @@ object CobblemonEvents {
@JvmField
val APRICORN_HARVESTED = EventObservable<ApricornHarvestEvent>()
// Berries
@JvmField
val BERRY_HARVEST = EventObservable<BerryHarvestEvent>()
@JvmField
val BERRY_MUTATION_OFFER = EventObservable<BerryMutationOfferEvent>()
@JvmField
val BERRY_MUTATION_RESULT = EventObservable<BerryMutationResultEvent>()
@JvmField
val BERRY_YIELD = EventObservable<BerryYieldCalculationEvent>()
@JvmField
val LEFTOVERS_CREATED = CancelableObservable<LeftoversCreatedEvent>()
@JvmField
@@ -1,27 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.berry
import com.cobblemon.mod.common.api.berry.Berry
/**
* The base of all Berry related events.
*
* @author Licious
* @since November 28th, 2022
*/
interface BerryEvent {
/**
* The [Berry] related to the event trigger
*/
val berry: Berry
}
@@ -1,41 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.berry
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import net.minecraft.block.BlockState
import net.minecraft.item.ItemStack
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
/**
* An event fired when [BerryBlockEntity.harvest] is invoked.
*
* @property berry The [Berry] the tree is attached to.
* @property player The [ServerPlayerEntity] harvesting the tree.
* @property world The [World] the tree is in.
* @property pos The [BlockPos] of the tree.
* @property state The [BlockState] of the tree.
* @property blockEntity The backing [BerryBlockEntity]-
* @property drops A collection of [ItemStack]s produced by this harvest.
*
* @author Licious
* @since November 28th, 2022
*/
data class BerryHarvestEvent(
override val berry: Berry,
val player: ServerPlayerEntity,
val world: World,
val pos: BlockPos,
val state: BlockState,
val blockEntity: BerryBlockEntity,
val drops: MutableList<ItemStack>
) : BerryEvent
@@ -1,35 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.berry
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.block.BerryBlock
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
/**
* The event fired when [BerryBlock.grow] is invoked and the tree passes to [BerryBlock.MATURE_AGE].
* This allows you to modify the possible mutations for the tree, for the event that decides which of these mutations is used see [BerryMutationResultEvent].
*
* @property world The [World] the berry tree is in.
* @property state The [BlockState] of the berry tree.
* @property pos The [BlockPos] of the berry tree.
* @property mutations The possible [Berry] mutations.
*
* @author Licious
* @since January 19th, 2022
*/
data class BerryMutationOfferEvent(
override val berry: Berry,
val world: World,
val state: BlockState,
val pos: BlockPos,
val mutations: MutableSet<Berry>
) : BerryEvent
@@ -1,37 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.berry
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.block.BerryBlock
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
/**
* The event fired when [BerryBlock.grow] is invoked and the tree passes to [BerryBlock.MATURE_AGE].
* This allows you to modify the possible mutations for the tree, for the event that decides which of these mutations is used see [BerryMutationResultEvent].
*
* @property world The [World] the berry tree is in.
* @property state The [BlockState] of the berry tree.
* @property pos The [BlockPos] of the berry tree.
* @property mutations The possible [Berry] mutations.
* @property pickedMutation The [Berry] chosen to mutate into, if null the mutation will not occur.
*
* @author Licious
* @since January 19th, 2022
*/
data class BerryMutationResultEvent(
override val berry: Berry,
val world: World,
val state: BlockState,
val pos: BlockPos,
val mutations: Set<Berry>,
var pickedMutation: Berry?
) : BerryEvent
@@ -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.api.events.berry
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.api.berry.GrowthFactor
import net.minecraft.block.BlockState
import net.minecraft.entity.LivingEntity
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
/**
* The event fired when [Berry.calculateYield] is invoked.
*
* @property world The [World] the berry tree is in.
* @property state The [BlockState] of the berry tree.
* @property pos The [BlockPos] of the berry tree.
* @property placer The [LivingEntity] triggering the calculation.
* @property yield The current yield of berries.
* @property passedGrowthFactors The [Berry.growthFactors] where [GrowthFactor.isValid] was true.
*
* @author Licious
* @since November 28th, 2022
*/
class BerryYieldCalculationEvent(
override val berry: Berry,
val world: World,
val state: BlockState,
val pos: BlockPos,
val placer: LivingEntity?,
yield: Int,
val passedGrowthFactors: Collection<GrowthFactor>
) : BerryEvent {
/**
* The amount of berries this tree will yield.
*
* @throws IllegalArgumentException If a new value exceeds [Berry.maxYield] or be lesser than 0.
*/
var yield: Int = yield
set(value) {
val max = this.berry.maxYield()
if (value > max) {
throw IllegalArgumentException("Cannot set the berry yield for ${this.berry.identifier} above $max")
}
if (value < 0) {
throw IllegalArgumentException("A berry tree cannot yield a negative amount of berries")
}
field = value
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.events.farming
import com.cobblemon.mod.common.api.apricorn.Apricorn
import net.minecraft.block.BlockState
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
@@ -19,7 +18,6 @@ import net.minecraft.util.math.BlockPos
*/
class ApricornHarvestEvent(
val player: ServerPlayerEntity,
val apricorn: Apricorn,
val world: ServerWorld,
val pos: BlockPos
) {
@@ -1,35 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokeball
import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.entity.LivingEntity
import net.minecraft.server.network.ServerPlayerEntity
/**
* Event fired when a Poké Ball has completed its capture calculation and is about to begin shaking
* or breaking free. The result of the capture can be changed by replacing [captureResult].
*
* @author Hiroku
* @since August 20th, 2023
*/
class PokeBallCaptureCalculatedEvent(
val thrower: LivingEntity,
val pokemonEntity: PokemonEntity,
val pokeBallEntity: EmptyPokeBallEntity,
var captureResult: CaptureContext
) {
fun ifPlayer(action: PokeBallCaptureCalculatedEvent.(player: ServerPlayerEntity) -> Unit) {
if (thrower is ServerPlayerEntity) {
action(this, thrower)
}
}
}
@@ -1,30 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokeball
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.entity.LivingEntity
/**
* Event fired when a capture calculator is accessing a Pokémon's catch rate. The [catchRate] property can
* be changed to adjust the rate.
*
* If you want to prevent a Pokémon from being caught, you should be handling the [PokeBallCaptureCalculatedEvent]
* event instead.
*
* @author Hiroku
* @since August 20th, 2023
*/
class PokemonCatchRateEvent(
val thrower: LivingEntity,
val pokeBallEntity: EmptyPokeBallEntity,
val pokemonEntity: PokemonEntity,
var catchRate: Float
)
@@ -8,12 +8,10 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.network.ServerPlayerEntity
data class PokemonCapturedEvent (
val pokemon: Pokemon,
val player: ServerPlayerEntity,
val pokeBallEntity: EmptyPokeBallEntity
val player: ServerPlayerEntity
)
@@ -1,14 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.trade.TradeParticipant
class TradeCompletedEvent(val tradeParticipant1 : TradeParticipant, val tradeParticipant1Pokemon : Pokemon, val tradeParticipant2 : TradeParticipant, val tradeParticipant2Pokemon : Pokemon)
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.api.text.font
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay.Companion.PORTRAIT_DIAMETER
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Species
@@ -182,10 +181,6 @@ fun drawPortraitPokemon(
val model = PokemonModelRepository.getPoser(species.resourceIdentifier, aspects)
val texture = PokemonModelRepository.getTexture(species.resourceIdentifier, aspects, state)
val context = RenderContext()
PokemonModelRepository.variations[species.resourceIdentifier]?.getTexture(aspects, 0f).let { it -> context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, species.getForm(aspects).baseScale)
val renderType = model.getLayer(texture)
RenderSystem.applyModelViewMatrix()
@@ -220,12 +215,11 @@ fun drawPortraitPokemon(
val packedLight = LightmapTextureManager.pack(11, 7)
model.withLayerContext(immediate, state, PokemonModelRepository.getLayers(species.resourceIdentifier, aspects)) {
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.DEFAULT_UV, 1F, 1F, 1F, 1F)
model.render(matrixStack, buffer, packedLight, OverlayTexture.DEFAULT_UV, 1F, 1F, 1F, 1F)
immediate.draw()
}
matrixStack.pop()
model.setDefault()
DiffuseLighting.enableGuiDepthLighting()
}
@@ -1,48 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.interaction
import net.minecraft.entity.Entity
import net.minecraft.item.ItemStack
import net.minecraft.server.network.ServerPlayerEntity
/**
* An interaction that will affect an [Entity].
* These need to be triggered.
*
* @param T The type of the [Entity] this interaction will affect.
*/
interface EntityInteraction<T : Entity> {
/**
* Fired when a [ServerPlayerEntity] interacts with the target entity.
*
* @param player The [ServerPlayerEntity] interacting with the [entity].
* @param entity The [Entity] of type [T] being interacted.
* @param stack The [ItemStack] used in this interaction.
* @return true if the interaction was successful and no further interactions should be processed
*/
fun onInteraction(player: ServerPlayerEntity, entity: T, stack: ItemStack): Boolean
/**
* Decreases the stack size by a given amount.
* The stack size should be validated beforehand.
* If the [player] is in creative mode the decrement won't be performed.
*
* @param player The [ServerPlayerEntity] that caused the interaction, this is used to check for creative mode.
* @param stack The [ItemStack] being mutated.
* @param amount The amount to deduct from the stack, default is 1.
*/
fun consumeItem(player: ServerPlayerEntity, stack: ItemStack, amount: Int = 1) {
if (!player.isCreative) {
stack.decrement(amount)
}
}
}
@@ -1,29 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.interaction
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import kotlin.reflect.KClass
import net.minecraft.util.Identifier
/**
* A type adapter for [EntityInteraction]s.
* For the default implementation with Pokémon entities see [CobblemonPokemonEntityInteractionTypeAdapter].
*
* @param T The type of [EntityInteraction].
*
* @author Licious
* @since November 30th, 2022
*/
interface EntityInteractionTypeAdapter<T : EntityInteraction<*>> : JsonDeserializer<T>, JsonSerializer<T> {
fun registerInteraction(identifier: Identifier, type: KClass<out T>)
}
@@ -6,8 +6,12 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
{
use(battle, pokemon, itemId, data) {
pokemon.removeVolatile(data[0]);
}
package com.cobblemon.mod.common.api.item
enum class Flavor {
SPICY,
DRY,
SWEET,
BITTER,
SOUR
}
@@ -98,11 +98,6 @@ interface PokemonAndMoveSelectingItem {
fun canUseOnMove(move: Move): Boolean
fun interactWithSpecific(player: ServerPlayerEntity, stack: ItemStack, pokemon: Pokemon): TypedActionResult<ItemStack>? {
if (player.isSneaking) {
return TypedActionResult.pass(stack)
}
MoveSelectCallbacks.create(
player = player,
moves = pokemon.moveSet.toList(),
@@ -151,4 +146,4 @@ interface PokemonAndMoveSelectingItem {
return TypedActionResult.success(stack)
}
}
}
@@ -8,8 +8,6 @@
package com.cobblemon.mod.common.api.item
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.PokemonInteractContext
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.callback.PartySelectCallbacks
import com.cobblemon.mod.common.api.text.red
@@ -24,7 +22,6 @@ import com.cobblemon.mod.common.util.isHeld
import com.cobblemon.mod.common.util.isLookingAt
import com.cobblemon.mod.common.util.party
import net.minecraft.item.ItemStack
import net.minecraft.registry.Registries
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.TypedActionResult
import net.minecraft.util.math.Box
@@ -62,17 +59,15 @@ interface PokemonSelectingItem {
return interactWithSpecificBattle(player, stack, battlePokemon)
}
} ?: run {
if (!player.isSneaking) {
return if (entity != null) {
val pokemon = entity.pokemon
if (entity.ownerUuid == player.uuid) {
applyToPokemon(player, stack, pokemon) ?: TypedActionResult.pass(stack)
} else {
TypedActionResult.fail(stack)
}
if (entity != null) {
val pokemon = entity.pokemon
if (entity.ownerUuid == player.uuid) {
return applyToPokemon(player, stack, pokemon) ?: TypedActionResult.pass(stack)
} else {
interactGeneral(player, stack)
return TypedActionResult.fail(stack)
}
} else {
return interactGeneral(player, stack)
}
}
@@ -94,7 +89,6 @@ interface PokemonSelectingItem {
if (!player.isCreative) {
stack.decrement(1)
}
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(battlePokemon.entity!!.pokemon.species.resourceIdentifier, Registries.ITEM.getId(stack.item)))
}
}
@@ -112,14 +106,9 @@ interface PokemonSelectingItem {
}
fun interactGeneral(player: ServerPlayerEntity, stack: ItemStack): TypedActionResult<ItemStack> {
val party = player.party().toList()
if (party.isEmpty()) {
return TypedActionResult.fail(stack)
}
PartySelectCallbacks.createFromPokemon(
player = player,
pokemon = party,
pokemon = player.party().toList(),
canSelect = ::canUseOnPokemon,
handler = { pk ->
if (stack.isHeld(player)) {
@@ -127,7 +116,6 @@ interface PokemonSelectingItem {
if (!player.isCreative) {
stack.decrement(1)
}
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(pk.species.resourceIdentifier, Registries.ITEM.getId(stack.item)))
}
}
)
@@ -18,8 +18,6 @@ import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtElement
import net.minecraft.nbt.NbtList
import net.minecraft.network.PacketByteBuf
import kotlin.math.min
class MoveSet : Iterable<Move> {
val observable = SimpleObservable<MoveSet>()
private var emit = true
@@ -65,11 +63,6 @@ class MoveSet : Iterable<Move> {
update()
}
fun partialHeal() {
getMoves().forEach { it.currentPp = min((it.currentPp + (it.maxPp / 2)), it.maxPp)}
update()
}
fun clear() {
doWithoutEmitting {
for (i in 0 until MOVE_COUNT){
@@ -8,24 +8,38 @@
package com.cobblemon.mod.common.api.mulch
import net.minecraft.util.StringIdentifiable
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.item.MulchItem
/**
* Represents the different types of Mulch implemented in the mod.
*
*/
enum class MulchVariant(val duration: Int = -1) : StringIdentifiable {
enum class MulchVariant {
COARSE,
GROWTH(3),
GROWTH,
HUMID,
LOAMY,
PEAT,
RICH(3),
RICH,
SANDY,
SURPRISE(3),
NONE;
SURPRISE;
override fun asString(): String {
return name.lowercase()
/**
* Resolves the item version of the Mulch.
*
* @return The [MulchItem] equivalent.
*/
fun item(): MulchItem = when (this) {
COARSE -> CobblemonItems.COARSE_MULCH
GROWTH -> CobblemonItems.GROWTH_MULCH
HUMID -> CobblemonItems.HUMID_MULCH
LOAMY -> CobblemonItems.LOAMY_MULCH
PEAT -> CobblemonItems.PEAT_MULCH
RICH -> CobblemonItems.RICH_MULCH
SANDY -> CobblemonItems.SANDY_MULCH
SURPRISE -> CobblemonItems.SURPRISE_MULCH
}
}
}
@@ -65,7 +65,6 @@ object CobblemonPermissions {
val TEST_PARTY_SLOT = this.create("${COMMAND_PREFIX}testpartyslot", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val CLEAR_PARTY = this.create("${COMMAND_PREFIX}clearparty", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val CLEAR_PC = this.create("${COMMAND_PREFIX}clearpc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
fun all(): Iterable<Permission> = this.permissions
@@ -8,12 +8,9 @@
package com.cobblemon.mod.common.api.pokeball.catching.calculators
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokeball.PokemonCatchRateEvent
import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.entity.LivingEntity
/**
@@ -27,6 +24,7 @@ import net.minecraft.entity.LivingEntity
* @since November 30, 2021
*/
interface CaptureCalculator {
/**
* The literal ID of this calculator.
* Used when registering the calculator to the registry in order to be used by the game rule.
@@ -39,22 +37,10 @@ interface CaptureCalculator {
* Processes a capture attempt with the given params.
*
* @param thrower The [LivingEntity] that threw the [PokeBall].
* @param pokeBallEntity The [EmptyPokeBallEntity] used.
* @param target The target [PokemonEntity] attempting to be captured.
* @return a [CaptureContext] that is the result of the capture attempt.
* @param pokeBall The [PokeBall] used.
* @param target The target [Pokemon] attempting to be captured.
* @return
*/
fun processCapture(thrower: LivingEntity, pokeBallEntity: EmptyPokeBallEntity, target: PokemonEntity) : CaptureContext
fun processCapture(thrower: LivingEntity, pokeBall: PokeBall, target: Pokemon) : CaptureContext
fun getCatchRate(thrower: LivingEntity, pokeBallEntity: EmptyPokeBallEntity, target: PokemonEntity, catchRate: Float): Float {
val event = PokemonCatchRateEvent(
thrower = thrower,
pokemonEntity = target,
pokeBallEntity = pokeBallEntity,
catchRate = catchRate
)
CobblemonEvents.POKEMON_CATCH_RATE.post(event)
return event.catchRate
}
}
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.api.pokemon
import com.cobblemon.mod.common.api.berry.Flavor
import com.cobblemon.mod.common.api.item.Flavor
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.pokemon.Nature
import com.cobblemon.mod.common.util.cobblemonResource
@@ -166,16 +166,6 @@ object Natures {
return allNatures.find { nature -> nature.name == name }
}
/**
* Utility method to get a nature by string
* @return a nature type or null
*/
fun getNature(identifier: String): Nature? {
val nature = getNature(cobblemonResource(identifier))
if(nature != null) return nature
return getNature(Identifier(identifier))
}
/**
* Helper function for a random Nature
* @return a random nature type
@@ -15,21 +15,16 @@ import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.EVs
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.IVs
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.pokemon.*
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
import com.cobblemon.mod.common.pokemon.status.PersistentStatusContainer
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.isInt
import com.cobblemon.mod.common.util.splitMap
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import kotlin.random.Random
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtElement
import net.minecraft.nbt.NbtList
@@ -91,9 +86,6 @@ open class PokemonProperties {
props.ability = parseStringOfRegistry(keyPairs, listOf("ability")) { Abilities.get(it)?.name }
props.status = parseStringOfRegistry(keyPairs, listOf("status")) { (Statuses.getStatus(it) ?: Statuses.getStatus(it.asIdentifierDefaultingNamespace()))?.showdownName }
props.nickname = parseText(keyPairs, listOf("nickname", "nick"))
props.teraType = parseStringOfRegistry(keyPairs, listOf("tera_type", "tera")) { ElementalTypes.get(it)?.name }
props.dmaxLevel = parseIntProperty(keyPairs, listOf("dmax_level", "dmax"))?.coerceIn(0, Cobblemon.config.maxDynamaxLevel)
props.gmaxFactor = parseBooleanProperty(keyPairs, listOf("gmax_factor", "gmax"))
val maybeIVs = IVs()
val maybeEVs = EVs()
@@ -155,10 +147,9 @@ open class PokemonProperties {
}
private fun parseSpeciesIdentifier(keyPairs: MutableList<Pair<String, String?>>): String? {
fun cleanSpeciesName(string: String) = string.lowercase().replace("[^a-z0-9:]".toRegex(), "")
val matched = getMatchedKeyPair(keyPairs, listOf("species"))
if (matched != null) {
val value = matched.second?.let(::cleanSpeciesName) ?: return null
val value = matched.second?.lowercase() ?: return null
return if (value.lowercase() == "random") {
"random"
} else {
@@ -177,7 +168,7 @@ open class PokemonProperties {
"random"
} else {
try {
val identifier = cleanSpeciesName(pair.first).asIdentifierDefaultingNamespace()
val identifier = pair.first.asIdentifierDefaultingNamespace()
val found = PokemonSpecies.getByIdentifier(identifier) ?: return@find false
if (found.resourceIdentifier.namespace == Cobblemon.MODID) found.resourceIdentifier.path else found.resourceIdentifier.toString()
} catch (e: InvalidIdentifierException) {
@@ -255,9 +246,6 @@ open class PokemonProperties {
var ability: String? = null
var aspects: Set<String> = emptySet()
var status: String? = null
var teraType: String? = null
var dmaxLevel: Int? = null
var gmaxFactor: Boolean? = null
var ivs: IVs? = null
var evs: EVs? = null
@@ -299,17 +287,14 @@ open class PokemonProperties {
customProperties.forEach { it.apply(pokemon) }
ivs?.let { ivs ->
ivs.forEach { stat ->
pokemon.setIV(stat.key, stat.value)
pokemon.ivs[stat.key] = stat.value
}
}
evs?.let { evs ->
evs.forEach { stat ->
pokemon.setEV(stat.key, stat.value)
pokemon.evs[stat.key] = stat.value
}
}
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemon.teraType = type } }
dmaxLevel?.let { pokemon.dmaxLevel = it }
gmaxFactor?.let { pokemon.gmaxFactor = it }
}
fun apply(pokemonEntity: PokemonEntity) {
@@ -337,17 +322,14 @@ open class PokemonProperties {
customProperties.forEach { it.apply(pokemonEntity) }
ivs?.let { ivs ->
ivs.forEach { stat ->
pokemonEntity.pokemon.setIV(stat.key, stat.value)
pokemonEntity.pokemon.ivs[stat.key] = stat.value
}
}
evs?.let { evs ->
evs.forEach { stat ->
pokemonEntity.pokemon.setEV(stat.key, stat.value)
pokemonEntity.pokemon.evs[stat.key] = stat.value
}
}
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemonEntity.pokemon.teraType = type } }
dmaxLevel?.let { pokemonEntity.pokemon.dmaxLevel = it }
gmaxFactor?.let { pokemonEntity.pokemon.gmaxFactor = it }
}
fun matches(pokemon: Pokemon): Boolean {
@@ -381,9 +363,6 @@ open class PokemonProperties {
evs?.forEach{ stat ->
if (stat.value != pokemon.evs[stat.key]) { return false }
}
teraType?.takeIf { it != pokemon.teraType.name }?.let { return false }
dmaxLevel?.takeIf { it != pokemon.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != pokemon.gmaxFactor }?.let { return false }
return customProperties.none { !it.matches(pokemon) }
}
@@ -416,9 +395,6 @@ open class PokemonProperties {
evs?.forEach{ stat ->
if (stat.value != pokemonEntity.pokemon.evs[stat.key]) { return false }
}
teraType?.takeIf { it != pokemonEntity.pokemon.teraType.name }?.let { return false }
dmaxLevel?.takeIf { it != pokemonEntity.pokemon.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != pokemonEntity.pokemon.gmaxFactor }?.let { return false }
return customProperties.none { !it.matches(pokemonEntity) }
}
@@ -459,33 +435,24 @@ open class PokemonProperties {
if (stat.value != propertiesEVs[stat.key]) { return false }
}
}
teraType?.takeIf { it != properties.teraType }?.let { return false }
dmaxLevel?.takeIf { it != properties.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != properties.gmaxFactor }?.let { return false }
return true
}
fun create(): Pokemon {
val pokemon = Pokemon()
apply(pokemon)
pokemon.initialize()
roll(pokemon)
// Force the setter to initialize it
pokemon.species = pokemon.species
// This should only be a thing once we have moveset control in properties until then a creation should require a moveset init.
/*
if (pokemon.moveSet.none { it != null }) {
pokemon.initializeMoveset()
}
*/
pokemon.initializeMoveset()
return pokemon
}
// TEST YOUR LUCK!
fun roll(pokemon: Pokemon) {
val baseTypes = pokemon.species.types.toList()
if (this.shiny == null) pokemon.shiny = Cobblemon.config.shinyRate.checkRate()
if (this.gmaxFactor == null) pokemon.gmaxFactor = Cobblemon.config.gigantamaxFactorRate.checkRate()
if (this.teraType == null) pokemon.teraType =
if (Cobblemon.config.teraTypeRate.checkRate()) ElementalTypes.all().filter { !baseTypes.contains(it) }.random()
else baseTypes.random()
}
// If the config value is at least 1, then do 1/x and use that as the property chance
private fun Float.checkRate(): Boolean = this >= 1 && (Random.Default.nextFloat() < 1 / this)
fun createEntity(world: World): PokemonEntity {
return PokemonEntity(world, create())
}
@@ -506,9 +473,6 @@ open class PokemonProperties {
status?.let { nbt.putString(DataKeys.POKEMON_STATUS_NAME, it) }
ivs?.let { nbt.put(DataKeys.POKEMON_IVS, it.saveToNBT(NbtCompound())) }
evs?.let { nbt.put(DataKeys.POKEMON_EVS, it.saveToNBT(NbtCompound())) }
teraType?.let { nbt.putString(DataKeys.POKEMON_TERA_TYPE, it) }
dmaxLevel?.let { nbt.putInt(DataKeys.POKEMON_DMAX_LEVEL, it) }
gmaxFactor?.let { nbt.putBoolean(DataKeys.POKEMON_GMAX_FACTOR, it) }
val custom = NbtList()
customProperties.map { NbtString.of(it.asString()) }.forEach { custom.add(it) }
nbt.put(DataKeys.POKEMON_PROPERTIES_CUSTOM, custom)
@@ -530,9 +494,6 @@ open class PokemonProperties {
status = if (tag.contains(DataKeys.POKEMON_STATUS_NAME)) tag.getString(DataKeys.POKEMON_STATUS_NAME) else null
ivs = if (tag.contains(DataKeys.POKEMON_IVS)) ivs?.loadFromNBT(tag.getCompound(DataKeys.POKEMON_IVS)) as IVs? else null
evs = if (tag.contains(DataKeys.POKEMON_EVS)) evs?.loadFromNBT(tag.getCompound(DataKeys.POKEMON_EVS)) as EVs? else null
teraType = if (tag.contains(DataKeys.POKEMON_TERA_TYPE)) tag.getString(DataKeys.POKEMON_TERA_TYPE) else null
dmaxLevel = if (tag.contains(DataKeys.POKEMON_DMAX_LEVEL)) tag.getInt(DataKeys.POKEMON_DMAX_LEVEL) else null
gmaxFactor = if (tag.contains(DataKeys.POKEMON_GMAX_FACTOR)) tag.getBoolean(DataKeys.POKEMON_GMAX_FACTOR) else null
val custom = tag.getList(DataKeys.POKEMON_PROPERTIES_CUSTOM, NbtElement.STRING_TYPE.toInt())
// This is kinda gross
custom.forEach { customProperties.addAll(parse(it.asString()).customProperties) }
@@ -556,9 +517,6 @@ open class PokemonProperties {
status?.let { json.addProperty(DataKeys.POKEMON_STATUS_NAME, it) }
ivs?.let { json.add(DataKeys.POKEMON_IVS, it.saveToJSON(JsonObject())) }
evs?.let { json.add(DataKeys.POKEMON_EVS, it.saveToJSON(JsonObject())) }
teraType?.let { json.addProperty(DataKeys.POKEMON_TERA_TYPE, it) }
dmaxLevel?.let { json.addProperty(DataKeys.POKEMON_DMAX_LEVEL, it) }
gmaxFactor?.let { json.addProperty(DataKeys.POKEMON_GMAX_FACTOR, it) }
val custom = JsonArray()
customProperties.map { it.asString() }.forEach { custom.add(it) }
json.add(DataKeys.POKEMON_PROPERTIES_CUSTOM, custom)
@@ -581,9 +539,6 @@ open class PokemonProperties {
status = json.get(DataKeys.POKEMON_STATUS_NAME)?.asString
ivs?.loadFromJSON(json.getAsJsonObject(DataKeys.POKEMON_IVS))
evs?.loadFromJSON(json.getAsJsonObject(DataKeys.POKEMON_EVS))
teraType = json.get(DataKeys.POKEMON_TERA_TYPE)?.asString
dmaxLevel = json.get(DataKeys.POKEMON_DMAX_LEVEL)?.asInt
gmaxFactor = json.get(DataKeys.POKEMON_GMAX_FACTOR)?.asBoolean
val custom = json.get(DataKeys.POKEMON_PROPERTIES_CUSTOM)?.asJsonArray
// This is still kinda gross
custom?.forEach { customProperties.addAll(parse(it.asString).customProperties) }
@@ -610,9 +565,6 @@ open class PokemonProperties {
evs?.forEach{ stat ->
pieces.add("${stat.key}_ev=${stat.value}")
}
teraType?.let { pieces.add("tera_type=$it") }
dmaxLevel?.let { pieces.add("dmax_level=$it") }
gmaxFactor?.let { pieces.add("gmax_factor=$it") }
customProperties.forEach { pieces.add(it.asString()) }
return pieces.joinToString(separator)
}
@@ -34,6 +34,7 @@ import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Species
@@ -41,8 +42,7 @@ import com.cobblemon.mod.common.pokemon.SpeciesAdditions
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonPreEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonRequirementAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.NbtItemPredicateAdapter
import com.cobblemon.mod.common.pokemon.evolution.predicate.NbtItemPredicate
import com.cobblemon.mod.common.pokemon.helditem.CobblemonEmptyHeldItemManager
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.cobblemonResource
@@ -95,7 +95,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
.registerTypeAdapter(StatusEffect::class.java, RegistryElementAdapter<StatusEffect> { Registries.STATUS_EFFECT })
.registerTypeAdapter(NbtItemPredicate::class.java, NbtItemPredicateAdapter)
.disableHtmlEscaping()
.enableComplexMapKeySerialization()
.create()
@@ -239,14 +238,11 @@ object PokemonSpecies : JsonDataRegistry<Species> {
val heightm = form?.height ?: species.height
val weightkg = form?.weight ?: species.weight
// This is ugly, but we already have it hardcoded in the mod anyway
val maxHP = if (species.showdownId() == "shedinja") 1 else null
val maxHp = if (species.showdownId() == "shedinja") 1 else null
val canGigantamax: String? = if (form?.gigantamaxMove != null) form.gigantamaxMove.name else null
val cannotDynamax = form?.dynamaxBlocked ?: species.dynamaxBlocked
// ToDo battleOnly
// ToDo changesFrom
val requiredMove = form?.requiredMove
val requiredItem = form?.requiredItem
val requiredItems = form?.requiredItems
}
private fun createShowdownName(species: Species): String {
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.pokemon.stats
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
/**
@@ -54,24 +53,9 @@ interface EvCalculator {
object Generation8EvCalculator : EvCalculator {
private val powerItems = mapOf(
Stats.SPEED to CobblemonItemTags.POWER_ANKLET,
Stats.SPECIAL_DEFENCE to CobblemonItemTags.POWER_BAND,
Stats.DEFENCE to CobblemonItemTags.POWER_BELT,
Stats.ATTACK to CobblemonItemTags.POWER_BRACER,
Stats.SPECIAL_ATTACK to CobblemonItemTags.POWER_LENS,
Stats.HP to CobblemonItemTags.POWER_WEIGHT
)
override fun calculate(battlePokemon: BattlePokemon, opponentPokemon: BattlePokemon): Map<Stat, Int> {
val heldItem = battlePokemon.effectedPokemon.heldItemNoCopy()
val evYield = mutableMapOf<Stat, Int>()
for ((stat, value) in opponentPokemon.originalPokemon.form.evYield) {
val boost = if (!heldItem.isEmpty && heldItem.isIn(powerItems[stat])) 8 else 0
evYield[stat] = evYield.getOrDefault(stat, 0) + value + boost
}
return evYield
// ToDo Once held items and EV related items are implemented update this
return opponentPokemon.originalPokemon.form.evYield
}
}
@@ -14,7 +14,9 @@ import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.feature.SeasonFeatureHandler
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.weightedSelection
import kotlin.random.Random
/**
* A [SpawnAction] that will spawn a single [PokemonEntity].
@@ -33,6 +35,10 @@ class PokemonSpawnAction(
if (props.level == null) {
props.level = detail.getDerivedLevelRange().random()
}
if (props.shiny == null) {
// If the config value is at least 1, then do 1/x and use that as the shiny chance
props.shiny = Cobblemon.config.shinyRate.takeIf { it >= 1 }?.let { Random.Default.nextFloat() < 1 / it }
}
val heldItems = detail.heldItems?.takeIf { it.isNotEmpty() }?.toMutableList() ?: mutableListOf()
val heldItem = if (heldItems.isNotEmpty()) {
val until100 = 1 - heldItems.sumOf { it.percentage / 100 }
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.lang
import com.google.gson.annotations.SerializedName
import kotlin.math.ceil
@@ -49,8 +48,6 @@ class PokemonSpawnDetail : SpawnDetail() {
override fun getName(): MutableText {
displayName?.let { return it.asTranslated() }
val speciesString = pokemon.species
if (speciesString != null) {
if (speciesString.lowercase() == "random") {
@@ -0,0 +1,20 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.influence
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules.DO_POKEMON_SPAWNING
open class GameRuleInfluence : SpawningInfluence {
override fun affectSpawnable(detail: SpawnDetail, ctx: SpawningContext): Boolean {
return detail !is PokemonSpawnDetail || ctx.world.gameRules.getBoolean(DO_POKEMON_SPAWNING)
}
}
@@ -30,7 +30,6 @@ import net.minecraft.util.math.Vec3d
import net.minecraft.world.World
import net.minecraft.world.chunk.Chunk
import net.minecraft.world.chunk.ChunkStatus
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules.DO_POKEMON_SPAWNING
/**
* A type of [TickingSpawner] that operates within some area. When this spawner type
@@ -61,9 +60,6 @@ abstract class AreaSpawner(
override fun run(cause: SpawnCause): Pair<SpawningContext, SpawnDetail>? {
val area = getArea(cause)
if (area?.world?.gameRules?.getBoolean(DO_POKEMON_SPAWNING) == false) {
return null
}
val constrainedArea = if (area != null) constrainArea(area) else null
if (constrainedArea != null) {
val numberNearby = constrainedArea.world.getEntitiesByClass(
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.spawning.spawner
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.SpawnerManager
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.influence.GameRuleInfluence
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import net.minecraft.server.network.ServerPlayerEntity
@@ -25,7 +26,7 @@ import net.minecraft.server.network.ServerPlayerEntity
*/
object PlayerSpawnerFactory {
var spawns: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
var influenceBuilders = mutableListOf<(player: ServerPlayerEntity) -> SpawningInfluence?>({ PlayerLevelRangeInfluence(it, variation = 5) })
var influenceBuilders = mutableListOf<(player: ServerPlayerEntity) -> SpawningInfluence?>({ PlayerLevelRangeInfluence(it, variation = 5) }, { GameRuleInfluence() })
fun create(spawnerManager: SpawnerManager, player: ServerPlayerEntity): PlayerSpawner {
val influences = influenceBuilders.mapNotNull { it(player) }
@@ -33,4 +34,4 @@ object PlayerSpawnerFactory {
it.influences.addAll(influences)
}
}
}
}
@@ -121,12 +121,8 @@ class ReforgedConversion(val base: Path) : CobblemonConverter<NbtCompound> {
evs[Stats.SPECIAL_DEFENCE] = nbt.getInt("EVSpecialDefense")
evs[Stats.SPEED] = nbt.getInt("EVSpeed")
ivs.forEach { stat ->
result.setIV(stat.key, stat.value)
}
evs.forEach { stat ->
result.setEV(stat.key, stat.value)
}
result.evs = evs
result.ivs = ivs
for (move in nbt.getList("Moveset", 10)) {
val compound = move as NbtCompound
@@ -9,8 +9,6 @@
package com.cobblemon.mod.common.api.storage.party
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.PartyCheckContext
import com.cobblemon.mod.common.api.pokemon.evolution.PassiveEvolution
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.battles.BattleRegistry
@@ -20,13 +18,13 @@ import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.isPokemonEntity
import com.cobblemon.mod.common.util.lang
import java.util.UUID
import kotlin.math.round
import kotlin.random.Random
import net.minecraft.nbt.NbtCompound
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import java.util.*
import kotlin.math.ceil
import kotlin.math.round
import kotlin.random.Random
/**
* A [PartyStore] used for a single player. This uses the player's UUID as the store's UUID, and is declared as its own
@@ -54,7 +52,6 @@ open class PlayerPartyStore(
override fun add(pokemon: Pokemon): Boolean {
return if (super.add(pokemon)) {
pokemon.getOwnerPlayer()?.let { CobblemonCriteria.PARTY_CHECK.trigger(it, PartyCheckContext(this)) }
true
} else {
val player = playerUUID.getPlayer()
@@ -154,31 +151,4 @@ open class PlayerPartyStore(
}
return true
}
override fun swap(position1: PartyPosition, position2: PartyPosition) {
super.swap(position1, position2)
//Make it so we can check what's in the Player's party
val pokemon1 = get(position1)
val pokemon2 = get(position2)
if (pokemon1 != null && pokemon2 != null) {
val player = pokemon1.getOwnerPlayer()
if (player != null) {
CobblemonCriteria.PARTY_CHECK.trigger(player, PartyCheckContext(this))
}
} else if (pokemon1 != null || pokemon2 != null) {
var player = pokemon1?.getOwnerPlayer()
if (player != null) {
CobblemonCriteria.PARTY_CHECK.trigger(player, PartyCheckContext(this))
} else {
player = pokemon2!!.getOwnerPlayer()
CobblemonCriteria.PARTY_CHECK.trigger(player!!, PartyCheckContext(this))
}
}
}
override fun set(position: PartyPosition, pokemon: Pokemon) {
super.set(position, pokemon)
pokemon.getOwnerPlayer()?.let { CobblemonCriteria.PARTY_CHECK.trigger(it, PartyCheckContext(this)) }
}
}
@@ -24,16 +24,8 @@ class PlayerAdvancementData {
private set
var totalBattleVictoryCount: Int = 0
private set
var totalPvPBattleVictoryCount: Int = 0
private set
var totalPvWBattleVictoryCount: Int = 0
private set
var totalPvNBattleVictoryCount: Int = 0
private set
var totalShinyCaptureCount: Int = 0
private set
var totalTradedCount: Int = 0
private set
private var totalTypeCaptureCounts = mutableMapOf<String, Int>()
private var totalDefeatedCounts = mutableMapOf<Identifier, Int>()
@@ -53,29 +45,13 @@ class PlayerAdvancementData {
}
fun updateTotalBattleVictoryCount() {
totalBattleVictoryCount++
}
fun updateTotalPvPBattleVictoryCount() {
totalPvPBattleVictoryCount++
}
fun updateTotalPvWBattleVictoryCount() {
totalPvWBattleVictoryCount++
}
fun updateTotalPvNBattleVictoryCount() {
totalPvNBattleVictoryCount++
totalEvolvedCount++
}
fun updateTotalShinyCaptureCount() {
totalShinyCaptureCount++
}
fun updateTotalTradedCount() {
totalTradedCount++
}
fun getTotalTypeCaptureCount(type: ElementalType): Int {
if (!totalTypeCaptureCounts.containsKey(key = type.name)) {
totalTypeCaptureCounts[type.name] = 0
@@ -11,10 +11,8 @@ package com.cobblemon.mod.common.api.storage.player
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.net.messages.client.starter.SetClientPlayerDataPacket
import com.cobblemon.mod.common.util.cobblemonResource
import java.util.UUID
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
data class PlayerData(
val uuid: UUID,
@@ -22,8 +20,7 @@ data class PlayerData(
var starterLocked: Boolean,
var starterSelected: Boolean,
var starterUUID: UUID?,
var keyItems: MutableSet<Identifier>,
val extraData: MutableMap<String, PlayerDataExtension>,
val extraData: MutableMap<String, PlayerDataExtension>
) {
var advancementData: PlayerAdvancementData = PlayerAdvancementData()
@@ -39,7 +36,6 @@ data class PlayerData(
starterLocked = !Cobblemon.starterConfig.allowStarterOnJoin,
starterSelected = false,
starterUUID = null,
keyItems = mutableSetOf(),
extraData = mutableMapOf()
)
}
@@ -8,10 +8,13 @@
package com.cobblemon.mod.common.api.storage.player.adapter
import com.cobblemon.mod.common.api.storage.player.PlayerAdvancementData
import com.cobblemon.mod.common.api.storage.player.PlayerData
import com.cobblemon.mod.common.api.storage.player.PlayerDataExtension
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.fromJson
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.removeIf
import com.google.gson.GsonBuilder
import java.io.BufferedReader
import java.io.FileReader
@@ -21,8 +24,6 @@ import java.util.UUID
import net.minecraft.server.MinecraftServer
import net.minecraft.util.Identifier
import net.minecraft.util.WorldSavePath
import kotlin.reflect.KMutableProperty
import kotlin.reflect.full.memberProperties
class JsonPlayerData: PlayerDataStoreAdapter {
@@ -56,11 +57,9 @@ class JsonPlayerData: PlayerDataStoreAdapter {
playerFile.parentFile.mkdirs()
return if (playerFile.exists()) {
gson.fromJson<PlayerData>(BufferedReader(FileReader(playerFile))).also {
// Resolves old data that's missing new properties
val newProps = it::class.memberProperties.filterIsInstance<KMutableProperty<*>>().filter { member -> member.getter.call(it) == null }
if (newProps.isNotEmpty()) {
val defaultData = PlayerData.defaultData(uuid)
newProps.forEach { member -> member.setter.call(it, member.getter.call(defaultData)) }
// Resolves old data from pre 1.0, don't even ask man. - Hiroku
if (it.advancementData == null) {
it.advancementData = PlayerAdvancementData()
}
}
} else {
@@ -119,10 +119,6 @@ object CobblemonBiomeTags {
val IS_VOlCANIC = create("is_volcanic")
@JvmField
val IS_WINTER = create("is_winter")
@JvmField
val IS_SPARSE = create("is_sparse")
@JvmField
val IS_DENSE = create("is_dense")
// Has Feature tags
@JvmField
@@ -26,9 +26,6 @@ object CobblemonBlockTags {
@JvmField val APRICORN_LOGS = createTag("apricorn_logs")
@JvmField val APRICORN_SAPLINGS = createTag("apricorn_saplings")
@JvmField val APRICORNS = createTag("apricorns")
@JvmField val BERRY_WILD_SOIL = createTag("berry_wild_soil")
@JvmField val BERRY_SOIL = createTag("berry_soil")
@JvmField val BERRY_REPLACEABLE = createTag("berry_replaceable")
@JvmField val CROPS = createTag("crops")
@JvmField val DRIPSTONE_GROWABLE = createTag("dripstone_growable")
@JvmField val DRIPSTONE_REPLACEABLES = createTag("dripstone_replaceables")
@@ -38,8 +35,7 @@ object CobblemonBlockTags {
@JvmField val ROOTS_SPREADABLE = createTag("roots_spreadable")
@JvmField val SMALL_FLOWERS = createTag("small_flowers")
@JvmField val SEES_SKY = createTag("sees_sky")
@JvmField val SNOW_BLOCK = createTag("snow_block")
@JvmField val ROOTS = createTag("roots")
private fun createTag(name: String) = TagKey.of(RegistryKeys.BLOCK, cobblemonResource(name))
}
}
@@ -42,18 +42,6 @@ object CobblemonItemTags {
@JvmField
val EVERSTONE = create("held/everstone")
@JvmField
val POWER_ANKLET = create("held/power_anklet")
@JvmField
val POWER_BAND = create("held/power_band")
@JvmField
val POWER_BELT = create("held/power_belt")
@JvmField
val POWER_BRACER = create("held/power_bracer")
@JvmField
val POWER_LENS = create("held/power_lens")
@JvmField
val POWER_WEIGHT = create("held/power_weight")
@JvmField
val EVOLUTION_STONES = create("evolution_stones")
@JvmField
val EVOLUTION_ITEMS = create("evolution_items")
@@ -169,6 +169,4 @@ object ElementalTypes {
}
fun count() = allTypes.size
fun all() = this.allTypes.toList()
}
@@ -21,27 +21,24 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.party
import java.util.Optional
import java.util.UUID
import net.minecraft.entity.Entity
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.MutableText
import net.minecraft.text.Text
import java.util.*
import kotlin.collections.HashMap
object BattleBuilder {
@JvmOverloads
fun pvp1v1(
player1: ServerPlayerEntity,
player2: ServerPlayerEntity,
leadingPokemonPlayer1: UUID? = null,
leadingPokemonPlayer2: UUID? = null,
battleFormat: BattleFormat = BattleFormat.GEN_9_SINGLES,
cloneParties: Boolean = false,
healFirst: Boolean = false,
partyAccessor: (ServerPlayerEntity) -> PartyStore = { it.party() }
): BattleStartResult {
val team1 = partyAccessor(player1).toBattleTeam(clone = cloneParties, checkHealth = !healFirst, leadingPokemonPlayer1)
val team2 = partyAccessor(player2).toBattleTeam(clone = cloneParties, checkHealth = !healFirst, leadingPokemonPlayer2)
val team1 = partyAccessor(player1).toBattleTeam(clone = cloneParties, checkHealth = !healFirst)
val team2 = partyAccessor(player2).toBattleTeam(clone = cloneParties, checkHealth = !healFirst)
val player1Actor = PlayerBattleActor(player1.uuid, team1)
val player2Actor = PlayerBattleActor(player2.uuid, team2)
@@ -92,7 +89,6 @@ object BattleBuilder {
* @param fleeDistance How far away the player must get to flee the Pokémon. If the value is -1, it cannot be fled.
* @param party The party of the player to use for the battle. This does not need to be their actual party. Defaults to it though.
*/
@JvmOverloads
fun pve(
player: ServerPlayerEntity,
pokemonEntity: PokemonEntity,
@@ -28,7 +28,7 @@ data class BattleFormat(
companion object {
val GEN_9_SINGLES = BattleFormat(
battleType = BattleTypes.SINGLES,
ruleSet = setOf(BattleRules.OBTAINABLE, BattleRules.PAST, BattleRules.UNOBTAINABLE)
ruleSet = setOf(BattleRules.OBTAINABLE)
)
val GEN_9_DOUBLES = BattleFormat(
@@ -16,9 +16,9 @@ import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.net.messages.client.battle.BattleChallengeExpiredPacket
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.google.gson.GsonBuilder
import java.time.Instant
import java.util.Optional
@@ -31,17 +31,13 @@ object BattleRegistry {
class BattleChallenge(
val challengeId: UUID,
val challengedPlayerUUID: UUID,
val selectedPokemonId: UUID,
var expiryTimeSeconds: Int = 60
) {
val challengedTime = Instant.now()
fun isExpired() = Instant.now().isAfter(challengedTime.plusSeconds(expiryTimeSeconds.toLong()))
}
val gson = GsonBuilder()
.disableHtmlEscaping()
.registerTypeAdapter(ShowdownMoveset::class.java, ShowdownMovesetAdapter)
.create()
val gson = GsonBuilder().disableHtmlEscaping().create()
private val battleMap = ConcurrentHashMap<UUID, PokemonBattle>()
// Challenger to challenge
val pvpChallenges = mutableMapOf<UUID, BattleChallenge>()
@@ -108,7 +104,7 @@ object BattleRegistry {
}|"
)
// Nature
val battleNature = pk.effectiveNature
val battleNature = pk.mintedNature ?: pk.nature
packedTeamBuilder.append("${battleNature.name.path}|")
// EVs
val evsInOrder = Stats.PERMANENT.map { pk.evs.getOrDefault(it) }.joinToString(separator = ",")
@@ -133,12 +129,11 @@ object BattleRegistry {
// Hidden Power Type
packedTeamBuilder.append(",")
// Gigantamax
packedTeamBuilder.append("${if (pk.gmaxFactor) "G" else ""},")
packedTeamBuilder.append(",")
// DynamaxLevel
// 0 - 9, empty == 10
packedTeamBuilder.append("${if (pk.dmaxLevel < 10) pk.dmaxLevel else ""},")
packedTeamBuilder.append(",")
// Teratype
packedTeamBuilder.append("${pokemon.effectedPokemon.teraType.name},")
packedTeamBuilder.append(",")
team.add(packedTeamBuilder.toString())
}
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.battles
object BattleRules {
const val OBTAINABLE = "Obtainable"
const val PAST = "+Past"
const val UNOBTAINABLE = "+Unobtainable"
const val TEAM_PREVIEW = "Team Preview"
const val ENDLESS_BATTLE_CLAUSE = "Endless Battle Clause"
const val CANCEL_MOD = "Cancel Mod"
@@ -19,7 +19,6 @@ class InBattleMove {
var maxpp: Int = 100
var target: MoveTarget = MoveTarget.self
var disabled: Boolean = false
var gimmickMove: InBattleGimmickMove? = null
companion object {
fun loadFromBuffer(buffer: PacketByteBuf): InBattleMove {
@@ -45,27 +44,4 @@ class InBattleMove {
buffer.writeEnumConstant(target)
buffer.writeBoolean(disabled)
}
}
// Defined in sim/battle-actions.ts canZMove and getMaxMove
class InBattleGimmickMove {
lateinit var move: String
var target: MoveTarget = MoveTarget.self
var disabled: Boolean = false
companion object {
fun loadFromBuffer(buffer: PacketByteBuf): InBattleGimmickMove {
return InBattleGimmickMove().apply {
move = buffer.readString()
target = buffer.readEnumConstant(MoveTarget::class.java)
disabled = buffer.readBoolean()
}
}
}
fun saveToBuffer(buffer: PacketByteBuf) {
buffer.writeString(move)
buffer.writeEnumConstant(target)
buffer.writeBoolean(disabled)
}
}
@@ -8,22 +8,14 @@
package com.cobblemon.mod.common.battles
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.item.battle.BagItem
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.hasKeyItem
import com.cobblemon.mod.common.util.readSizedInt
import com.cobblemon.mod.common.util.writeSizedInt
import com.google.gson.*
import com.google.gson.reflect.TypeToken
import java.lang.Integer.max
import java.util.UUID
import net.minecraft.network.PacketByteBuf
import java.lang.reflect.Type
class ShowdownActionRequest(
var wait: Boolean = false,
var active: MutableList<ShowdownMoveset>? = null,
@@ -71,22 +63,6 @@ class ShowdownActionRequest(
}
return this
}
fun sanitize(battle: PokemonBattle, battleActor: BattleActor) {
val player = battle.players.find { it.uuid == battleActor.uuid } ?: return
this.active?.forEach { moveset ->
moveset.getGimmicks().forEach { gimmick ->
// TODO: use the Identifiers of actual items
val triggerItem = when(gimmick) {
ShowdownMoveset.Gimmick.MEGA_EVOLUTION -> cobblemonResource("key_stone")
ShowdownMoveset.Gimmick.DYNAMAX -> cobblemonResource("dynamax_band")
ShowdownMoveset.Gimmick.TERASTALLIZATION -> cobblemonResource("tera_orb")
else -> cobblemonResource("z_ring")
}
if (!player.hasKeyItem(triggerItem)) moveset.blockGimmick(gimmick)
}
}
}
}
enum class ShowdownActionResponseType(val loader: (PacketByteBuf) -> ShowdownActionResponse) {
@@ -115,20 +91,18 @@ abstract class ShowdownActionResponse(val type: ShowdownActionResponseType) {
abstract fun toShowdownString(activeBattlePokemon: ActiveBattlePokemon, showdownMoveSet: ShowdownMoveset?): String
}
data class MoveActionResponse(var moveName: String, var targetPnx: String? = null, var gimmickID: String? = null): ShowdownActionResponse(ShowdownActionResponseType.MOVE) {
data class MoveActionResponse(var moveName: String, var targetPnx: String? = null): ShowdownActionResponse(ShowdownActionResponseType.MOVE) {
override fun isValid(activeBattlePokemon: ActiveBattlePokemon, showdownMoveSet: ShowdownMoveset?, forceSwitch: Boolean): Boolean {
if (forceSwitch || showdownMoveSet == null) {
return false
}
val move = showdownMoveSet.moves.find { it.id == moveName } ?: return false
val gimmickMove = move.gimmickMove
val validGimmickMove = gimmickMove != null && !gimmickMove.disabled
if (!validGimmickMove && !move.canBeUsed()) {
if (!move.canBeUsed()) {
// No PP or disabled or something
return false
}
val availableTargets = (gimmickMove?.target ?: move.target).targetList(activeBattlePokemon)?.takeIf { it.isNotEmpty() } ?: return true
val availableTargets = move.target.targetList(activeBattlePokemon)?.takeIf { it.isNotEmpty() } ?: return true
val pnx = targetPnx ?: return false // If the targets list is non-null then they need to have specified a target
val (_, targetPokemon) = activeBattlePokemon.actor.battle.getActorAndActiveSlotFromPNX(pnx)
@@ -150,21 +124,22 @@ data class MoveActionResponse(var moveName: String, var targetPnx: String? = nul
"move $moveIndex $digit"
} else {
"move $moveIndex"
}.plus(gimmickID?.let { " $gimmickID" } ?: "")
}
}
override fun saveToBuffer(buffer: PacketByteBuf) {
super.saveToBuffer(buffer)
buffer.writeString(moveName)
buffer.writeNullable(targetPnx) { _, targetPnx -> buffer.writeString(targetPnx) }
buffer.writeNullable(gimmickID) { _, gimmickID -> buffer.writeString(gimmickID) }
buffer.writeBoolean(targetPnx != null)
targetPnx?.let(buffer::writeString)
}
override fun loadFromBuffer(buffer: PacketByteBuf): ShowdownActionResponse {
super.loadFromBuffer(buffer)
moveName = buffer.readString()
targetPnx = buffer.readNullable { buffer.readString() }
gimmickID = buffer.readNullable { buffer.readString() }
if (buffer.readBoolean()) {
targetPnx = buffer.readString()
}
return this
}
}
@@ -268,31 +243,11 @@ class BagItemActionResponse(val bagItem: BagItem, val target: BattlePokemon, val
class ShowdownMoveset {
lateinit var moves: List<InBattleMove>
var trapped = false
var canMegaEvo = false
var canUltraBurst = false
var canZMove: List<InBattleGimmickMove?>? = null
var canDynamax = false
var maxMoves: List<InBattleGimmickMove?>? = null
var canTerastallize: String? = null
fun saveToBuffer(buffer: PacketByteBuf) {
buffer.writeSizedInt(IntSize.U_BYTE, moves.size)
moves.forEach { it.saveToBuffer(buffer) }
buffer.writeBoolean(trapped)
buffer.writeBoolean(canMegaEvo)
buffer.writeBoolean(canUltraBurst)
buffer.writeNullable(canZMove) { _, canZMove ->
canZMove.forEach {
buffer.writeNullable(it) { _, zmove -> zmove.saveToBuffer(buffer)}
}
}
buffer.writeBoolean(canDynamax)
buffer.writeNullable(maxMoves) { _, maxMoves ->
maxMoves.forEach {
buffer.writeNullable(it) { _, maxMove -> maxMove.saveToBuffer(buffer)}
}
}
buffer.writeNullable(canTerastallize) { _, teraType -> buffer.writeString(teraType) }
}
fun loadFromBuffer(buffer: PacketByteBuf): ShowdownMoveset {
@@ -302,60 +257,8 @@ class ShowdownMoveset {
}
this.moves = moves
this.trapped = buffer.readBoolean()
this.canMegaEvo = buffer.readBoolean()
this.canUltraBurst = buffer.readBoolean()
this.canZMove = buffer.readNullable {
val zMoves = mutableListOf<InBattleGimmickMove?>()
repeat(moves.size) { zMoves.add(buffer.readNullable { InBattleGimmickMove.loadFromBuffer(buffer) }) }
return@readNullable zMoves
}
this.canDynamax = buffer.readBoolean()
this.maxMoves = buffer.readNullable {
val maxMoves = mutableListOf<InBattleGimmickMove?>()
repeat(moves.size) { maxMoves.add(buffer.readNullable { InBattleGimmickMove.loadFromBuffer(buffer) }) }
return@readNullable maxMoves
}
this.canTerastallize = buffer.readNullable{ buffer.readString() }
this.setGimmickMapping()
return this
}
/** Showdown IDs of battle gimmicks. */
enum class Gimmick(val id: String) {
MEGA_EVOLUTION("mega"),
ULTRA_BURST("ultra"),
Z_POWER("zmove"),
DYNAMAX("max"),
TERASTALLIZATION("terastal")
}
/** Check whether Dynamax/Gigantamax is already active. */
fun hasActiveGimmick() = !this.canDynamax && this.maxMoves != null
/** [Gimmick]s that can be activated with a [MoveActionResponse]. */
fun getGimmicks() = if (!hasActiveGimmick()) buildList{
if (canMegaEvo) add(Gimmick.MEGA_EVOLUTION)
if (canUltraBurst) add(Gimmick.ULTRA_BURST)
if (canZMove != null) add(Gimmick.Z_POWER)
if (canDynamax) add(Gimmick.DYNAMAX)
if (canTerastallize != null) add(Gimmick.TERASTALLIZATION)
}.toList() else listOf() // keep this immutable
/** Map each [InBattleMove] to their respective [InBattleGimmickMove] variant. */
fun setGimmickMapping() = (canZMove ?: maxMoves)?.let { gimmickMoves ->
moves.forEachIndexed { index, move -> move.gimmickMove = gimmickMoves[index] }
}
/** Sanitize Moveset by disabling gimmick. */
fun blockGimmick(gimmick: Gimmick) {
when (gimmick) {
Gimmick.MEGA_EVOLUTION -> this.canMegaEvo = false
Gimmick.DYNAMAX -> this.canDynamax = false
Gimmick.ULTRA_BURST -> this.canUltraBurst = false
Gimmick.Z_POWER -> this.canZMove = null
else -> this.canTerastallize = null
}
}
}
class ShowdownSide {
lateinit var name: UUID
@@ -417,30 +320,4 @@ class ShowdownPokemon {
ability = buffer.readString()
return this
}
}
/** Unwraps useless maxMoves object and initializes [ShowdownMoveset.gimmickMapping] */
object ShowdownMovesetAdapter : JsonDeserializer<ShowdownMoveset> {
val gson = GsonBuilder().addDeserializationExclusionStrategy(MovesetExclusionStrategy).create()
object MovesetExclusionStrategy : ExclusionStrategy {
override fun shouldSkipField(field: FieldAttributes?): Boolean {
return field?.name == "maxMoves"
}
override fun shouldSkipClass(p0: Class<*>?): Boolean {
return false;
}
}
override fun deserialize(jsonElement: JsonElement, type: Type, context: JsonDeserializationContext): ShowdownMoveset {
val json = jsonElement.asJsonObject
val moveset = gson.fromJson(json, ShowdownMoveset::class.java)
json.get("maxMoves")?.asJsonObject?.let { dynamaxOptions ->
val maxMovesToken = object: TypeToken<List<InBattleGimmickMove?>?>() {}.type
moveset.maxMoves = Gson().fromJson(dynamaxOptions.get("maxMoves"), maxMovesToken)
}
moveset.setGimmickMapping()
return moveset
}
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.battles
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.battles.interpreter.*
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
@@ -23,7 +22,6 @@ import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.scheduling.after
import com.cobblemon.mod.common.api.text.*
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.battles.dispatch.BattleDispatch
import com.cobblemon.mod.common.battles.dispatch.DispatchResult
import com.cobblemon.mod.common.battles.dispatch.GO
@@ -109,12 +107,6 @@ object ShowdownInterpreter {
updateInstructions["|-sidestart|"] = this::handleSideStartInstructions
updateInstructions["|-sideend|"] = this::handleSideEndInstructions
updateInstructions["|-fieldactivate|"] = this::handleFieldActivateInstructions
updateInstructions["|-clearnegativeboost|"] = this::handleClearNegativeBoostInstructions
updateInstructions["|-zpower|"] = this::handleZPowerInstructions
updateInstructions["|-zbroken|"] = this::handleZBrokenInstructions
updateInstructions["|-terastallize|"] = this::handleTerastallizeInstructions
updateInstructions["|detailschange|"] = this::handleDetailsChangeInstructions
updateInstructions["|-mega|"] = this::handleMegaInstructions
sideUpdateInstructions["|request|"] = this::handleRequestInstruction
splitUpdateInstructions["|switch|"] = this::handleSwitchInstruction
@@ -155,12 +147,7 @@ object ShowdownInterpreter {
}
battle.dispatchWaiting(1.5F) {
val lang = when {
message.hasOptionalArgument("zeffect") -> battleLang("$rootKey.$severity.zeffect", pokemon.getName(), stat)
else -> battleLang("$rootKey.$severity", pokemon.getName(), stat)
}
battle.broadcastChatMessage(lang)
battle.broadcastChatMessage(battleLang("$rootKey.$severity", pokemon.getName(), stat))
val boostBucket = if (isBoost) BattleContext.Type.BOOST else BattleContext.Type.UNBOOST
val context = getContextFromAction(message, boostBucket, battle)
// TODO: replace with context that tracks detailed information such as # of stages
@@ -658,9 +645,10 @@ object ShowdownInterpreter {
this.lastCauser[battle.battleId] = message
userPokemon.effectedPokemon.let { pokemon ->
if (UseMoveEvolutionProgress.supports(pokemon, move)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is UseMoveEvolutionProgress && it.currentProgress().move == move }) { UseMoveEvolutionProgress() }
progress.updateProgress(UseMoveEvolutionProgress.Progress(move, progress.currentProgress().amount + 1))
val progress = UseMoveEvolutionProgress()
if (progress.shouldKeep(pokemon)) {
val created = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is UseMoveEvolutionProgress && it.currentProgress().move == move }) { progress }
created.updateProgress(UseMoveEvolutionProgress.Progress(created.currentProgress().move, created.currentProgress().amount + 1))
}
}
@@ -690,7 +678,7 @@ object ShowdownInterpreter {
val effectID = message.effectAt(1)?.id ?: return@dispatchWaiting
val name = pokemon.getName()
// Move may be null as it's not always given
val moveName = message.argumentAt(2)?.let { Moves.getByName(it)?.displayName } ?: Text.EMPTY
val moveName = message.argumentAt(2)?.let { Moves.getByName(it.replace(" ",""))?.displayName } ?: "".text()
val lang = when (effectID) {
// TODO: in the games they use a generic image because there is a popup of the ability and the sprite of the mon, it may be good to have a similar system here
@@ -826,7 +814,7 @@ object ShowdownInterpreter {
private fun handleRechargeInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>){
battle.dispatchWaiting(2F){
val pokemon = message.getBattlePokemon(0, battle) ?: return@dispatchWaiting
battle.broadcastChatMessage(battleLang("recharge", pokemon.getName()))
battle.broadcastChatMessage(battleLang("recharge", pokemon.getName() ?: ""))
battle.minorBattleActions[pokemon.uuid] = message
}
}
@@ -887,7 +875,6 @@ object ShowdownInterpreter {
val lang = when (effectID) {
"confusion", "perish3" -> return@dispatch GO // Skip
"perish2", "perish1", "perish0" -> battleLang("start.perish", pokemon.getName(), effectID.last().digitToInt())
"dynamax" -> battleLang("start.${message.effectAt(2)?.id ?: effectID}", pokemon.getName()).yellow()
else -> battleLang("start.$effectID", pokemon.getName())
}
battle.broadcastChatMessage(lang)
@@ -962,11 +949,10 @@ object ShowdownInterpreter {
}
// Includes revealed move
"forewarn" -> {
val moveName = message.argumentAt(2)?.let { Moves.getByName(it)?.displayName } ?: Text.EMPTY
val moveName = message.argumentAt(2)?.let { Moves.getByName(it.replace(" ",""))?.displayName } ?: "".text()
battleLang("activate.forewarn", sourceName, moveName)
}
"focussash", "focusband" -> battleLang("activate.focusband", pokemonName, message.effectAt(1)!!.typelessData)
"maxguard", "protect" -> battleLang("activate.protect", pokemonName)
else -> battleLang("activate.${effect.id}", pokemonName, sourceName)
}
battle.broadcastChatMessage(lang)
@@ -1236,15 +1222,13 @@ object ShowdownInterpreter {
// Parse Json message and update state info for actor
val request = BattleRegistry.gson.fromJson(message.rawMessage.split("|request|")[1], ShowdownActionRequest::class.java)
request.sanitize(battle, battleActor)
if (battle.started) {
battle.dispatchGo {
// This request won't be acted on until the start of next turn
battleActor.sendUpdate(BattleQueueRequestPacket(request))
battleActor.request = request
battleActor.responses.clear()
// We need to send this out because 'upkeep' isn't received until the request is handled since the turn won't swap
if (request.forceSwitch.contains(true)) {
if (request.forceSwitch.withIndex().any { it.value && battleActor.activePokemon.getOrNull(it.index)?.isGone() == false }) {
battle.doWhenClear {
battleActor.mustChoose = true
battleActor.sendUpdate(BattleMakeChoicePacket())
@@ -1369,12 +1353,14 @@ object ShowdownInterpreter {
val battlePokemon = publicMessage.getBattlePokemon(0, battle) ?: return
if (privateMessage.optionalArgument("from")?.equals("recoil", true) == true) {
battlePokemon.effectedPokemon.let { pokemon ->
if (RecoilEvolutionProgress.supports(pokemon)) {
val recoilProgress = RecoilEvolutionProgress()
// Lazy cheat to see if it's necessary to use this
if (recoilProgress.shouldKeep(pokemon)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is RecoilEvolutionProgress }) { recoilProgress }
val newPercentage = privateMessage.argumentAt(1)?.split("/")?.getOrNull(0)?.toIntOrNull() ?: 0
val newHealth = (pokemon.hp * (newPercentage / 100.0)).roundToInt()
val difference = pokemon.currentHealth - newHealth
if (difference > 0) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is RecoilEvolutionProgress }) { RecoilEvolutionProgress() }
progress.updateProgress(RecoilEvolutionProgress.Progress(progress.currentProgress().recoil + difference))
}
}
@@ -1418,8 +1404,10 @@ object ShowdownInterpreter {
battlePokemon.effectedPokemon.currentHealth = remainingHealth
if (difference > 0) {
battlePokemon.effectedPokemon.let { pokemon ->
if (DamageTakenEvolutionProgress.supports(pokemon)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DamageTakenEvolutionProgress }) { DamageTakenEvolutionProgress() }
val damageProgress = DamageTakenEvolutionProgress()
// Lazy cheat to see if it's necessary to use this
if (damageProgress.shouldKeep(pokemon)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DamageTakenEvolutionProgress }) { damageProgress }
progress.updateProgress(DamageTakenEvolutionProgress.Progress(progress.currentProgress().amount + difference))
}
}
@@ -1444,8 +1432,8 @@ object ShowdownInterpreter {
* The switched Pokémon has HP HP, and status STATUS.
*/
fun handleDragInstruction(battle: PokemonBattle, actor: BattleActor, publicMessage: BattleMessage, privateMessage: BattleMessage) {
battle.dispatchInsert {
val (pnx, pokemonID) = publicMessage.pnxAndUuid(0)!!
battle.dispatchGo {
val (pnx, pokemonID) = publicMessage.pnxAndUuid(0) ?: return@dispatchGo
val (_, activePokemon) = battle.getActorAndActiveSlotFromPNX(pnx)
val pokemon = battle.getBattlePokemon(pnx, pokemonID)
@@ -1457,15 +1445,13 @@ object ShowdownInterpreter {
battle.majorBattleActions[pokemon.uuid] = publicMessage
val entity = if (actor is EntityBackedBattleActor<*>) actor.entity else null
setOf(
BattleDispatch {
if (entity != null) {
this.createEntitySwitch(battle, actor, entity, pnx, activePokemon, pokemon)
} else {
this.createNonEntitySwitch(battle, actor, pnx, activePokemon, pokemon)
}
battle.dispatch {
if (entity != null) {
this.createEntitySwitch(battle, actor, entity, pnx, activePokemon, pokemon)
} else {
this.createNonEntitySwitch(battle, actor, pnx, activePokemon, pokemon)
}
)
}
}
}
@@ -1527,9 +1513,6 @@ object ShowdownInterpreter {
battlePokemon.heldItemManager.handleEndInstruction(battlePokemon, battle, message)
battle.minorBattleActions[battlePokemon.uuid] = message
battlePokemon.contextManager.remove(item.id, BattleContext.Type.ITEM)
if (message.hasOptionalArgument("eat")) {
battlePokemon.entity?.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
}
}
}
@@ -1544,16 +1527,15 @@ object ShowdownInterpreter {
val battlePokemon = privateMessage.getBattlePokemon(0, battle) ?: return
val rawHpAndStatus = privateMessage.argumentAt(1)?.split(" ") ?: return
val rawHpRatio = rawHpAndStatus.getOrNull(0) ?: return
val newHealth = rawHpRatio.split("/").map { it.toFloatOrNull() ?: return }
val newHealthRatio = publicMessage.argumentAt(1)?.split("/")?.map { it.toFloatOrNull() ?: return } ?: return
val newHealth = rawHpRatio.split("/").getOrNull(0)?.toIntOrNull() ?: return
val newHealthRatio = publicMessage.argumentAt(1)?.split("/")?.getOrNull(0)?.toFloatOrNull()?.times(0.01F) ?: return
val effect = privateMessage.effect()
val pokemonName = battlePokemon.getName()
broadcastOptionalAbility(battle, effect, pokemonName)
battle.dispatchWaiting {
if (pnx != null) {
// dynamax changes max health
battle.sendSidedUpdate(actor, BattleHealthChangePacket(pnx, newHealth[0], newHealth[1]), BattleHealthChangePacket(pnx, newHealthRatio[0], newHealthRatio[1]))
battle.sendSidedUpdate(actor, BattleHealthChangePacket(pnx, newHealth.toFloat()), BattleHealthChangePacket(pnx, newHealthRatio))
}
val silent = privateMessage.hasOptionalArgument("silent")
if (!silent) {
@@ -1588,7 +1570,7 @@ object ShowdownInterpreter {
battle.broadcastChatMessage(lang)
}
battle.minorBattleActions[battlePokemon.uuid] = privateMessage
battlePokemon.effectedPokemon.currentHealth = newHealth[0].toInt()
battlePokemon.effectedPokemon.currentHealth = newHealth
// This part is not always present
val rawStatus = rawHpAndStatus.getOrNull(1) ?: return@dispatchWaiting
@@ -1631,7 +1613,7 @@ object ShowdownInterpreter {
/**
* Format:
* |-clearallboost|
* |-clearallboost
*
* Clears all boosts from all Pokémon on both sides.
*/
@@ -1644,105 +1626,6 @@ object ShowdownInterpreter {
}
}
/**
* Format:
* |-clearnegativeboost|POKEMON
*
* Clear the negative boosts from the target Pokémon POKEMON (usually as the result of a zeffect).
*/
private fun handleClearNegativeBoostInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
val pokemonName = battlePokemon.getName()
battle.dispatchWaiting(1.5F) {
val lang = when {
message.hasOptionalArgument("zeffect") -> battleLang("clearallnegativeboost.zeffect", pokemonName)
else -> battleLang("clearallnegativeboost", pokemonName)
}
battle.broadcastChatMessage(lang)
battlePokemon.contextManager.clear(BattleContext.Type.UNBOOST)
battle.minorBattleActions[battlePokemon.uuid] = message
}
}
/**
* Format:
* |-zpower|POKEMON
*
* The Pokémon POKEMON has used the z-move version of its move.
*/
private fun handleZPowerInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
val pokemonName = battlePokemon.getName()
battle.dispatchWaiting {
battle.broadcastChatMessage(battleLang("zpower", pokemonName).yellow())
battle.minorBattleActions[battlePokemon.uuid] = message
}
}
/**
* Format:
* |-zbroken|POKEMON
*
* A z-move has broken through protect and hit the Pokémon POKEMON.
*/
private fun handleZBrokenInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
val pokemonName = battlePokemon.getName()
battle.dispatchWaiting {
battle.broadcastChatMessage(battleLang("zbroken", pokemonName).red())
battle.minorBattleActions[battlePokemon.uuid] = message
}
}
/**
* Format:
* |-terastallize|POKEMON|TYPE
*
* The Pokémon POKEMON terastallized into type TYPE.
*/
private fun handleTerastallizeInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
val pokemonName = battlePokemon.getName()
val type = message.effectAt(1)?.let { ElementalTypes.get(it.id) } ?: return
battle.dispatchWaiting {
battle.broadcastChatMessage(battleLang("terastallize", pokemonName, type.displayName).yellow())
battle.minorBattleActions[battlePokemon.uuid] = message
}
}
/**
* Format:
* |detailschange|POKEMON|DETAILS|HP STATUS
*
* The specified Pokémon has changed formes (via Mega Evolution, ability, etc.). If the forme change is permanent,
* then detailschange will appear; otherwise, the client will send -formechange.
*/
private fun handleDetailsChangeInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
val pokemonName = battlePokemon.getName()
val formName = message.argumentAt(1)?.split(',')?.get(0)?.substringAfter('-')?.lowercase() ?: return
battle.dispatchWaiting {
battle.broadcastChatMessage(battleLang("detailschange.$formName", pokemonName))
battle.majorBattleActions[battlePokemon.uuid] = message
}
}
/**
* Format:
* |-mega|POKEMON|MEGASTONE
*
* The Pokémon POKEMON used MEGASTONE to Mega Evolve.
*/
private fun handleMegaInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
val pokemonName = battlePokemon.getName()
battle.dispatchWaiting {
battle.broadcastChatMessage(battleLang("mega", pokemonName).yellow())
battle.minorBattleActions[battlePokemon.uuid] = message
}
}
// Used for things that are only meant for visual information we don't have
private fun handleSilently(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
battle.dispatchGo { }
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.battles.pokemon
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.moves.MoveSet
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemManager
@@ -90,8 +89,4 @@ open class BattlePokemon(
fun isSentOut() = actor.battle.activePokemon.any { it.battlePokemon == this }
fun canBeSentOut() = !isSentOut() && !willBeSwitchedIn && health > 0
fun writeVariables(struct: VariableStruct) {
effectedPokemon.writeVariables(struct)
}
}
@@ -1,56 +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.berry
import com.cobblemon.mod.common.api.berry.GrowthFactor
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.block.BlockState
import net.minecraft.predicate.NumberRange
import net.minecraft.util.math.BlockPos
import net.minecraft.world.WorldView
import net.minecraft.world.biome.Biome
/**
* A [GrowthFactor] that is active based on the downfall property of a [Biome].
*
* @property range The range of possible [Biome.getDownfall] values.
* @property bonusYield The range of possible bonus values.
*
* @author Licious
* @since December 2nd, 2022
*/
class BiomeDownfallGrowthFactor(
val range: NumberRange.FloatRange,
val bonusYield: IntRange
) : GrowthFactor {
override fun validateArguments() {
if (this.bonusYield.first < 0 || this.bonusYield.last < 0) {
throw IllegalArgumentException("$ID bonusYield must be a positive range")
}
}
override fun isValid(world: WorldView, state: BlockState, pos: BlockPos): Boolean {
val biome = world.getBiome(pos).value()
return this.range.test(biome.weather.downfall.toDouble())
}
override fun yield() = this.bonusYield.random()
override fun minYield() = this.bonusYield.first
override fun maxYield() = this.bonusYield.last
companion object {
val ID = cobblemonResource("biome_downfall")
}
}
@@ -1,52 +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.berry
import com.cobblemon.mod.common.api.berry.GrowthFactor
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.registry.BiomeIdentifierCondition
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.block.BlockState
import net.minecraft.registry.RegistryKeys
import net.minecraft.registry.tag.TagKey
import net.minecraft.util.math.BlockPos
import net.minecraft.world.WorldView
import net.minecraft.world.biome.Biome
class PreferredBiomeGrowthFactor(
val bonusYield: IntRange
) : GrowthFactor{
override fun validateArguments() {
if (this.bonusYield.first < 0 || this.bonusYield.last < 0) {
throw IllegalArgumentException("$ID bonusYield must be a positive range")
}
}
override fun isValid(world: WorldView, state: BlockState, pos: BlockPos): Boolean {
val biome = world.getBiome(pos)
val block = state.block as BerryBlock
val biomeTags = block.berry()?.preferredBiomeTags ?: emptyList()
return biomeTags.any { biome.isIn(it) }
}
override fun yield() = this.bonusYield.random()
override fun minYield() = this.bonusYield.first
override fun maxYield() = this.bonusYield.last
companion object {
val ID = cobblemonResource("preferred_biome")
}
}
@@ -1,56 +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.berry
import com.cobblemon.mod.common.api.berry.GrowthFactor
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.block.BlockState
import net.minecraft.predicate.NumberRange
import net.minecraft.util.math.BlockPos
import net.minecraft.world.WorldView
import net.minecraft.world.biome.Biome
/**
* A [GrowthFactor] that is active based on the temperature property of a [Biome].
*
* @property range The range of possible [Biome.getTemperature] values.
* @property bonusYield The range of possible bonus values.
*
* @author Licious
* @since December 2nd, 2022
*/
class BiomeTemperatureGrowthFactor(
val range: NumberRange.FloatRange,
val bonusYield: IntRange
) : GrowthFactor {
override fun validateArguments() {
if (this.bonusYield.first < 0 || this.bonusYield.last < 0) {
throw IllegalArgumentException("$ID bonusYield must be a positive range")
}
}
override fun isValid(world: WorldView, state: BlockState, pos: BlockPos): Boolean {
val biome = world.getBiome(pos).value()
return this.range.test(biome.temperature.toDouble())
}
override fun yield() = this.bonusYield.random()
override fun minYield() = this.bonusYield.first
override fun maxYield() = this.bonusYield.last
companion object {
val ID = cobblemonResource("biome_temperature")
}
}
@@ -137,7 +137,7 @@ class ApricornBlock(settings: Settings, val apricorn: Apricorn) : HorizontalFaci
world.playSoundServer(position = pos.toVec3d(), sound = SoundEvents.ENTITY_ITEM_PICKUP, volume = 0.7F, pitch = 1.4F)
if (world is ServerWorld && player is ServerPlayerEntity) {
CobblemonEvents.APRICORN_HARVESTED.post(ApricornHarvestEvent(player, apricorn, world, pos))
CobblemonEvents.APRICORN_HARVESTED.post(ApricornHarvestEvent(player, world, pos))
}
}
return ActionResult.success(world.isClient)
@@ -1,303 +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
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.berry.Berries
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.berry.BerryMutationOfferEvent
import com.cobblemon.mod.common.api.events.berry.BerryMutationResultEvent
import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.api.mulch.Mulchable
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import net.minecraft.block.Block
import net.minecraft.block.BlockRenderType
import net.minecraft.block.BlockState
import net.minecraft.block.BlockWithEntity
import net.minecraft.block.Blocks
import net.minecraft.block.Fertilizable
import net.minecraft.block.ShapeContext
import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityTicker
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.item.ShovelItem
import net.minecraft.server.world.ServerWorld
import net.minecraft.sound.SoundCategory
import net.minecraft.state.StateManager
import net.minecraft.state.property.BooleanProperty
import net.minecraft.state.property.EnumProperty
import net.minecraft.state.property.IntProperty
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
import net.minecraft.util.math.Direction
import net.minecraft.util.math.random.Random
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.minecraft.world.WorldView
class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) : BlockWithEntity(settings), Fertilizable, Mulchable {
private val lookupDirections = setOf(Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH)
/**
* Returns the [Berry] behind this block,
* This will be null if it doesn't exist in the [Berries] registry.
*
* @return The [Berry] if existing.
*/
fun berry(): Berry? = Berries.getByIdentifier(this.berryIdentifier)
override fun createBlockEntity(pos: BlockPos, state: BlockState) = BerryBlockEntity(pos, state, berryIdentifier)
override fun isFertilizable(world: WorldView, pos: BlockPos, state: BlockState, isClient: Boolean) = !this.isMaxAge(state)
override fun canGrow(world: World, random: Random, pos: BlockPos, state: BlockState) = !this.isMaxAge(state)
override fun <T : BlockEntity> getTicker(world: World, blockState: BlockState, blockWithEntityType: BlockEntityType<T>): BlockEntityTicker<T>? = checkType(blockWithEntityType, CobblemonBlockEntities.BERRY, BerryBlockEntity.TICKER)
init {
defaultState = this.stateManager.defaultState
.with(WAS_GENERATED, false)
.with(AGE, 0)
.with(MULCH, MulchVariant.NONE)
}
override fun grow(world: ServerWorld, random: Random, pos: BlockPos, state: BlockState) {
val curAge = state.get(AGE)
val newAge = curAge + 1
if (newAge > FRUIT_AGE) return
val newState = state.with(AGE, newAge)
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
if (curAge == MATURE_AGE) {
determineMutation(world, random, pos, state)
treeEntity.generateGrowthPoints(world, state, pos, null)
}
world.setBlockState(pos, newState, Block.NOTIFY_LISTENERS)
treeEntity.goToNextStageTimer(FRUIT_AGE - curAge)
treeEntity.markDirty()
}
fun determineMutation(world: World, random: Random, pos: BlockPos, state: BlockState) {
val mutations = hashSetOf<Berry>()
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
for (direction in this.lookupDirections) {
val redirectedPos = pos.add(direction.vector)
val redirectedState = world.getBlockState(redirectedPos)
val berryBlock = redirectedState.block as? BerryBlock ?: continue
val berry = berryBlock.berry() ?: continue
val mutation = this.berry()?.mutationWith(berry) ?: continue
mutations += mutation
}
this.berry()?.let { berry ->
CobblemonEvents.BERRY_MUTATION_OFFER.post(BerryMutationOfferEvent(berry, world, state, pos, mutations)) { berryMutationOffer ->
if (berryMutationOffer.mutations.isNotEmpty()) {
var mutateChance = 125
if (getMulch(state) == MulchVariant.SURPRISE) {
mutateChance *= 4
treeEntity.decrementMulchDuration(world, pos, state)
}
val mutation = if (random.nextInt(1000) < mutateChance) mutations.random() else null
(world.getBlockEntity(pos) as? BerryBlockEntity)?.let { blockEntity ->
CobblemonEvents.BERRY_MUTATION_RESULT.post(BerryMutationResultEvent(berry, world, state, pos, berryMutationOffer.mutations, mutation)) { berryMutationResult ->
berryMutationResult.pickedMutation?.let { mutation -> blockEntity.mutate(mutation) }
}
}
}
}
}
}
override fun canHaveMulchApplied(
world: ServerWorld,
pos: BlockPos,
state: BlockState,
variant: MulchVariant
): Boolean {
return getMulch(state) == MulchVariant.NONE && state.get(AGE) < FLOWER_AGE && world.getBlockState(pos.down()).isOf(Blocks.FARMLAND)
}
override fun applyMulch(
world: ServerWorld,
random: Random,
pos: BlockPos,
state: BlockState,
variant: MulchVariant
) {
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
setMulch(world, pos, state, variant)
treeEntity.mulchDuration = variant.duration
world.playSound(null, pos, CobblemonSounds.MULCH_PLACE, SoundCategory.BLOCKS, 0.6F, 1F)
}
@Deprecated("Deprecated in Java")
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
if (player.getStackInHand(hand).item is ShovelItem && getMulch(state) != MulchVariant.NONE) {
setMulch(world, pos, state, MulchVariant.NONE)
treeEntity.markDirty()
world.playSound(null, pos, CobblemonSounds.MULCH_REMOVE, SoundCategory.BLOCKS, 0.6F, 1F)
this.spawnBreakParticles(world, player, pos, state.with(AGE, 0))
return ActionResult.SUCCESS
}
if (player.getStackInHand(hand).isOf(Items.BONE_MEAL) && !this.isMaxAge(state)) {
return ActionResult.PASS
} else if (this.isMaxAge(state)) {
val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return ActionResult.PASS
blockEntity.harvest(world, state, pos, player).forEach { drop ->
Block.dropStack(world, pos, drop)
}
world.playSound(null, pos, CobblemonSounds.BERRY_HARVEST, SoundCategory.BLOCKS, 0.4F, 1F)
return ActionResult.success(world.isClient)
}
return super.onUse(state, world, pos, player, hand, hit)
}
@Deprecated("Deprecated in Java")
override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean {
val below = world.getBlockState(pos.down())
return (state.get(WAS_GENERATED) && below.isIn(CobblemonBlockTags.BERRY_WILD_SOIL)) || below.isIn(CobblemonBlockTags.BERRY_SOIL)
}
@Deprecated("Deprecated in Java")
override fun getStateForNeighborUpdate(state: BlockState, direction: Direction, neighborState: BlockState, world: WorldAccess, pos: BlockPos, neighborPos: BlockPos): BlockState {
return if (state.canPlaceAt(world, pos)) super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos) else Blocks.AIR.defaultState
}
override fun onPlaced(world: World, pos: BlockPos, state: BlockState, placer: LivingEntity?, itemStack: ItemStack) {
// if (!world.isClient) {
// val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return
// blockEntity.generateGrowthPoints(world, state, pos, placer)
// }
}
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
builder.add(AGE)
builder.add(WAS_GENERATED)
builder.add(MULCH)
}
override fun getPickStack(world: BlockView?, pos: BlockPos?, state: BlockState?): ItemStack {
val berryItem = this.berry()?.item() ?: return ItemStack.EMPTY
return ItemStack(berryItem)
}
@Deprecated("Deprecated in Java")
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape {
val berry = this.berry() ?: return VoxelShapes.fullCube()
return when (state.get(AGE)) {
0 -> PLANTED_SHAPE
1 -> PLANTED_SHAPE
2 -> berry.sproutShape
else -> berry.matureShape
}
}
private fun isMaxAge(state: BlockState) = state.get(AGE) == FRUIT_AGE
@Deprecated("Deprecated in Java")
override fun getRenderType(blockState: BlockState) = BlockRenderType.MODEL
companion object {
const val MATURE_AGE = 3
const val FLOWER_AGE = 4
const val FRUIT_AGE = 5
val AGE: IntProperty = IntProperty.of("age", 0, FRUIT_AGE)
val MULCH: EnumProperty<MulchVariant> = EnumProperty.of("mulch", MulchVariant::class.java)
val WAS_GENERATED: BooleanProperty = BooleanProperty.of("generated")
// val PLANTED_SHAPE = VoxelShapes.union(
// VoxelShapes.cuboid(0.3125, -0.0625, 0.3125, 0.6875, 0.0, 0.6875),
// VoxelShapes.cuboid(0.375, 0.0, 0.375, 0.625, 0.0625, 0.625)
// )
val PLANTED_SHAPE = VoxelShapes.cuboid(0.0, -0.1, 0.0, 1.0, 0.25, 1.0)
val STANDARD_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val STANDARD_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 25.0, 16.0))
val SHORT_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 12.0, 16.0))
val SHORT_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val VOLCANO_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 6.0, 16.0))
val VOLCANO_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val NEST_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 6.0, 16.0))
val NEST_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 20.0, 16.0))
val FRILL_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 8.0, 16.0))
val FRILL_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 14.0, 16.0))
val BLOCK_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 17.0, 16.0))
val BLOCK_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
val PYRAMID_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 17.0, 16.0))
val PYRAMID_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
val TAIL_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val TAIL_MATURE = listOf(Box(0.0, 1.0, 0.0, 16.0, 24.0, 16.0))
val SWORD_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 5.0, 16.0))
val SWORD_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
val PLATFORM_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 7.0, 16.0))
val PLATFORM_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 22.0, 16.0))
val STAND_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 13.0, 16.0))
val STAND_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
val CONE_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val CONE_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 23.0, 16.0))
val SQUAT_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 12.0, 16.0))
val SQUAT_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 22.0, 16.0))
val LANTERN_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 20.0, 16.0))
val LANTERN_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
val BOX_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val BOX_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
val BLOSSOM_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 4.0, 16.0))
val BLOSSOM_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 6.0, 16.0))
val LILYPAD_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 11.0, 16.0))
val LILYPAD_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val TALL_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val TALL_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
fun getMulch(state: BlockState): MulchVariant {
if (!state.contains(MULCH)) {
return MulchVariant.NONE
}
return state.get(MULCH)
}
fun setMulch(world: World, pos: BlockPos, state: BlockState, mulch: MulchVariant) {
world.setBlockState(pos, state.with(MULCH, mulch))
}
}
}
@@ -25,6 +25,7 @@ import net.minecraft.world.World
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
class BigRootBlock(settings: Settings) : RootBlock(settings) {
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape = AABB
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
@@ -50,7 +50,7 @@ class MedicinalLeekBlock(settings: Settings) : CropBlock(settings) {
// These 3 are still around for the sake of compatibility, vanilla won't trigger it but some mods might
// We implement applyGrowth & getGrowthAmount for them
override fun isFertilizable(world: WorldView?, pos: BlockPos?, state: BlockState?, isClient: Boolean): Boolean = !this.isMature(state)
override fun isFertilizable(world: WorldView?, pos: BlockPos?, state: BlockState?, isClient: Boolean): Boolean = false
override fun applyGrowth(world: World, pos: BlockPos, state: BlockState) {
world.setBlockState(pos, state.with(this.ageProperty, (this.getAge(state) + 1).coerceAtMost(this.maxAge)), NOTIFY_LISTENERS)
@@ -260,7 +260,7 @@ class PCBlock(properties: Settings): BlockWithEntity(properties), Waterloggable
world.playSoundServer(
position = blockPos.toVec3d(),
sound = CobblemonSounds.PC_ON,
volume = 0.5F,
volume = 1F,
pitch = 1F
)
return ActionResult.SUCCESS
@@ -21,48 +21,39 @@ import com.cobblemon.mod.common.util.isInBattle
import com.cobblemon.mod.common.util.playSoundServer
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.voxelShape
import java.util.UUID
import net.minecraft.block.Block
import net.minecraft.block.BlockRenderType
import net.minecraft.block.BlockState
import net.minecraft.block.BlockWithEntity
import net.minecraft.block.Blocks
import net.minecraft.block.HorizontalFacingBlock
import net.minecraft.block.ShapeContext
import net.minecraft.block.Waterloggable
import net.minecraft.block.*
import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityTicker
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.ai.pathing.NavigationType
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.fluid.FluidState
import net.minecraft.fluid.Fluids
import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.state.StateManager
import net.minecraft.state.property.BooleanProperty
import net.minecraft.state.property.EnumProperty
import net.minecraft.util.ActionResult
import net.minecraft.util.BlockMirror
import net.minecraft.util.BlockRotation
import net.minecraft.util.Hand
import net.minecraft.util.StringIdentifiable
import net.minecraft.util.*
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.minecraft.world.WorldView
import net.minecraft.world.explosion.Explosion
import java.util.*
import net.minecraft.entity.LivingEntity
import net.minecraft.fluid.FluidState
import net.minecraft.fluid.Fluids
import net.minecraft.item.ItemStack
import net.minecraft.server.world.ServerWorld
import net.minecraft.state.property.BooleanProperty
import net.minecraft.state.property.EnumProperty
import net.minecraft.util.BlockMirror
import net.minecraft.util.BlockRotation
import net.minecraft.util.StringIdentifiable
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterloggable, PreEmptsExplosion {
class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterloggable {
companion object {
val PART: EnumProperty<PasturePart> = EnumProperty.of("part", PasturePart::class.java)
val ON: BooleanProperty = BooleanProperty.of("on")
@@ -154,7 +145,7 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
}
fun getPositionOfOtherPart(state: BlockState, pos: BlockPos): BlockPos {
return if (state.contains(PART) && state.get(PART) == PasturePart.BOTTOM) {
return if (state.get(PART) == PasturePart.BOTTOM) {
pos.up()
} else {
pos.down()
@@ -169,7 +160,7 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
}
}
private fun isBase(state: BlockState): Boolean = state.contains(PART) && state.get(PART) == PasturePart.BOTTOM
private fun isBase(state: BlockState): Boolean = state.get(PART) == PasturePart.BOTTOM
override fun canPathfindThrough(blockState: BlockState, blockGetter: BlockView, blockPos: BlockPos, pathComputationType: NavigationType) = false
@@ -195,11 +186,6 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
blockEntity.onBroken()
}
override fun whenExploded(world: World?, state: BlockState, pos: BlockPos?) {
val blockEntity = world?.getBlockEntity(pos) as? PokemonPastureBlockEntity ?: return
blockEntity.onBroken()
}
override fun <T : BlockEntity?> getTicker(world: World, state: BlockState, type: BlockEntityType<T>): BlockEntityTicker<T>? {
return checkType(type, CobblemonBlockEntities.PASTURE, PokemonPastureBlockEntity.TICKER::tick)
}
@@ -260,7 +246,7 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
world.playSoundServer(
position = pos.toVec3d(),
sound = CobblemonSounds.PC_ON,
volume = 0.5F,
volume = 1F,
pitch = 1F
)
@@ -1,17 +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
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
interface PreEmptsExplosion {
fun whenExploded(world: World?, state: BlockState, pos: BlockPos?)
}
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.api.mulch.Mulchable
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.Blocks
import net.minecraft.block.CropBlock
import net.minecraft.block.ShapeContext
import net.minecraft.item.ItemConvertible
@@ -51,9 +50,8 @@ class RevivalHerbBlock(settings: Settings) : CropBlock(settings), Mulchable {
override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean {
val floor = world.getBlockState(pos.down())
val block = world.getBlockState(pos)
// A bit of a copy pasta but we don't have access to the BlockState being attempted to be placed above on the canPlantOnTop
return (block.isIn(BlockTags.REPLACEABLE_BY_TREES) || block.isOf(Blocks.AIR) || block.isOf(this)) && ((state.get(IS_WILD) && floor.isIn(BlockTags.DIRT)) || this.canPlantOnTop(floor, world, pos))
return (world.getBaseLightLevel(pos, 0) >= 8 || world.isSkyVisible(pos)) && ((state.get(IS_WILD) && floor.isIn(BlockTags.DIRT)) || this.canPlantOnTop(floor, world, pos))
}
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape = AGE_SHAPES.getOrNull(this.getAge(state)) ?: VoxelShapes.fullCube()
@@ -65,7 +63,7 @@ class RevivalHerbBlock(settings: Settings) : CropBlock(settings), Mulchable {
override fun applyMulch(world: ServerWorld, random: Random, pos: BlockPos, state: BlockState, variant: MulchVariant) {
val picked = Mutation.values().filterNot { it == Mutation.NONE }.random()
world.setBlockState(pos, state.with(MUTATION, picked))//.with(AGE, MUTABLE_MAX_AGE + 1))
world.setBlockState(pos, state.with(MUTATION, picked).with(AGE, MUTABLE_MAX_AGE + 1))
}
/**
@@ -135,7 +133,6 @@ class RevivalHerbBlock(settings: Settings) : CropBlock(settings), Mulchable {
val AGE = IntProperty.of("age", MIN_AGE, MAX_AGE)
val IS_WILD = BooleanProperty.of("is_wild")
val MUTATION = EnumProperty.of("mutation", Mutation::class.java)
// val MULCH = EnumProperty.of("mulch", MulchVariant::class.java)
/**
* The [VoxelShape] equivalent to a certain age.
Loaded 100 of 4256 files, more files were not shown because too many files have changed in this diff. Show more