Compare commits

..
1063 changed files with 3525 additions and 18877 deletions

No files matched your search

+1 -2
View File
@@ -8,8 +8,7 @@
- Lure Cakes can be used to modify the spawn rate of Pokémon in the wild.
- Added the Saccharine Tree, which can be found in the wild and is used to obtain Honey Bottles.
- The leaves of the tree can be harvested to obtain honey, which can be used in the Campfire Pot to make Lure Cakes.
- The Log of the tree can be interacted with using a honey bottle to create a Honey-Slathered Saccharine Log, which increases the change of spawning a Pokémon with a Hidden Ability.
- Added Hearty Grains
- The Log of the tree can be clicked on with honey to create a Honey Log, which increases the change of spawning a Pokemon with an Hidden Ability.
- Clicking on the Progress Arrow will show the JEI Recipe Book for the Campfire Pot, which will show all recipes that can be made with it.
- Added a Ton of Redstone Compatibility.
- Dispenser can now Honey and Unhoney:
+1 -31
View File
@@ -18,7 +18,6 @@
- Added cosmetics for Gurdurr, Conkeldurr, Squirtle Line, Sneasler, Sandle line, Treecko line, Braixen, Delphox, and Dragonite.
- Added `/transformmodelpart (position|rotation|scale) <modelPart> <transform: x y z>` command that can add transformations to a pokemon's model part.
- The player executing the command must be facing the target pokemon entity. Transformations are not persistent and will revert when resources are reloaded.
- Added `legacy` and `special` sourced moves to Pokémon.
- Added lang keys for all moves and abilities up to Generation 9.
- Added `translucent_cull` boolean option into resolver's layer to allow for translucent textures with culling
- Added [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) support for items held by Pokémon, evolution stone blocks, evolution stone items, Pokédex, Luminous Moss, Flame Orb, and Magmarizer.
@@ -33,14 +32,8 @@
- Added Hyper Training items (IV Modification) as well as some additional candy items to do so (Health Candy, Sickly Candy)
- Added Galarica Nut Bushes
- Many Pokémon (mostly cats) are now feared by phantoms
- Lightning is now affected by a Pokemon's ability/typing
- Pokémon with the ability Lightning Rod draw in lightning similar to a lightning rod block albeit with a lower priority and range, gain immunity to lightning damage, and receive a temporary damage buff.
- Pokémon with the ability Motor Drive are immune to lightning damage and receive a temporary speed buff when struck by lightning
- Pokémon with the ability Volt Absorb are immune to lightning damage and receive Regeneration II for a short duration
- Ground type Pokémon are immune to lightning damage
- Added functionality to Everstone when held by a Pokémon; suppresses evolution notification and hides evolve button in summary interface.
- Added new optional property `attachment_options` for most EmitterShapes to be attached to the locator/entities scale, rotation, and/or position. Position is true by default.
- Galarica Nut bushes now generate on beaches
### Pokémon Added
@@ -238,7 +231,6 @@
- Healing Machine recipe rebalanced.
- Reorganised the `block` texture folder to be more organised, in line with the `item` texture folder.
- Offset in EmitterShape now ignores scale to be more like Blockbench by default. You can get this behaviour back by adding `"scale": true` in the `attachment_options` property in most EmitterShapes.
- Not specifying a dex in `/pokedex printcalculations {player} {dex}` will now print the National Dex statistics instead of showing all dexes. `/pokedex printcalculations {player} all` is how to view all dex statistics in one command.
### Fixes
- Fixed game crashing when removing national pokedex using datapacks
@@ -268,7 +260,6 @@
- Fixed Pokémon marked as silent still playing shiny sounds and effects.
- Fixed an issue with newer versions of Fabric API where underground Pokémon were spawning in The End.
- Fixed spawning not working well when you're at high points surrounded by lower altitude spawning areas, such as flying.
- Fixed some Pokémon having erroneous tutor moves if another move included a valid tutor move as a substring.
- Fixed certain Pokémon with forms not having appropriate stock Pokédex entries.
- Fixed issue with Pokédex Scanner that caused the open/close overlay to have the wrong opacity values
- Fixed dragon's breath not being usable on the restoration tank when it should be
@@ -304,8 +295,6 @@
- Fixed Pokédex Scanner not respecting the "Invert Mouse" option.
- Fixed a crash due to a ConcurrentModificationException that could occur during world generation.
- Fixed Moon Ball moon phase logic to actually work correctly
- Fixed `/pokedex printcalculations` to now show the correct percentage completed of the Pokedex
- Fixed mod incompatibility with the `Raised` mod
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
@@ -324,11 +313,6 @@
- Updated NPCEntity pokeball throw positioning to properly account for the baseScale property.
- Fixed `[Pokemon].copyFrom` error causing forms, IVs, and EVs to not be applied properly when using `[Pokemon].loadFromJSON` or `[Pokemon].loadFromNBT`
- Added new item class, `WearableItem`. Instances of this class should have a corresponding 3D model. These models render when the items display context is `HEAD`.
- Added new LearnsetQuery types:
- `LEGAL` for moves that are innately compatible and learnable by the Pokémon.
- `LEGACY` for moves that were once officially learnable by the Pokémon but aren't due to GameFreak's re-balancing.
- `SPECIAL` for moves that are not learnable by the Pokémon but may have appeared in a special event or distribution.
- Pokemon now have a fireImmune attribute in their behaviour that can be set to true to ignore all fire damage (lava, magma blocks, etc.)
`JSON
{
@@ -340,7 +324,6 @@
- The IVs class has now been extended to include Hyper Trained values.
- Added `Pokemon#hyperTrainIV()` and `IVs#setHyperTrainedIV(Stat, Int)`.
- Added `HyperTrainedIvEvent.Pre` and `HyperTrainedIvEvent.Post`.
- Added `Pokemon#validateMoveSet()` to validate an existing Pokemon's moveset, clearing illegal moves.
- Added a `hoverText` option to PartySelectCallback, to display a tooltip on hovering over a Pokémon in the selection screen.
- `PokemonEntity` instances spawned into the world now appropriately finalize the spawn for mod compatibility.
- Added PokedexManager.obtain as a replacement for .catch which is not a friendly function name in Java.
@@ -365,9 +348,6 @@
- Added `pokemon` as an available Molang function for the `battleActor` functions.
- Fixed `heldItem` property inside spawn files not working and causing crashes
- Fixed `spawn_bedrock_particles` MoLang causing crashes when used in a server environment
- The following move sources are now valid for the `moves` array in species data:
- `legacy:{move}`
- `special:{move}`
- The Pokédex form lang key definition now follows `cobblemon.ui.pokedex.info.form.{species}-{formname}` instead of `cobblemon.ui.pokedex.info.form.{formname}`.
- Added `play_sound_on_server` as an available Molang function for the `worldHolder` & `player` functions.
- Added `run_molang_after` as an available Molang function for the `entity` functions when schedulable.
@@ -377,18 +357,8 @@
- The format of the `remedies.json` file has changed to allow for individual friendshipDrop amounts per remedy
- Fixed `entity.find_nearby_block` causing crashes when attempting to use a block tag
- Spawn Filters can now access `v.spawn.class` to get the identifier of an NPC class for when trying to influence NPC spawns
- Added Molang functions for Party and PC: `set_pokemon`
- Added Molang functions for Pokémon: `pokeball`, `held_item`, `remove_held_item`, `hyper_train_iv`, `validate_moveset`, `initialize_moveset`, and `add_exp`.
- Added Molang functions for Pokémon: `aspects`, `form_aspects`, `unlearn_move`, `teach_learnable_moves`, `cosmetic_item`, and `remove_cosmetic_item`.
- Added Molang functions for Pokémon: `ability`, `set_iv`, `set_ev`, `teach_move`, and `can_learn_move`.
- Adds Flows for `STARTER_CHOSEN`, `SHOULDER_MOUNTED`, `EV_GAINED`, `POKEMON_RELEASED`, `POKEMON_NICKNAMED`, `HELD_ITEM`, and `TRADE_COMPLETED` events
- Adds Flows for `POKEMON_HEALED`, `POKEMON_SCANNED`, `BERRY_HARVEST`, `LOOT_DROPPED`, `POKEMON_SEEN`, `COLLECT_EGG`, `HATCH_EGG`, and `EXPERIENCE_GAINED`.
- Adds Flows for `POKEMON_CATCH_RATE`, `BAIT_SET`, `BAIT_SET_PRE`, `BAIT_CONSUMED`, `POKEROD_CAST_PRE`, `POKEROD_CAST_POST`, `POKEROD_REEL`, and `BOBBER_SPAWN_POKEMON_PRE`.
- Adds Flows for `POKEMON_ASPECTS_CHANGED`, `FRIENDSHIP_UPDATED`, `CHANGE_PC_BOX_WALLPAPER_EVENT_PRE`, `CHANGE_PC_BOX_WALLPAPER_EVENT_POST`, and `FULLNESS_UPDATED`.
- MoLang triggered battles may now set the battle format, whether to clone the player's party, set level, or heal prior.
- Added Molang function for Player: `inventory`
- Adds Flows for `STARTER_CHOSEN`, `EV_GAINED`, `POKEMON_RELEASED`, `POKEMON_NICKNAMED`, `HELD_ITEM`, and `TRADE_COMPLETED` events
- Adds Pokemon functions for `pokeball`, `held_item`, `remove_held_item`, `add_aspects`, and `remove_aspects`
- Adds Pokemon functions for `pokeball`, `held_item` and `remove_held_item`
- Added `pokemon.hyper_train_iv` as an available Molang function.
## [1.6.1 (January 26th, 2025)](#1-6-1)
@@ -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.entity.pokemon.PokemonEntity;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LightningBolt;
import net.minecraft.world.phys.AABB;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
@Mixin(LightningBolt.class)
public abstract class LightningBoltMixin {
@Inject(method = "spawnFire", at = @At(value = "Head"), cancellable = true)
private void cobblemon$spawnFire(int extraIgnitions, CallbackInfo ci) {
// This prevents fire from spawning around Pokemon who otherwise should be immune to lightning and effectively absorb the hit
final LightningBolt lightningBolt = (LightningBolt) (Object) this;
List<Entity> list = lightningBolt.level().getEntities(
lightningBolt,
new AABB(lightningBolt.getX() - 2.0, lightningBolt.getY() - 2.0, lightningBolt.getZ() - 2.0, lightningBolt.getX() + 2.0, lightningBolt.getY() + 2.0, lightningBolt.getZ() + 2.0),
it -> {
if (!it.isAlive() || !(it instanceof PokemonEntity)) {
return false;
}
String abilityName = ((PokemonEntity) it).getPokemon().getAbility().getName();
return abilityName.equals("lightningrod") || abilityName.equals("voltabsorb") || abilityName.equals("motordrive");
});
if (!list.isEmpty()) {
ci.cancel();
}
}
}
@@ -9,13 +9,21 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.CobblemonItems;
import net.minecraft.world.entity.monster.piglin.PiglinAi;
import com.cobblemon.mod.common.CobblemonMemories;
import com.cobblemon.mod.common.entity.ai.PiglinDanceTarget;
import com.google.common.collect.ImmutableList;
import net.minecraft.world.entity.ai.Brain;
import net.minecraft.world.entity.monster.piglin.*;
import net.minecraft.world.entity.schedule.Activity;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.*;
@Mixin(PiglinAi.class)
public class PiglinBarterMixin {
@@ -29,5 +37,31 @@ public class PiglinBarterMixin {
}
}
@Inject(method = "makeBrain", at = @At("TAIL"), cancellable = true)
private static void cobblemon$makeBrain(Piglin piglin, Brain<Piglin> brain, CallbackInfoReturnable<Brain<?>> cir) {
// Get access to private task map
brain.addActivityAndRemoveMemoryWhenStopped(Activity.INVESTIGATE, 10,
ImmutableList.of(
new PiglinDanceTarget()
), CobblemonMemories.INSTANCE.getNEAREST_LOVED_ENTITY());
}
@Redirect(
method = "updateActivity(Lnet/minecraft/world/entity/monster/piglin/Piglin;)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/ai/Brain;setActiveActivityToFirstValid(Ljava/util/List;)V"
)
)
private static void redirectSetActiveActivityToFirstValid(
Brain<Piglin> brain,
List<Activity> originalList
) {
// Add new activity to the front
List<Activity> newList = new ArrayList<>(List.of(Activity.INVESTIGATE));
newList.addAll(originalList);
// Call original method with modified list
brain.setActiveActivityToFirstValid(newList);
}
}
@@ -9,10 +9,19 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.CobblemonItems;
import com.cobblemon.mod.common.CobblemonMemories;
import com.cobblemon.mod.common.CobblemonSensors;
import com.google.common.collect.ImmutableList;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import net.minecraft.world.entity.ai.sensing.Sensor;
import net.minecraft.world.entity.ai.sensing.SensorType;
import net.minecraft.world.entity.monster.piglin.Piglin;
import net.minecraft.world.item.ItemStack;
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.callback.CallbackInfo;
@@ -28,4 +37,27 @@ public abstract class PiglinEntityMixin {
ci.cancel();
}
}
@Shadow
@Final
@Mutable
private static ImmutableList<SensorType<? extends Sensor<? super Piglin>>> SENSOR_TYPES;
@Shadow
@Final
@Mutable
private static ImmutableList<MemoryModuleType<?>> MEMORY_TYPES;
@Inject(method = "<clinit>", at = @At("TAIL"))
private static void cobblemon$onInit(CallbackInfo ci) {
SENSOR_TYPES = ImmutableList.<SensorType<? extends Sensor<? super Piglin>>>builder()
.addAll(SENSOR_TYPES)
.add(CobblemonSensors.INSTANCE.getNEARBY_PIGLIN_LOVED_POKEMON())
.build();
MEMORY_TYPES = ImmutableList.<MemoryModuleType<?>>builder()
.addAll(MEMORY_TYPES)
.add(CobblemonMemories.INSTANCE.getNEAREST_LOVED_ENTITY())
.build();
}
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.Brain;
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities;
import net.minecraft.world.entity.ai.sensing.PiglinSpecificSensor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Optional;
@Mixin(PiglinSpecificSensor.class)
public abstract class PiglinSpecificSensorMixin {
@Inject(method = "doTick", at = @At("TAIL"))
protected void cobblemon$doTick(ServerLevel level, LivingEntity entity, CallbackInfo ci) {
Brain<?> brain = entity.getBrain();
if (!brain.hasMemoryValue(MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED)) {
NearestVisibleLivingEntities nearestVisibleLivingEntities = brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES).orElse(NearestVisibleLivingEntities.empty());
Optional<LivingEntity> fearedEntity = Optional.empty();
for (LivingEntity livingEntity : nearestVisibleLivingEntities.findAll((x) -> true)) {
if (fearedEntity.isEmpty() && livingEntity instanceof PokemonEntity && ((PokemonEntity) livingEntity).getPokemon().getSpecies().getBehaviour().getEntityInteract().getAvoidedByPiglin()) {
fearedEntity = Optional.of(livingEntity);
brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED, livingEntity);
}
}
}
}
}
@@ -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.entity.pokemon.PokemonEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.AABB;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.List;
import java.util.Optional;
@Mixin(ServerLevel.class)
public abstract class ServerLevelMixin {
@Inject(method = "findLightningRod", at = @At(value = "RETURN"), cancellable = true)
private void cobblemon$findLightningRod(BlockPos blockPos, CallbackInfoReturnable<Optional<BlockPos>> ci) {
final ServerLevel serverLevel = (ServerLevel) (Object) this;
if (ci.getReturnValue().isEmpty()) {
// Search for Pokemon with the ability Lightning Rod and redirect to them
AABB aABB = AABB.encapsulatingFullBlocks(blockPos, new BlockPos(blockPos.atY(serverLevel.getMaxBuildHeight()))).inflate(64.0);
List<LivingEntity> list = serverLevel.getEntitiesOfClass(LivingEntity.class, aABB, (livingEntity) -> livingEntity != null
&& livingEntity.isAlive()
&& livingEntity instanceof PokemonEntity
&& ((PokemonEntity) livingEntity).getBeamMode() == 0
&& ((PokemonEntity) livingEntity).getPokemon().getAbility().getName().equals("lightningrod"));
if (!list.isEmpty()) {
ci.setReturnValue(Optional.of((list.get(serverLevel.random.nextInt(list.size()))).blockPosition()));
}
}
}
}
@@ -1,50 +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.HeartyGrainsBlock;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.DoublePlantBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.SimpleBlockFeature;
import net.minecraft.world.level.levelgen.feature.configurations.SimpleBlockConfiguration;
import org.spongepowered.asm.mixin.Debug;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(SimpleBlockFeature.class)
@Debug(export = true, print = true)
public class SimpleBlockFeatureMixin {
//this mixin inserts the logic needed to ensure that when hearty grains are of a certain age, they place with the upper block too
//similarly to what the doubleblock logic is here
@Inject(method = "place", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;canSurvive(Lnet/minecraft/world/level/LevelReader;Lnet/minecraft/core/BlockPos;)Z", shift = At.Shift.AFTER), cancellable = true)
public void place(FeaturePlaceContext<SimpleBlockConfiguration> context, CallbackInfoReturnable<Boolean> cir,
@Local BlockState blockState, @Local BlockPos blockPos, @Local WorldGenLevel worldGenLevel) {
if (!(blockState.getBlock() instanceof HeartyGrainsBlock)) {
return;
}
if (blockState.getValue(HeartyGrainsBlock.Companion.getAGE()) <= HeartyGrainsBlock.AGE_AFTER_HARVEST) {
return; //block is too young to be double
}
if (!worldGenLevel.isEmptyBlock(blockPos.above())) {
cir.setReturnValue(false); //no space for the 2nd block above
return;
}
DoublePlantBlock.placeAt(worldGenLevel, blockState, blockPos, Block.UPDATE_CLIENTS);
cir.setReturnValue(true);
}
}
@@ -9,21 +9,22 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.gui.ScrollingWidget;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.client.gui.components.AbstractSelectionList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
@Mixin(AbstractSelectionList.class)
public abstract class EntryListWidgetMixin {
@SuppressWarnings("ConstantValue")
@ModifyExpressionValue(method = "getEntryAtPosition", at = @At(value = "CONSTANT", args = {"intValue=4"}))
public int cobblemon$adjustOnlyIfNecessary(int original) {
@ModifyConstant(method = "getEntryAtPosition", constant = @Constant(intValue = 4))
public int cobblemon$adjustOnlyIfNecessary(int input) {
if(ScrollingWidget.class.isInstance(this)) {
return 0;
}
return original;
return input;
}
}
@@ -19,7 +19,6 @@ object CobblemonActivities {
val POKEMON_GROW_CROP = register(Activity("pokemon_grow_crop"))
val POKEMON_SLEEP_ON_TRAINER_BED = register(Activity("pokemon_sleep_on_trainer_bed"))
val POKEMON_HERD = register(Activity("pokemon_herd"))
val POKEMON_POLLINATION = register(Activity("pokemon_pollination"))
fun register(activity: Activity): Activity {
activities.add(activity)
@@ -78,9 +78,6 @@ object CobblemonBehaviours : JsonDataRegistry<CobblemonBehaviour> {
*/
val autoPokemonBehaviours = mutableListOf<CobblemonBehaviour>()
/** See [autoPokemonBehaviours] but think npc instead of pokemon*/
val autoNPCBehaviours = mutableListOf<CobblemonBehaviour>()
val behaviours = mutableMapOf<ResourceLocation, CobblemonBehaviour>()
override fun sync(player: ServerPlayer) {
@@ -97,8 +94,6 @@ object CobblemonBehaviours : JsonDataRegistry<CobblemonBehaviour> {
val behaviour = parse(reader, resolvedIdentifier)
if ("pokemon/auto/" in identifier.path) {
autoPokemonBehaviours.add(behaviour)
} else if ("npc/auto/" in identifier.path) {
autoNPCBehaviours.add(behaviour)
}
data[resolvedIdentifier] = behaviour
}
@@ -93,8 +93,6 @@ object CobblemonFlows : DataRegistry {
functions: Map<String, (MoParams) -> Any> = emptyMap(),
cancelable: Cancelable? = null
) {
if (eventResourceLocation !in flows) return
if (cancelable == null) {
runtime.environment.query.functions.remove("cancel")
} else {
@@ -437,27 +437,28 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
.food(FoodProperties.Builder()
.nutrition(10) // todo get final values for this from vera
.saturationModifier(1.2F) // todo get final values for this from vera
.alwaysEdible()
.build())))
@JvmField
val FRIED_RICE = create("fried_rice", CobblemonItem(Properties().stacksTo(16)
.food(FoodProperties.Builder()
.nutrition(10) // todo get final values for this from vera
.saturationModifier(1.2F) // todo get final values for this from vera
.usingConvertsTo(Items.BOWL)
.alwaysEdible()
.build())))
@JvmField
val CANDIED_APPLE = create("candied_apple", CobblemonItem(Properties().stacksTo(16)
.food(FoodProperties.Builder()
.nutrition(10) // todo get final values for this from vera
.saturationModifier(1.2F) // todo get final values for this from vera
.usingConvertsTo(Items.STICK)
.alwaysEdible()
.build())))
@JvmField
val CANDIED_BERRY = create("candied_berry", CobblemonItem(Properties().stacksTo(16)
.food(FoodProperties.Builder()
.nutrition(10) // todo get final values for this from vera
.saturationModifier(1.2F) // todo get final values for this from vera
.usingConvertsTo(Items.STICK)
.alwaysEdible()
.build())))
//@JvmField
@@ -9,9 +9,9 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.ai.CobblemonAttackTargetData
import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
import com.cobblemon.mod.common.api.dialogue.ActiveDialogue
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.ListCodec
import com.mojang.serialization.codecs.PrimitiveCodec
@@ -44,16 +44,12 @@ object CobblemonMemories {
val NEAREST_VISIBLE_ATTACKER = register<LivingEntity>("nearest_visible_attacker")
val NEARBY_GROWABLE_CROPS = register<BlockPos>("nearby_growable_crops")
val RECENTLY_ATE_GRASS = register<Boolean>("recently_ate_grass")
val HIVE_LOCATION = register<BlockPos>("hive_location")
val HIVE_COOLDOWN = register<Boolean>("hive_cooldown")
val NEARBY_FLOWERS = register<List<BlockPos>>("nearby_flower")
val POLLINATED = register<Boolean>("pollinated", PrimitiveCodec.BOOL)
/** who am i following rn? */
val HERD_LEADER = register<String>("herd_leader", PrimitiveCodec.STRING)
/** how many are following me rn? */
val HERD_SIZE = register<Int>("herd_size") // Don't bother saving it, we'll try to keep count roughly.
val ATTACK_TARGET_DATA = register<CobblemonAttackTargetData>("attack_target_data") // This is used to store additional information about the attack target, such as when to give up pursuit.
val WANDER_CONTROL = register<CobblemonWanderControl>("wander_control")
val NEAREST_LOVED_ENTITY = register<PokemonEntity>("nearst_loved_entity")
fun <U> register(id: String, codec: Codec<U>): MemoryModuleType<U> {
val memoryModule = MemoryModuleType(Optional.of(codec))
@@ -24,7 +24,6 @@ import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
import com.cobblemon.mod.common.client.net.effect.SaccharineLogBlockParticlesHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
@@ -78,7 +77,6 @@ import com.cobblemon.mod.common.net.messages.client.data.*
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.net.messages.client.effect.SaccharineLogBlockParticlesPacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import com.cobblemon.mod.common.net.messages.client.npc.CloseNPCEditorPacket
@@ -404,7 +402,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
list.add(PacketRegisterInfo(SpawnSnowstormEntityParticlePacket.ID, SpawnSnowstormEntityParticlePacket::decode, SpawnSnowstormEntityParticleHandler))
list.add(PacketRegisterInfo(RunPosableMoLangPacket.ID, RunPosableMoLangPacket::decode, RunPosableMoLangHandler))
list.add(PacketRegisterInfo(SaccharineLogBlockParticlesPacket.ID, SaccharineLogBlockParticlesPacket::decode, SaccharineLogBlockParticlesHandler))
// Hax
list.add(PacketRegisterInfo(UnvalidatedPlaySoundS2CPacket.ID, UnvalidatedPlaySoundS2CPacket::decode, UnvalidatedPlaySoundS2CPacketHandler))
@@ -8,7 +8,11 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.*
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.DefendOwnerSensor
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.DrowsySensor
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonAdultSensor
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonGrowableCropSensor
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonPiglinLovedSensor
import com.cobblemon.mod.common.pokemon.ai.PokemonDisturbancesSensor
import com.cobblemon.mod.common.entity.sensor.BattlingPokemonSensor
import com.cobblemon.mod.common.entity.sensor.NPCBattlingSensor
@@ -33,9 +37,7 @@ object CobblemonSensors {
val NEARBY_GROWABLE_CROPS = register("nearby_growable_crops", ::PokemonGrowableCropSensor)
val NEARBY_BEE_HIVE = register("nearby_bee_hive", ::BeeHiveSensor)
val NEARBY_FLOWER = register("nearby_flower", ::FlowerSensor)
val NEARBY_PIGLIN_LOVED_POKEMON = register("nearby_golden_pokemon", ::PokemonPiglinLovedSensor)
fun <E : Entity, U : Sensor<E>> register(id: String, supplier: Supplier<U>): SensorType<U> {
val sensor = SensorType(supplier)
@@ -1,91 +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.ai
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.util.getBooleanOrNull
import com.cobblemon.mod.common.util.getDoubleOrNull
import kotlin.math.sqrt
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.PathfinderMob
/**
* Used to maintain some settings around how the entity should wander this tick. The idea is you
* reset this with some script task at priority 0 then some number of steps before it gets to the wander
* tasks you can have other conditional tasks jump in to configure parts of this.
*
* @author Hiroku
* @since July 18, 2025
*/
class CobblemonWanderControl(
var center: WanderCenter? = null,
var maxAttempts: Int = 4, // How many times to try to find a suitable point before giving up on that tick.
var allowLand: Boolean = true,
var allowWater: Boolean = false,
var allowAir: Boolean = false,
var pathCooldownTicks: Int = 20 * 5, // Used to mitigate the cost of pathfinding.
var wanderSpeed: Float = 0.35F,
) {
class WanderCenter(val x: Number, val y: Number, val z: Number, val minRange: Float, val maxRange: Float)
@Transient
val struct: QueryStruct = QueryStruct(hashMapOf())
.addFunction("center_x") { center?.x ?: 0.0 }
.addFunction("center_y") { center?.y ?: 0.0 }
.addFunction("center_z") { center?.z ?: 0.0 }
.addFunction("max_attempts") { maxAttempts.toDouble() }
.addFunction("min_range") { center?.minRange ?: 0.0 }
.addFunction("max_range") { center?.maxRange ?: 0.0 }
.addFunction("allow_land") { if (allowLand) 1.0 else 0.0 }
.addFunction("allow_water") { if (allowWater) 1.0 else 0.0 }
.addFunction("allow_air") { if (allowAir) 1.0 else 0.0 }
.addFunction("path_cooldown_ticks") { pathCooldownTicks.toDouble() }
.addFunction("wander_speed") { wanderSpeed }
.addFunction("set_center") { params ->
if (params.params.size < 3) {
throw IllegalArgumentException("set_center requires at least 3 parameters: x, y, z")
}
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
val minRange = params.getDoubleOrNull(3) ?: 0.0
val maxRange = params.getDoubleOrNull(4) ?: Float.MAX_VALUE.toDouble()
center = WanderCenter(
x = x,
y = y,
z = z,
minRange = minRange.toFloat(),
maxRange = maxRange.toFloat(),
)
}
.addFunction("set_max_attempts") { params -> maxAttempts = params.getDoubleOrNull(0)?.toInt() ?: 4 }
.addFunction("set_allow_land") { params -> allowLand = params.getBooleanOrNull(0) ?: true }
.addFunction("set_allow_water") { params -> allowWater = params.getBooleanOrNull(0) ?: false }
.addFunction("set_allow_air") { params -> allowAir = params.getBooleanOrNull(0) ?: false }
.addFunction("set_path_cooldown_ticks") { params -> pathCooldownTicks = params.getDoubleOrNull(0)?.toInt() ?: (20 * 5) }
.addFunction("set_wander_speed") { params -> wanderSpeed = params.getDoubleOrNull(0)?.toFloat() ?: 0.35F }
.addFunction("reset") {
center = null
allowLand = false
allowWater = false
allowAir = false
pathCooldownTicks = 20 * 5
wanderSpeed = 0.35F
maxAttempts = 4
}
fun isSuitable(position: BlockPos): Boolean {
val center = center ?: return true
val centerPos = BlockPos(center.x.toInt(), center.y.toInt(), center.z.toInt())
val distance = sqrt(centerPos.distSqr(position))
val suitable = distance >= center.minRange && distance <= center.maxRange
return suitable
}
}
@@ -1,45 +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.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.OneShot
import net.minecraft.world.entity.schedule.Activity
class ActivityChangeTaskConfig : SingleTaskConfig {
val activity: Activity = Activity.IDLE
val condition: ExpressionLike = "false".asExpressionLike()
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> = emptyList()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return object : OneShot<LivingEntity>() {
override fun trigger(serverLevel: ServerLevel, livingEntity: LivingEntity, l: Long): Boolean {
runtime.withQueryValue("entity", livingEntity.asMostSpecificMoLangValue())
if (condition.resolveBoolean(runtime)) {
livingEntity.brain.setActiveActivityIfPossible(activity)
return true
} else {
return false
}
}
}
}
}
@@ -21,7 +21,6 @@ import net.minecraft.world.entity.Mob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.LookAtTargetSink
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.SensorType
class LookAtTargetTaskConfig : SingleTaskConfig {
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
@@ -40,8 +39,7 @@ class LookAtTargetTaskConfig : SingleTaskConfig {
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
behaviourConfigurationContext.addMemories(MemoryModuleType.LOOK_TARGET, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES)
behaviourConfigurationContext.addSensors(SensorType.NEAREST_LIVING_ENTITIES)
behaviourConfigurationContext.addMemories(MemoryModuleType.LOOK_TARGET)
return WrapperLivingEntityTask(LookAtTargetSink(minDurationTicks.resolveInt(), maxDurationTicks.resolveInt()), Mob::class.java)
}
}
@@ -1,82 +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.ai.config.task
import com.bedrockk.molang.ast.StringExpression
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.mojang.datafixers.util.Either
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.Behavior
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.MemoryStatus
/**
* A task that modifies the aspects of a Pokémon or NPC based on a memory module.
* If the memory is registered, the aspect is added; if it is absent, the aspect is removed.
*
* @author Hiroku
* @since July 6th, 2025
*/
class MemoryAspectTaskConfig : SingleTaskConfig {
var memory: MemoryModuleType<*> = MemoryModuleType.DUMMY
var aspect: ExpressionOrEntityVariable = Either.left(StringExpression(StringValue("")))
override fun getVariables(entity: LivingEntity) = listOf(aspect).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (entity !is PokemonEntity && entity !is NPCEntity) {
return null
} else if (memory == MemoryModuleType.DUMMY) {
Cobblemon.LOGGER.error("MemoryAspectTaskConfig: Memory module type is not set for ${entity.name.string}.")
return null
} else if (aspect.left().orElse(null)?.originalString == "") {
Cobblemon.LOGGER.error("MemoryAspectTaskConfig: Aspect is not set for ${entity.name.string}.")
return null
}
val aspect = aspect.resolveString()
return object : Behavior<LivingEntity>(
mapOf(memory to MemoryStatus.REGISTERED),
Int.MAX_VALUE,
Int.MAX_VALUE
) {
override fun canStillUse(level: ServerLevel, entity: LivingEntity, gameTime: Long) = true
override fun tick(level: ServerLevel, owner: LivingEntity, gameTime: Long) {
val hasMemory = !owner.brain.hasMemoryValue(memory)
if (owner is PokemonEntity) {
if (!hasMemory && aspect in owner.pokemon.forcedAspects) {
owner.pokemon.forcedAspects -= aspect
} else if (hasMemory && aspect !in owner.pokemon.forcedAspects) {
owner.pokemon.forcedAspects += aspect
} else return
owner.pokemon.updateAspects()
} else if (owner is NPCEntity) {
if (!hasMemory && aspect in owner.appliedAspects) {
owner.appliedAspects -= aspect
} else if (hasMemory && aspect !in owner.appliedAspects) {
owner.appliedAspects += aspect
} else return
owner.updateAspects()
}
}
}
}
}
@@ -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.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToBeeHiveTask
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class PathToBeeHiveTaskConfig : SingleTaskConfig {
companion object {
const val HONEY = "honey"
}
val condition = booleanVariable(HONEY, "can_add_honey", true).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (entity !is PokemonEntity) {
return null
}
if (!checkCondition(entity, condition)) {
return null
}
return PathToBeeHiveTask.create()
}
}
@@ -1,51 +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.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.CobblemonSensors
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToFlowerTask
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class PathToFlowerTaskConfig : SingleTaskConfig {
companion object {
const val POLLINATE = "pollinate"
}
val condition = booleanVariable(POLLINATE, "can_pollinate", true).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition
).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (entity !is PokemonEntity) {
return null
}
if (!checkCondition(entity, condition)) {
return null
}
behaviourConfigurationContext.addMemories(
CobblemonMemories.NEARBY_FLOWERS,
CobblemonMemories.HIVE_COOLDOWN,
CobblemonMemories.HIVE_LOCATION,
CobblemonMemories.POLLINATED
)
behaviourConfigurationContext.addSensors(CobblemonSensors.NEARBY_BEE_HIVE, CobblemonSensors.NEARBY_FLOWER)
return PathToFlowerTask.create()
}
}
@@ -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.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PlaceHoneyInHiveTask
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class PlaceHoneyInHiveTaskConfig : SingleTaskConfig {
companion object {
const val HONEY = "honey"
}
val condition = booleanVariable(HONEY, "can_add_honey", true).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition
).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (entity !is PokemonEntity) {
return null
}
if (!checkCondition(entity, condition)) {
return null
}
return PlaceHoneyInHiveTask.create()
}
}
@@ -1,94 +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.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.getMemorySafely
import com.cobblemon.mod.common.util.sendParticlesServer
import com.mojang.datafixers.util.Either
import net.minecraft.core.particles.ParticleTypes
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.Behavior
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.MemoryStatus
import net.minecraft.world.phys.Vec3
class PollinateFlowerTaskConfig : SingleTaskConfig {
companion object {
const val POLLINATE = "pollinate"
}
val condition = booleanVariable(POLLINATE, "can_pollinate", true).asExpressible()
val durationTicks: ExpressionOrEntityVariable = Either.left("30".asExpression())
override fun getVariables(entity: LivingEntity) = listOf(
condition
).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (entity !is PokemonEntity) {
return null
}
if (!checkCondition(entity, condition)) {
return null
}
return object : Behavior<LivingEntity>(
mapOf(
CobblemonMemories.NEARBY_FLOWERS to MemoryStatus.VALUE_PRESENT,
MemoryModuleType.WALK_TARGET to MemoryStatus.VALUE_ABSENT,
CobblemonMemories.POLLINATED to MemoryStatus.VALUE_ABSENT,
CobblemonMemories.HIVE_COOLDOWN to MemoryStatus.VALUE_ABSENT,
),
15,
45
) {
override fun checkExtraStartConditions(level: ServerLevel, owner: LivingEntity): Boolean {
return entity.brain.getMemorySafely(CobblemonMemories.NEARBY_FLOWERS).orElse(emptyList()).any {
Vec3.atCenterOf(it).distanceTo(entity.position()) <= 1.0
}
}
override fun canStillUse(level: ServerLevel, entity: LivingEntity, gameTime: Long): Boolean {
return checkExtraStartConditions(level, entity)
}
override fun start(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
super.start(level, entity, gameTime)
}
override fun tick(level: ServerLevel, owner: LivingEntity, gameTime: Long) {
level.sendParticlesServer(
particleType = ParticleTypes.DRIPPING_HONEY,
position = owner.eyePosition,
particles = 3,
offset = Vec3.ZERO,
speed = 0.0
)
}
override fun stop(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
val flowerStillThere = canStillUse(level, entity, gameTime)
if (flowerStillThere) {
entity.brain.setMemory(CobblemonMemories.POLLINATED, true)
}
}
}
}
}
@@ -99,11 +99,6 @@ interface TaskConfig {
cobblemonResource("eat_grass") to EatGrassTaskConfig::class.java,
cobblemonResource("find_air") to FindAirTaskConfig::class.java,
cobblemonResource("go_to_land") to GoToLandTaskConfig::class.java,
cobblemonResource("path_to_hive") to PathToBeeHiveTaskConfig::class.java,
cobblemonResource("place_honey_in_hive") to PlaceHoneyInHiveTaskConfig::class.java,
cobblemonResource("path_to_flower") to PathToFlowerTaskConfig::class.java,
cobblemonResource("pollinate_flower") to PollinateFlowerTaskConfig::class.java,
cobblemonResource("go_to_land") to GoToLandTaskConfig::class.java,
cobblemonResource("manage_flight_in_battle") to ManageFlightInBattleTaskConfig::class.java,
cobblemonResource("attack_hostile_mobs") to AttackHostileMobsTaskConfig::class.java,
cobblemonResource("move_into_fluid") to MoveIntoFluidTaskConfig::class.java,
@@ -115,8 +110,6 @@ interface TaskConfig {
cobblemonResource("count_followers") to CountFollowersTaskConfig::class.java,
cobblemonResource("hate_entity") to HateEntityTaskConfig::class.java,
cobblemonResource("target_entity") to TargetEntityTaskConfig::class.java,
cobblemonResource("memory_aspect") to MemoryAspectTaskConfig::class.java,
cobblemonResource("activity_change") to ActivityChangeTaskConfig::class.java,
)
val runtime = MoLangRuntime().setup()
@@ -10,14 +10,12 @@ package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.ai.CobblemonWalkTarget
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.core.BlockPos
@@ -25,11 +23,9 @@ import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.util.HoverRandomPos
import net.minecraft.world.entity.ai.util.LandRandomPos
import net.minecraft.world.level.pathfinder.PathType
@@ -105,70 +101,45 @@ class WanderTaskConfig : SingleTaskConfig {
behaviourConfigurationContext.addMemories(
MemoryModuleType.WALK_TARGET,
MemoryModuleType.LOOK_TARGET,
CobblemonMemories.PATH_COOLDOWN,
CobblemonMemories.WANDER_CONTROL
CobblemonMemories.PATH_COOLDOWN
)
return BehaviorBuilder.create {
it.group(
it.absent(MemoryModuleType.WALK_TARGET),
it.registered(MemoryModuleType.LOOK_TARGET),
it.absent(CobblemonMemories.PATH_COOLDOWN),
it.registered(CobblemonMemories.WANDER_CONTROL)
).apply(it) { walkTarget, lookTarget, pathCooldown, wanderControl ->
it.absent(CobblemonMemories.PATH_COOLDOWN)
).apply(it) { walkTarget, lookTarget, pathCooldown ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || entity.isUnderWater) {
return@Trigger false
}
val wanderControl = it.tryGet(wanderControl).orElse(null) ?: CobblemonWanderControl()
val avoidsTargetingAir = avoidTargetingAir.resolveBoolean()
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val wanderChance = runtime.resolveFloat(wanderChanceExpression)
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance || !wanderControl.allowLand) {
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance) {
return@Trigger false
}
pathCooldown.setWithExpiry(true, wanderControl.pathCooldownTicks.toLong())
pathCooldown.setWithExpiry(true, 40L)
val avoidsTargetingAir = avoidTargetingAir.resolveBoolean()
// val targetVec = getLandTarget(entity) ?: return@Trigger true
val targetVec = LandRandomPos.getPos(
entity,
horizontalRange.resolveInt(),
verticalRange.resolveInt()
) ?: return@Trigger false
val minimumHeight = minimumHeight.resolveInt()
val maximumHeight = maximumHeight.resolveInt()
var attempts = 0
var pos: BlockPos? = null
while (attempts++ < wanderControl.maxAttempts && pos == null) {
// val targetVec = getLandTarget(entity) ?: return@Trigger true
val targetVec = if (maximumHeight != -1) {
HoverRandomPos.getPos(
entity,
horizontalRange.resolveInt(),
maxOf(verticalRange.resolveInt(), maximumHeight), // In case they fly up pretty high and need to find a way down
entity.random.nextFloat() - 0.5,
entity.random.nextFloat() - 0.5,
Math.PI.toFloat(),
maximumHeight,
minimumHeight
)
} else {
LandRandomPos.getPos(
entity,
horizontalRange.resolveInt(),
verticalRange.resolveInt()
)
} ?: continue
pos = applyHeightConstraints(
pos = BlockPos.containing(targetVec),
minimumHeight = minimumHeight,
maximumHeight = maximumHeight,
world = world
).takeIf(wanderControl::isSuitable)
}
if (pos == null) {
return@Trigger false
}
val pos = applyHeightConstraints(
pos = BlockPos.containing(targetVec),
minimumHeight = minimumHeight,
maximumHeight = maximumHeight,
world = world
)
walkTarget.set(
CobblemonWalkTarget(
pos = pos,
@@ -178,7 +149,7 @@ class WanderTaskConfig : SingleTaskConfig {
destinationNodeTypeFilter = { nodeType -> !avoidsTargetingAir || nodeType !in listOf(PathType.OPEN) }
)
)
lookTarget.set(BlockPosTracker(pos.toVec3d().add(0.0, entity.eyeHeight.toDouble(), 0.0)))
lookTarget.erase()
return@Trigger true
}
}
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.ai.config.task.WanderTaskConfig.Companion.WANDER
@@ -31,7 +30,6 @@ import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.level.pathfinder.PathType
import net.minecraft.world.phys.Vec3
class WaterWanderTaskConfig : SingleTaskConfig {
val condition = booleanVariable(WANDER, "water_wanders", true).asExpressible()
@@ -56,59 +54,38 @@ class WaterWanderTaskConfig : SingleTaskConfig {
behaviourConfigurationContext.addMemories(
MemoryModuleType.WALK_TARGET,
MemoryModuleType.LOOK_TARGET,
CobblemonMemories.PATH_COOLDOWN,
CobblemonMemories.WANDER_CONTROL
CobblemonMemories.PATH_COOLDOWN
)
return BehaviorBuilder.create {
it.group(
it.absent(MemoryModuleType.WALK_TARGET),
it.registered(MemoryModuleType.LOOK_TARGET),
it.registered(CobblemonMemories.PATH_COOLDOWN),
it.registered(CobblemonMemories.WANDER_CONTROL)
).apply(it) { walkTarget, lookTarget, pathCooldown, wanderControlData ->
it.registered(CobblemonMemories.PATH_COOLDOWN)
).apply(it) { walkTarget, lookTarget, pathCooldown ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isInWater) {
return@Trigger false
}
val wanderControl = it.tryGet(wanderControlData).orElse(null) ?: CobblemonWanderControl()
if (!wanderControl.allowWater) {
return@Trigger false
}
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val wanderChance = runtime.resolveFloat(wanderChanceExpression)
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance) {
return@Trigger false
}
pathCooldown.setWithExpiry(true, wanderControl.pathCooldownTicks.toLong())
var pos: BlockPos? = null
var target: Vec3? = null
var attempts = 0
while (attempts < wanderControl.maxAttempts && pos == null) {
attempts++
target = BehaviorUtils.getRandomSwimmablePos(entity, horizontalRange.resolveInt(), verticalRange.resolveInt())
?: continue
pos = BlockPos.containing(target).takeIf(wanderControl::isSuitable)
}
if (pos == null || target == null) {
return@Trigger false
}
pathCooldown.setWithExpiry(true, 60L)
val target = BehaviorUtils.getRandomSwimmablePos(entity, horizontalRange.resolveInt(), verticalRange.resolveInt()) ?: return@Trigger false
walkTarget.set(
CobblemonWalkTarget(
pos = pos,
pos = BlockPos.containing(target),
nodeTypeFilter = { it == PathType.WATER || it == PathType.WATER_BORDER },
speedModifier = speedMultiplier.resolveFloat(),
completionRange = 0
completionRange = 1
)
)
lookTarget.set(BlockPosTracker(target /* trust me dude. */))
lookTarget.set(BlockPosTracker(target))
return@Trigger true
}
}
@@ -12,17 +12,9 @@ import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.battles.model.ai.BattleAI
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.battles.PassActionResponse
import com.cobblemon.mod.common.battles.ShowdownActionResponse
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.exception.IllegalActionChoiceException
import com.cobblemon.mod.common.net.messages.client.battle.BattleFaintPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleMakeChoicePacket
import com.cobblemon.mod.common.net.messages.client.battle.BattlePersistentStatusPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleReplacePokemonPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleSwapPokemonPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleSwitchPokemonPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleTransformPokemonPacket
import java.util.UUID
abstract class AIBattleActor(
@@ -33,15 +25,8 @@ abstract class AIBattleActor(
override fun sendUpdate(packet: NetworkPacket<*>) {
super.sendUpdate(packet)
when (packet) {
is BattleMakeChoicePacket -> this.onChoiceRequested()
is BattlePersistentStatusPacket -> {}
is BattleFaintPacket -> {}
is BattleSwitchPokemonPacket -> if (!packet.isAlly) { /* TODO this might be redundant; switch on opposing site should be visible during turn */ }
is BattleSwapPokemonPacket -> { /* TODO CONSIDER MULTI SHIFTING */ }
is BattleHealthChangePacket -> battleAI.onHealthChange(packet)
is BattleTransformPokemonPacket -> if (!packet.isAlly) { /* TODO this might be redundant; switch on opposing site should be visible during turn */ }
is BattleReplacePokemonPacket -> if (!packet.isAlly) { /* TODO this might be redundant; switch on opposing site should be visible during turn */ }
if (packet is BattleMakeChoicePacket) {
this.onChoiceRequested()
}
}
@@ -50,35 +35,11 @@ abstract class AIBattleActor(
*/
open fun onChoiceRequested() {
try {
request?.let {
setActionResponses(it.iterate(this.activePokemon) { battleMon, moveset, forceSwitch ->
battleAI.choose(battleMon, battle, getSide(), moveset, forceSwitch)
})
pokemonList.forEach { pokemon ->
pokemon.willBeSwitchedIn = false
}
} ?: {
val response = mutableListOf<ShowdownActionResponse>()
repeat(activePokemon.size) {
response.add(PassActionResponse)
}
setActionResponses(response)
LOGGER.warn("AI requested choice, but no request was set. Returning PassActionResponses.")
}
setActionResponses(request!!.iterate(this.activePokemon, battleAI::choose))
} catch (exception: IllegalActionChoiceException) {
LOGGER.error("AI was unable to choose an action, we're going to need to pass!")
LOGGER.error("AI was unable to choose a move, we're going to need to pass!")
exception.printStackTrace()
request?.let {
setActionResponses(it.iterate(this.activePokemon) { _, _ , _->
PassActionResponse
})
} ?: {
val response = mutableListOf<ShowdownActionResponse>()
repeat(activePokemon.size) {
response.add(PassActionResponse)
}
setActionResponses(response)
}
setActionResponses(request!!.iterate(this.activePokemon) { _, _, _ -> PassActionResponse })
}
}
}
@@ -8,36 +8,23 @@
package com.cobblemon.mod.common.api.battles.model.ai
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.ShowdownActionResponse
import com.cobblemon.mod.common.battles.ShowdownMoveset
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
/**
* Interface for an actor battle AI
* Interface for an actors battle AI
*
* @since January 16th, 2022
* @author Deltric, Polymeta
* @author Deltric
*/
interface BattleAI {
/**
* Requests that the AI choose an action for the given Pokémon
* @param activeBattlePokemon The Pokémon slot that is choosing an action
* @param battle The ongoing [PokemonBattle]
* @param aiSide The [BattleSide] the AI is fighting on, useful for allies and opponent determination
* @param moveset The [ShowdownMoveset] for this slot. This can be null if [forceSwitch] is true. Otherwise, it is the available move information from Showdown.
* @param forceSwitch Whether this is a force switch situation.
* @param moveset The [ShowdownMoveset] for this slot. This can be null if [forceSwitch] is true. Otherwise it is the available move information from Showdown.
* @param forceSwitch Whether or not this is a force switch situation.
* @return the action response
*/
fun choose(
activeBattlePokemon: ActiveBattlePokemon,
battle: PokemonBattle,
aiSide: BattleSide,
moveset: ShowdownMoveset?,
forceSwitch: Boolean
): ShowdownActionResponse
fun onHealthChange(packet: BattleHealthChangePacket) {}
fun choose(activeBattlePokemon: ActiveBattlePokemon, moveset: ShowdownMoveset?, forceSwitch: Boolean): ShowdownActionResponse
}
@@ -21,7 +21,6 @@ interface PokemonSideDelegate : EntitySideDelegate<PokemonEntity> {
fun updatePostDeath() {}
fun handleStatus(status: Byte) {}
fun spawnShinyParticle(player: Player) {}
fun spawnAspectParticle() {}
fun positionRider(passenger: Entity, positionUpdater: MoveFunction)
fun updateAge(age: Int) {}
}
@@ -231,7 +231,7 @@ object CobblemonEvents {
@JvmField
val POKEMON_GAINED = EventObservable<PokemonGainedEvent>()
@JvmField
val POKEMON_SEEN = CancelableObservable<PokemonSeenEvent>()
val POKEMON_SEEN = EventObservable<PokemonSeenEvent>()
@JvmField
val POKEMON_ASPECTS_CHANGED = EventObservable<PokemonAspectsChangedEvent>()
@JvmField
@@ -8,14 +8,9 @@
package com.cobblemon.mod.common.api.events.berry
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.cobblemon.mod.common.util.toArrayStruct
import com.cobblemon.mod.common.util.worldRegistry
import net.minecraft.core.BlockPos
import net.minecraft.core.registries.Registries
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
@@ -43,11 +38,4 @@ data class BerryHarvestEvent(
val state: BlockState,
val blockEntity: BerryBlockEntity,
val drops: MutableList<ItemStack>
) : BerryEvent {
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"berry" to StringValue(berry.identifier.toString()),
"world" to world.worldRegistry.wrapAsHolder(world).asMoLangValue(Registries.DIMENSION),
"pos" to pos.toArrayStruct()
)
}
) : BerryEvent
@@ -8,16 +8,10 @@
package com.cobblemon.mod.common.api.events.drops
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.drop.DropEntry
import com.cobblemon.mod.common.api.drop.DropTable
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.util.asArrayValue
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
@@ -44,14 +38,4 @@ class LootDroppedEvent(
val player: ServerPlayer?,
val entity: LivingEntity?,
val drops: MutableList<DropEntry>
) : Cancelable() {
val context = mapOf<String, MoValue>(
"player" to (player?.asMoLangValue() ?: DoubleValue.ZERO),
"entity" to (entity?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO),
"drops" to drops.asArrayValue { it.asMoLangValue() }
)
val functions = moLangFunctionMap(
cancelFunc
)
}
) : Cancelable()
@@ -9,21 +9,10 @@
package com.cobblemon.mod.common.api.events.fishing
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.util.server
import net.minecraft.world.item.ItemStack
/**
* Event that is fired when a bait is consumed.
* @param rod The ItemStack of the rod that consumed the bait.
*/
class BaitConsumedEvent(val rod: ItemStack) : Cancelable() {
val context = mutableMapOf(
"rod" to rod.asMoLangValue(server()!!.registryAccess())
)
val functions = moLangFunctionMap(
cancelFunc
)
}
class BaitConsumedEvent(val rod: ItemStack) : Cancelable()
@@ -8,12 +8,8 @@
package com.cobblemon.mod.common.api.events.fishing
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.reactive.CancelableObservable
import com.cobblemon.mod.common.util.server
import net.minecraft.world.item.ItemStack
/**
@@ -21,12 +17,4 @@ import net.minecraft.world.item.ItemStack
* @param rod The ItemStack of the rod that the bait is set on.
* @param bait The ItemStack of the bait that is set on the rod.
*/
class BaitSetEvent(val rod: ItemStack, val bait: ItemStack) : Cancelable() {
val context = mutableMapOf(
"rod" to rod.asMoLangValue(server()!!.registryAccess()),
"bait" to bait.asMoLangValue(server()!!.registryAccess())
)
val functions = moLangFunctionMap(
cancelFunc
)
}
class BaitSetEvent(val rod: ItemStack, val bait: ItemStack) : Cancelable()
@@ -9,18 +9,14 @@
package com.cobblemon.mod.common.api.events.fishing
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.itemRegistry
import com.cobblemon.mod.common.util.server
import net.minecraft.core.registries.Registries
import net.minecraft.world.item.ItemStack
@@ -39,14 +35,7 @@ interface BobberSpawnPokemonEvent {
val bobber: PokeRodFishingBobberEntity,
val spawnAction: SpawnAction<*>,
val rod: ItemStack
) : Cancelable(), BobberSpawnPokemonEvent {
val context = mutableMapOf<String, MoValue>(
"rod" to rod.asMoLangValue(server()!!.registryAccess())
)
val functions = moLangFunctionMap(
cancelFunc
)
}
) : Cancelable(), BobberSpawnPokemonEvent
/**
* Event that is fired when a Pokemon is modified before it is spawned by a bobber.
@@ -8,12 +8,8 @@
package com.cobblemon.mod.common.api.events.fishing
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
import com.cobblemon.mod.common.util.server
import net.minecraft.world.item.ItemStack
/**
@@ -32,15 +28,7 @@ interface PokerodCastEvent {
override var rod: ItemStack,
var bobber: PokeRodFishingBobberEntity,
var bait: ItemStack
) : PokerodCastEvent, Cancelable() {
val context = mutableMapOf<String, MoValue>(
"rod" to rod.asMoLangValue(server()!!.registryAccess()),
"bait" to bait.asMoLangValue(server()!!.registryAccess())
)
val functions = moLangFunctionMap(
cancelFunc
)
}
) : PokerodCastEvent, Cancelable()
/**
* Event that is fired after a fishing rod is cast.
@@ -52,10 +40,5 @@ interface PokerodCastEvent {
override var rod: ItemStack,
var bobber: PokeRodFishingBobberEntity,
var bait: ItemStack
) : PokerodCastEvent {
val context = mutableMapOf<String, MoValue>(
"rod" to rod.asMoLangValue(server()!!.registryAccess()),
"bait" to bait.asMoLangValue(server()!!.registryAccess())
)
}
) : PokerodCastEvent
}
@@ -9,9 +9,6 @@
package com.cobblemon.mod.common.api.events.fishing
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.util.server
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
@@ -19,13 +16,4 @@ import net.minecraft.world.item.ItemStack
* Event that is fired when a fishing rod is reeled.
* @param rod The ItemStack of the rod that is being reeled.
*/
class PokerodReelEvent(val player: Player, val rod: ItemStack) : Cancelable() {
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"rod" to rod.asMoLangValue(server()!!.registryAccess())
)
val functions = moLangFunctionMap(
cancelFunc
)
}
class PokerodReelEvent(val player: Player, val rod: ItemStack) : Cancelable()
@@ -8,9 +8,6 @@
package com.cobblemon.mod.common.api.events.pokeball
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.world.entity.LivingEntity
@@ -30,17 +27,4 @@ class PokemonCatchRateEvent(
val pokeBallEntity: EmptyPokeBallEntity,
val pokemonEntity: PokemonEntity,
var catchRate: Float
) {
val context = mutableMapOf(
"thrower" to thrower.asMostSpecificMoLangValue(),
"poke_ball_entity" to pokeBallEntity.struct,
"pokemon_entity" to pokemonEntity.struct,
"catch_rate" to DoubleValue(catchRate.toDouble())
)
val functions = moLangFunctionMap(
"set_catch_rate" to {
catchRate = it.getDouble(0).toFloat()
DoubleValue.ONE
}
)
}
)
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.api.events.pokedex.scanning
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -22,14 +21,15 @@ data class PokemonScannedEvent(val player: ServerPlayer, val scannedPokemonEntit
val isOwned: Boolean
get() = scannedPokemonEntityData.pokemon.getOwnerUUID() == player.uuid
val context: MutableMap<String, MoValue> = mutableMapOf(
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"pokemon" to scannedPokemonEntityData.pokemon.struct,
"disguise" to (scannedPokemonEntityData.disguise?.struct ?: DoubleValue.ZERO),
"entity" to when (scannedEntity) {
is LivingEntity -> scannedEntity.asMostSpecificMoLangValue()
else -> DoubleValue.ZERO
"entity" to {
when (scannedEntity) {
is LivingEntity -> scannedEntity.asMostSpecificMoLangValue()
else -> DoubleValue.ZERO
}
},
"is_owned" to DoubleValue(if (isOwned) 1.0 else 0.0)
)
}
@@ -8,13 +8,9 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.getPlayer
import net.minecraft.server.level.ServerPlayer
class CollectEggEvent (
@@ -22,13 +18,4 @@ class CollectEggEvent (
val maleParent : Pokemon,
val femaleParent : Pokemon,
val player : ServerPlayer
) : Cancelable() {
val context = mutableMapOf(
"male_parent" to maleParent.struct,
"female_parent" to femaleParent.struct,
"player" to (player.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO)
)
val functions = moLangFunctionMap(
cancelFunc
)
}
) : Cancelable()
@@ -14,7 +14,9 @@ import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.pokemon.stats.*
import com.cobblemon.mod.common.api.pokemon.stats.EvSource
import com.cobblemon.mod.common.api.pokemon.stats.ItemEvSource
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.ifIsType
@@ -54,15 +56,7 @@ interface EvGainedEvent {
val context = mutableMapOf(
"pokemon" to source.pokemon.struct,
"stat" to StringValue(stat.identifier.toString()),
"amount" to DoubleValue(amount.toDouble()),
"source" to StringValue(
when (source) {
is BattleEvSource -> "battle"
is ItemEvSource -> "interaction"
is SidemodEvSource -> source.sidemodId
else -> "unknown"
}
)
"amount" to DoubleValue(amount.toDouble())
)
val functions = moLangFunctionMap(
cancelFunc,
@@ -84,15 +78,7 @@ interface EvGainedEvent {
val context = mutableMapOf(
"pokemon" to source.pokemon.struct,
"stat" to StringValue(stat.identifier.toString()),
"amount" to DoubleValue(amount.toDouble()),
"source" to StringValue(
when (source) {
is BattleEvSource -> "battle"
is ItemEvSource -> "interaction"
is SidemodEvSource -> source.sidemodId
else -> "unknown"
}
)
"amount" to DoubleValue(amount.toDouble())
)
}
@@ -8,12 +8,9 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.pokemon.experience.*
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceSource
import com.cobblemon.mod.common.pokemon.Pokemon
/**
@@ -27,28 +24,7 @@ class ExperienceGainedPreEvent(
val pokemon: Pokemon,
val source: ExperienceSource,
var experience: Int
) : Cancelable() {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"source" to StringValue(
when (source) {
is BattleExperienceSource -> "battle"
is CandyExperienceSource -> "interaction"
is CommandExperienceSource -> "command"
is SidemodExperienceSource -> source.sidemodId
else -> "unknown"
}
),
"experience" to DoubleValue(experience.toDouble())
)
val functions = moLangFunctionMap(
cancelFunc,
"set_amount" to {
experience = it.getInt(0)
DoubleValue.ONE
}
)
}
) : Cancelable()
/**
* Event fired when experience has been gained. Information about whether it leveled up or not is
@@ -64,20 +40,4 @@ class ExperienceGainedPostEvent(
val previousLevel: Int,
val currentLevel: Int,
val learnedMoves: MutableSet<MoveTemplate>
) {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"source" to StringValue(
when (source) {
is BattleExperienceSource -> "battle"
is CandyExperienceSource -> "interaction"
is CommandExperienceSource -> "command"
is SidemodExperienceSource -> source.sidemodId
else -> "unknown"
}
),
"experience" to DoubleValue(experience.toDouble()),
"previous_level" to DoubleValue(previousLevel.toDouble()),
"current_level" to DoubleValue(currentLevel.toDouble())
)
}
)
@@ -8,9 +8,7 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.pokemon.Pokemon
/**
@@ -27,16 +25,4 @@ data class FriendshipUpdatedEvent(
set(value) {
field = value.coerceIn(0, Cobblemon.config.maxPokemonFriendship)
}
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"new_friendship" to DoubleValue(newFriendship.toDouble())
)
val functions = moLangFunctionMap(
"set_new_friendship" to {
newFriendship = it.getInt(0)
DoubleValue.ONE
}
)
}
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.bedrockk.molang.runtime.value.DoubleValue
/**
* Event that is fired when a player owned Pokémon has its fullness changed
@@ -24,16 +22,4 @@ data class FullnessUpdatedEvent(
set(value) {
field = value.coerceIn(0, pokemon.getMaxFullness())
}
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"new_fullness" to DoubleValue(newFullness.toDouble())
)
val functions = moLangFunctionMap(
"set_new_fullness" to {
newFullness = it.getInt(0)
DoubleValue.ONE
}
)
}
@@ -8,30 +8,15 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.util.getPlayer
import net.minecraft.server.level.ServerPlayer
interface HatchEggEvent {
val egg: PokemonProperties
val player: ServerPlayer
data class Pre(override var egg : PokemonProperties, override var player: ServerPlayer) : HatchEggEvent, Cancelable() {
val context = mutableMapOf(
"player" to (player.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO)
)
val functions = moLangFunctionMap(
cancelFunc
)
}
data class Pre(override var egg : PokemonProperties, override var player: ServerPlayer) : HatchEggEvent, Cancelable()
data class Post(override var egg : PokemonProperties, override var player: ServerPlayer) : HatchEggEvent {
val context = mutableMapOf(
"player" to (player.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO)
)
}
data class Post(override var egg : PokemonProperties, override var player: ServerPlayer) : HatchEggEvent
}
@@ -50,7 +50,7 @@ interface HeldItemEvent {
"pokemon" to pokemon.struct,
"receiving" to receiving.asMoLangValue(server()!!.registryAccess()),
"returning" to returning.asMoLangValue(server()!!.registryAccess()),
"decrement" to DoubleValue(if (decrement) 1.0 else 0.0)
"decrement" to DoubleValue(if (decrement) 1.0 else 0.0),
)
}
val functions = moLangFunctionMap(
@@ -74,7 +74,7 @@ interface HeldItemEvent {
"pokemon" to pokemon.struct,
"received" to received.asMoLangValue(server()!!.registryAccess()),
"returned" to returned.asMoLangValue(server()!!.registryAccess()),
"decremented" to DoubleValue(if (decremented) 1.0 else 0.0)
"decremented" to DoubleValue(if (decremented) 1.0 else 0.0),
)
}
}
@@ -8,18 +8,10 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.getPlayer
import java.util.*
data class PokemonAspectsChangedEvent(
val ownerId: UUID?,
val pokemon: Pokemon,
) {
val context = mutableMapOf(
"player" to (ownerId?.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"pokemon" to pokemon.struct
)
}
)
@@ -8,23 +8,10 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.getPlayer
import java.util.UUID
data class PokemonSeenEvent(
val playerId: UUID,
val pokemon: Pokemon
) : Cancelable() {
val context = mapOf<String, MoValue>(
"player" to playerId.getPlayer()!!.asMoLangValue(),
"pokemon" to pokemon.struct
)
val functions = moLangFunctionMap(
cancelFunc
)
}
)
@@ -8,9 +8,7 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.level.ServerLevel
@@ -27,15 +25,7 @@ data class PokemonSentPreEvent(
val pokemon: Pokemon,
val level: ServerLevel,
val position: Vec3
) : Cancelable() {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"position" to StringValue(position.toString())
)
val functions = moLangFunctionMap(
cancelFunc
)
}
) : Cancelable()
/**
* Event fired after a [PokemonEntity] is spawned from a player's party and after its animations are finished.
@@ -47,9 +37,4 @@ data class PokemonSentPreEvent(
data class PokemonSentPostEvent(
val pokemon: Pokemon,
val pokemonEntity: PokemonEntity
) {
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"pokemon_entity" to pokemonEntity.struct
)
}
)
@@ -8,10 +8,7 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.level.ServerPlayer
@@ -25,13 +22,4 @@ data class ShoulderMountEvent(
val player: ServerPlayer,
val pokemon: Pokemon,
val isLeft: Boolean
) : Cancelable() {
val context = mutableMapOf(
"player" to (player.asMoLangValue() ?: DoubleValue.ZERO),
"pokemon" to pokemon.struct,
"is_left" to DoubleValue(if (isLeft) 1.0 else 0.0),
)
val functions = moLangFunctionMap(
cancelFunc
)
}
) : Cancelable()
@@ -8,12 +8,9 @@
package com.cobblemon.mod.common.api.events.pokemon.healing
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.item.HealingSource
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.item.berry.HealingBerryItem
import com.cobblemon.mod.common.item.interactive.PotionItem
import com.cobblemon.mod.common.item.interactive.PotionType
@@ -34,14 +31,4 @@ class PokemonHealedEvent(
) : Cancelable() {
fun isFullHeal() = amount == -1
fun isHealed() = amount > 0
val context = mutableMapOf(
"pokemon" to pokemon.struct,
"amount" to DoubleValue(amount),
"source" to StringValue(source.toString())
)
val functions = moLangFunctionMap(
cancelFunc
)
}
@@ -8,10 +8,7 @@
package com.cobblemon.mod.common.api.events.storage
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.storage.pc.PCBox
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
@@ -45,26 +42,11 @@ interface ChangePCBoxWallpaperEvent {
override val player: ServerPlayer,
override val box: PCBox,
override var wallpaper: ResourceLocation
) : ChangePCBoxWallpaperEvent, Cancelable() {
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"box" to StringValue(box.toString()),
"wallpaper" to StringValue(wallpaper.toString())
)
val functions = mapOf(
cancelFunc
)
}
) : ChangePCBoxWallpaperEvent, Cancelable()
class Post(
override val player: ServerPlayer,
override val box: PCBox,
override val wallpaper: ResourceLocation
) : ChangePCBoxWallpaperEvent {
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"box" to StringValue(box.toString()),
"wallpaper" to StringValue(wallpaper.toString())
)
}
) : ChangePCBoxWallpaperEvent
}
@@ -23,17 +23,12 @@ import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.CobblemonUnlockableWallpapers
import com.cobblemon.mod.common.Environment
import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
import com.cobblemon.mod.common.api.dialogue.ReferenceDialogueFaceProvider
import com.cobblemon.mod.common.api.drop.DropEntry
import com.cobblemon.mod.common.api.mark.Marks
import com.cobblemon.mod.common.api.moves.BenchedMove
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.api.moves.animations.NPCProvider
@@ -50,8 +45,6 @@ import com.cobblemon.mod.common.api.pokedex.SeenCount
import com.cobblemon.mod.common.api.pokedex.SeenPercent
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.experience.SidemodExperienceSource
import com.cobblemon.mod.common.api.pokemon.moves.LearnsetQuery
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
import com.cobblemon.mod.common.api.scheduling.Schedulable
@@ -66,11 +59,6 @@ import com.cobblemon.mod.common.api.storage.pc.PCPosition
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.battles.BattleBuilder
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.battles.BattleFormat.Companion.GEN_9_DOUBLES
import com.cobblemon.mod.common.battles.BattleFormat.Companion.GEN_9_SINGLES
import com.cobblemon.mod.common.battles.BattleFormat.Companion.GEN_9_TRIPLES
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
@@ -103,7 +91,6 @@ import net.minecraft.core.BlockPos
import net.minecraft.core.Holder
import net.minecraft.core.Registry
import net.minecraft.core.RegistryAccess
import net.minecraft.core.Vec3i
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
import net.minecraft.nbt.CompoundTag
@@ -205,48 +192,6 @@ object MoLangFunctions {
values.forEachIndexed { index, moValue -> array.setDirectly("$index", moValue) }
return@Function array
},
"length" to java.util.function.Function { params ->
val array = params.get<ArrayStruct>(0)
return@Function DoubleValue(array.map.size.toDouble())
},
"append" to java.util.function.Function { params ->
val array = params.get<ArrayStruct>(0)
val value = params.get<MoValue>(1)
val nextIndex = array.map.size
array.setDirectly("$nextIndex", value)
return@Function array
},
"insert" to java.util.function.Function { params ->
val array = params.get<ArrayStruct>(0)
val index = params.getInt(1)
val value = params.get<MoValue>(2)
val size = array.map.size
// Shift elements at and after index up by 1
for (i in (size - 1) downTo index) {
val current = array.map[i.toString()]
if (current != null) {
array.map[(i + 1).toString()] = current
}
}
array.map[index.toString()] = value
return@Function array
},
"delete" to java.util.function.Function { params ->
val array = params.get<ArrayStruct>(0)
val index = params.getInt(1)
if (index in 0 until array.map.size) {
array.map.remove(index.toString())
// Re-index the array to keep keys sequential in numerical order
val newMap = hashMapOf<String, MoValue>()
array.map.keys.mapNotNull { it.toIntOrNull() }
.sorted()
.forEachIndexed { i, k -> newMap[i.toString()] = array.map[k.toString()]!! }
array.map.clear()
array.map.putAll(newMap)
}
return@Function array
},
"run_script" to java.util.function.Function { params ->
val runtime = MoLangRuntime()
runtime.environment.query = params.environment.query
@@ -473,14 +418,6 @@ object MoLangFunctions {
map.put("uuid") { _ -> StringValue(player.gameProfile.id.toString()) }
map.put("main_held_item") { _ -> player.mainHandItem.asMoLangValue(player.registryAccess()) }
map.put("off_held_item") { _ -> player.offhandItem.asMoLangValue(player.registryAccess()) }
map.put("inventory") { _ ->
val inventory = player.inventory
val items = ArrayStruct(hashMapOf())
for (i in 0 until inventory.containerSize) {
items.setDirectly("$i", inventory.getItem(i).asMoLangValue(player.registryAccess()))
}
return@put items
}
map.put("face") { params -> ObjectValue(PlayerDialogueFaceProvider(player.uuid, params.getBooleanOrNull(0) != false)) }
map.put("swing_hand") { _ -> player.swing(player.usedItemHand) }
map.put("food_level") { _ -> DoubleValue(player.foodData.foodLevel) }
@@ -592,50 +529,6 @@ object MoLangFunctions {
}
return@put DoubleValue.ZERO
}
map.put("start_battle") { params ->
val opponentValue = params.get<MoValue>(0)
val opponent = if (opponentValue is ObjectValue<*>) {
opponentValue.obj as ServerPlayer
} else {
val paramString = opponentValue.asString()
val playerUUID = paramString.asUUID
if (playerUUID != null) {
server()?.playerList?.getPlayer(playerUUID) ?: return@put DoubleValue.ZERO
} else {
server()?.playerList?.getPlayerByName(paramString) ?: return@put DoubleValue.ZERO
}
}
val format = params.getStringOrNull(1)
?.let(BattleFormat::fromFormatIdentifier)
?: BattleFormat.GEN_9_SINGLES
val setLevel = params.getIntOrNull(2) ?: -1
format.adjustLevel = setLevel
val rules = params.getStringOrNull(5)
?.split(",")
?.toSet()
?: emptySet()
val modifiedBattleFormat = BattleFormat.setBattleRules(
battleFormat = format,
rules = rules
)
val cloneParties = (setLevel != -1) || (params.getBooleanOrNull(3) ?: false)
val healFirst = params.getBooleanOrNull(4) ?: false
val battleStartResult = BattleBuilder.pvp1v1(
player1 = player,
player2 = opponent,
battleFormat = modifiedBattleFormat,
cloneParties = cloneParties,
healFirst = healFirst
)
var returnValue: MoValue = DoubleValue.ZERO
battleStartResult.ifSuccessful { returnValue = it.struct }
return@put returnValue
}
}
map
}
@@ -877,55 +770,6 @@ object MoLangFunctions {
map.put("is_sleeping") { _ -> DoubleValue(entity.isSleeping) }
map.put("health") { _ -> DoubleValue(entity.health) }
map.put("max_health") { _ -> DoubleValue(entity.maxHealth) }
map.put("get_wander_control_memory") {
val value = entity.brain.getMemorySafely(CobblemonMemories.WANDER_CONTROL).orElse(null)
?: CobblemonWanderControl()
if (
!entity.brain.checkMemory(CobblemonMemories.WANDER_CONTROL, MemoryStatus.VALUE_PRESENT)
&& entity.brain.checkMemory(CobblemonMemories.WANDER_CONTROL, MemoryStatus.REGISTERED)
) {
entity.brain.setMemory(CobblemonMemories.WANDER_CONTROL, value)
}
return@put value.struct
}
map.put("get_position_memory") { params ->
val id = params.getString(0).asIdentifierDefaultingNamespace()
val memoryType = BuiltInRegistries.MEMORY_MODULE_TYPE.get(id)
if (entity.brain.checkMemory(memoryType, MemoryStatus.VALUE_PRESENT)) {
val memory = entity.brain.getMemory(memoryType).get()
return@put when (memory) {
is Vec3i -> VariableStruct(
mapOf(
"x" to DoubleValue(memory.x),
"y" to DoubleValue(memory.y),
"z" to DoubleValue(memory.z)
)
)
is net.minecraft.core.Position -> VariableStruct(
mapOf(
"x" to DoubleValue(memory.x()),
"y" to DoubleValue(memory.y()),
"z" to DoubleValue(memory.z())
)
)
else -> DoubleValue.ZERO
}
} else {
return@put DoubleValue.ZERO
}
}
map.put("has_memory_value") { params ->
val memoryTypes = params.params.map {
it.asString().asIdentifierDefaultingNamespace()
}.map(BuiltInRegistries.MEMORY_MODULE_TYPE::get)
return@put DoubleValue(memoryTypes.all { entity.brain.checkMemory(it, MemoryStatus.VALUE_PRESENT) })
}
map.put("lacks_memory_value") { params ->
val memoryTypes = params.params.map {
it.asString().asIdentifierDefaultingNamespace()
}.map(BuiltInRegistries.MEMORY_MODULE_TYPE::get)
return@put DoubleValue(memoryTypes.none { entity.brain.checkMemory(it, MemoryStatus.VALUE_PRESENT) })
}
map.put("can_see") { params ->
val target = params.get<MoValue>(0)
val range = params.getDoubleOrNull(1) ?: 32.0
@@ -1241,37 +1085,9 @@ object MoLangFunctions {
map.put("max_hp") { DoubleValue(pokemon.maxHealth.toDouble()) }
map.put("current_hp") { DoubleValue(pokemon.currentHealth.toDouble()) }
map.put("friendship") { DoubleValue(pokemon.friendship.toDouble()) }
map.put("max_fullness") { DoubleValue(pokemon.getMaxFullness().toDouble()) }
map.put("fullness") { DoubleValue(pokemon.currentFullness.toDouble()) }
map.put("lose_fullness") { params ->
val amount = params.getDouble(0)
pokemon.loseFullness(amount.toInt())
}
map.put("feed_pokemon") { params ->
val amount = params.getDouble(0)
val playSound = params.getBooleanOrNull(1) ?: true
pokemon.feedPokemon(amount.toInt(), playSound)
}
map.put("behaviour") { pokemon.form.behaviour.struct }
map.put("behavior") { pokemon.form.behaviour.struct } // Inferior
map.put("pokeball") { StringValue(pokemon.caughtBall.toString()) }
map.put("ability") { StringValue(pokemon.ability.name) }
map.put("has_learned") { params ->
val moveName = params.getString(0)
val move = pokemon.allAccessibleMoves.find { it.name == moveName }
if(move != null) {
return@put DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
}
}
map.put("moveset") {
val struct = QueryStruct(hashMapOf())
for ((index, move) in pokemon.moveSet.withIndex()) {
struct.addFunction(index.toString()) { move.struct }
}
struct
}
map.put("evs") {
val struct = QueryStruct(hashMapOf())
for (stat in Stats.PERMANENT) {
@@ -1286,13 +1102,6 @@ object MoLangFunctions {
}
struct
}
map.put("hyper_trained_ivs") {
val struct = QueryStruct(hashMapOf())
for (stat in Stats.PERMANENT) {
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.ivs.hyperTrainedIVs[stat] ?: -1.0) }
}
struct
}
map.put("natdex_number") {
DoubleValue(pokemon.species.nationalPokedexNumber.toDouble())
}
@@ -1381,17 +1190,6 @@ object MoLangFunctions {
}
struct
}
map.put("aspects") {
val aspects = pokemon.aspects
return@put aspects.asArrayValue { StringValue(it) }
}
map.put("form_aspects") {
val aspects = pokemon.form.aspects
return@put aspects.asArrayValue { StringValue(it) }
}
map.put("form_name") {
StringValue(pokemon.form.name.toString())
}
// Yes, this is the lazy call for a single hardcoded pre-evolution.
// Lol. Lmao, even.
// TO-DO: Subscribe to [PokemonSpecies.observable].
@@ -1416,18 +1214,6 @@ object MoLangFunctions {
map.put("remove_held_item") { _ ->
pokemon.removeHeldItem()
}
map.put("add_aspects") { params ->
for (aspect in params.params) pokemon.forcedAspects += aspect.asString()
pokemon.updateAspects()
}
map.put("remove_aspects") { params ->
for (aspect in params.params) pokemon.forcedAspects -= aspect.asString()
pokemon.updateAspects()
}
map.put("cosmetic_item") { pokemon.cosmeticItem().asMoLangValue(server()!!.registryAccess()) ?: DoubleValue.ZERO }
map.put("remove_cosmetic_item") { _ ->
pokemon.removeCosmeticItem()
}
map.put("add_marks") { params ->
for (param in params.params) {
val identifier = param.asString().asIdentifierDefaultingNamespace()
@@ -1469,92 +1255,6 @@ object MoLangFunctions {
return@put DoubleValue.ZERO
}
}
map.put("add_exp") { params ->
val exp = params.getDouble(0).toInt()
pokemon.addExperience(SidemodExperienceSource("molang"), exp)
return@put DoubleValue.ONE
}
map.put("set_iv") { params ->
val statId = params.getString(0)
val stat = Stats.getStat(statId)
val value = params.getIntOrNull(1)?.coerceIn(0, IVs.MAX_VALUE) ?: IVs.MAX_VALUE
if (Stats.PERMANENT.contains(stat)) {
pokemon.setIV(stat, value)
return@put DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
}
}
map.put("set_ev") { params ->
val statId = params.getString(0)
val stat = Stats.getStat(statId)
val value = (params.getIntOrNull(1) ?: 0).coerceIn(0, 255)
if (Stats.PERMANENT.contains(stat)) {
pokemon.setEV(stat, value)
return@put DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
}
}
map.put("initialize_moveset") { params ->
val preferLatest = params.getBooleanOrNull(0) ?: true
pokemon.initializeMoveset(preferLatest)
return@put DoubleValue.ONE
}
map.put("validate_moveset") { params ->
val includeLegacy = params.getBooleanOrNull(0) ?: true
pokemon.validateMoveset(includeLegacy)
return@put DoubleValue.ONE
}
map.put("teach_learnable_moves") { params ->
val includeLegacy = params.getBooleanOrNull(0) ?: true
pokemon.teachLearnableMoves(includeLegacy)
return@put DoubleValue.ONE
}
map.put("teach_move") { params ->
val moveName = params.getString(0)
val moveTemplate = Moves.getByName(moveName) ?: return@put DoubleValue.ZERO
val bypass = params.getBooleanOrNull(1) ?: false
val canLearn = bypass || LearnsetQuery.ANY.canLearn(moveTemplate, pokemon.form.moves)
if (!canLearn) {
return@put DoubleValue.ZERO
}
val alreadyKnows = pokemon.moveSet.getMoves().any { it.template == moveTemplate } ||
pokemon.benchedMoves.any { it.moveTemplate == moveTemplate }
if (alreadyKnows) {
return@put DoubleValue.ZERO
}
if (pokemon.moveSet.hasSpace()) {
pokemon.moveSet.add(moveTemplate.create())
} else {
pokemon.benchedMoves.add(BenchedMove(moveTemplate, 0))
}
DoubleValue.ONE
}
map.put("can_learn_move") { params ->
val moveName = params.getString(0)
val moveTemplate = Moves.getByName(moveName) ?: return@put DoubleValue.ZERO
val includeLegacy = params.getBooleanOrNull(1) ?: true
val canLearn = LearnsetQuery.ANY.canLearn(moveTemplate, pokemon.form.moves)
return@put DoubleValue(if (canLearn) 1.0 else 0.0)
}
map.put("unlearn_move") { params ->
val moveName = params.getString(0)
val moveTemplate = Moves.getByName(moveName)
if (moveTemplate != null) {
pokemon.unlearnMove(moveTemplate)
return@put DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
}
}
map
}
)
@@ -1678,12 +1378,6 @@ object MoLangFunctions {
val pokemon = party.get(index) ?: return@put DoubleValue.ZERO
return@put pokemon.struct
}
map.put("set_pokemon") { params ->
val index = params.getInt(0)
val pokemon = params.get<ObjectValue<Pokemon>>(1).obj
party.set(index, pokemon)
return@put DoubleValue.ONE
}
return@mutableListOf map
})
@@ -1696,13 +1390,6 @@ object MoLangFunctions {
val pokemon = pc[PCPosition(box, slot)] ?: return@put DoubleValue.ZERO
return@put pokemon.struct
}
map.put("set_pokemon") { params ->
val box = params.getInt(0)
val slot = params.getInt(1)
val pokemon = params.get<ObjectValue<Pokemon>>(2).obj
pc.set(PCPosition(box, slot), pokemon)
return@put DoubleValue.ONE
}
map.put("resize") { params ->
val newSize = params.getInt(0)
val lockNewSize = params.getBooleanOrNull(1) == true
@@ -1881,21 +1568,6 @@ object MoLangFunctions {
}
)
val dropEntryFunctions: MutableList<(DropEntry) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ dropEntry ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("percentage") { DoubleValue(dropEntry.percentage.toDouble()) }
map.put("quantity") { DoubleValue(dropEntry.quantity.toDouble()) }
map.put("max_selectable_times") { DoubleValue(dropEntry.maxSelectableTimes.toDouble()) }
map.put("can_drop") { params ->
val pokemon = params.getOrNull<ObjectValue<Pokemon>>(0)?.obj
DoubleValue(if (dropEntry.canDrop(pokemon)) 1.0 else 0.0)
}
map
}
)
val pokemonPropertiesFunctions: MutableList<(PokemonProperties) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ props ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
@@ -2024,15 +1696,6 @@ object MoLangFunctions {
return value
}
fun DropEntry.asMoLangValue(): ObjectValue<DropEntry> {
val value = ObjectValue(
obj = this,
stringify = { it.toString() }
)
value.addFunctions(dropEntryFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
}
fun PokemonProperties.asMoLangValue(): ObjectValue<PokemonProperties> {
val value = ObjectValue(
obj = this,
@@ -17,7 +17,6 @@ import net.minecraft.resources.ResourceLocation
* As opposed to the [GlobalPokedexValueCalculator] this interface is always called on a specific dex definition (e.g. galar, kanto)
*/
interface PokedexValueCalculator<T> {
val outputIsPercentage: Boolean
fun calculate(dexManager: AbstractPokedexManager, dex: Map<ResourceLocation, PokedexEntry>): T
}
@@ -26,7 +25,6 @@ interface PokedexValueCalculator<T> {
* The calculation of this interface is aimed to consider all available Pokédex entries and is not limited or filtered to a particular dex
*/
interface GlobalPokedexValueCalculator<T> {
val outputIsPercentage: Boolean
fun calculate(dexManager: AbstractPokedexManager): T
}
@@ -34,7 +32,6 @@ interface GlobalPokedexValueCalculator<T> {
* Calculates the amount of caught Pokémon (globally or per dex)
*/
object CaughtCount : PokedexValueCalculator<Int>, GlobalPokedexValueCalculator<Int> {
override val outputIsPercentage = false
override fun calculate(dexManager: AbstractPokedexManager): Int {
return dexManager.speciesRecords.values.count { it.getKnowledge() == PokedexEntryProgress.CAUGHT }
}
@@ -48,7 +45,6 @@ object CaughtCount : PokedexValueCalculator<Int>, GlobalPokedexValueCalculator<I
* Calculates the amount of seen Pokémon (globally or per dex)
*/
object SeenCount : PokedexValueCalculator<Int>, GlobalPokedexValueCalculator<Int> {
override val outputIsPercentage = false
override fun calculate(dexManager: AbstractPokedexManager): Int {
return dexManager.speciesRecords.values.count { it.getKnowledge() != PokedexEntryProgress.NONE }
}
@@ -62,14 +58,12 @@ object SeenCount : PokedexValueCalculator<Int>, GlobalPokedexValueCalculator<Int
* Calculates the seen percentage globally or relative to a particular dex
*/
object SeenPercent : PokedexValueCalculator<Float>, GlobalPokedexValueCalculator<Float> {
override val outputIsPercentage = true
override fun calculate(dexManager: AbstractPokedexManager): Float {
return dexManager.speciesRecords.values.count { it.getKnowledge() != PokedexEntryProgress.NONE }.toFloat() / DexEntries.entries.values.map { it.speciesId }.toSet().size * 100F
return dexManager.speciesRecords.values.count { it.getKnowledge() != PokedexEntryProgress.NONE }.toFloat() / DexEntries.entries.count().toFloat() * 100F
}
override fun calculate(dexManager: AbstractPokedexManager, dex: Map<ResourceLocation, PokedexEntry>): Float {
return dex.entries.map { it.value }.count { dexManager.getKnowledgeForSpecies(it.speciesId) != PokedexEntryProgress.NONE }.toFloat() / dex.entries.size * 100F
return dex.entries.map { it.value }.count { dexManager.getKnowledgeForSpecies(it.speciesId) != PokedexEntryProgress.NONE }.toFloat() / DexEntries.entries.count().toFloat() * 100F
}
}
@@ -77,13 +71,11 @@ object SeenPercent : PokedexValueCalculator<Float>, GlobalPokedexValueCalculator
* Calculates the caught percentage globally or relative to a particular dex
*/
object CaughtPercent : PokedexValueCalculator<Float>, GlobalPokedexValueCalculator<Float> {
override val outputIsPercentage = true
override fun calculate(dexManager: AbstractPokedexManager): Float {
return dexManager.speciesRecords.values.count { it.getKnowledge() == PokedexEntryProgress.CAUGHT }.toFloat() / DexEntries.entries.values.map { it.speciesId }.toSet().size * 100F
return dexManager.speciesRecords.values.count { it.getKnowledge() == PokedexEntryProgress.CAUGHT }.toFloat() / DexEntries.entries.count().toFloat().toFloat() * 100F
}
override fun calculate(dexManager: AbstractPokedexManager, dex: Map<ResourceLocation, PokedexEntry>): Float {
return dex.entries.map { it.value }.count { dexManager.getKnowledgeForSpecies(it.speciesId) == PokedexEntryProgress.CAUGHT }.toFloat() / dex.entries.size * 100F
return dex.entries.map { it.value }.count { dexManager.getKnowledgeForSpecies(it.speciesId) == PokedexEntryProgress.CAUGHT }.toFloat() / DexEntries.entries.count().toFloat().toFloat() * 100F
}
}
@@ -340,7 +340,6 @@ open class PokemonProperties {
var originalTrainer: String? = null // Original Trainer by Username or UUID
var moves: List<String>? = null
var heldItem: String? = null
var cosmeticItem: String? = null
var ivs: IVs? = null
var evs: EVs? = null
@@ -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.pokemon.aspect
import com.cobblemon.mod.common.api.spawning.influence.SaccharineLogSlatheredInfluence
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import net.minecraft.core.particles.ParticleOptions
import net.minecraft.core.particles.ParticleTypes
import net.minecraft.resources.ResourceLocation
/**
* Used by [PokemonClientDelegate.spawnAspectParticle] to spawn associated particles for aspects
*/
val aspectParticleMap: Map<String, ParticleData> = mapOf(
SaccharineLogSlatheredInfluence.HONEY_DRENCHED_ASPECT to ParticleData.MinecraftParticle(ParticleTypes.FALLING_HONEY, 0.075, 1)
)
sealed class ParticleData {
data class SnowstormParticle(val particle: ResourceLocation, val chance: Double, val amount: Int, val locators: List<String>): ParticleData()
data class MinecraftParticle(val particle: ParticleOptions, val chance: Double, val amount: Int): ParticleData()
}
@@ -43,8 +43,6 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
val tmInterpreter = Interpreter.parseFromPrefixIntoList("tm") { it.tmMoves }
val eggInterpreter = Interpreter.parseFromPrefixIntoList("egg") { it.eggMoves }
val tutorInterpreter = Interpreter.parseFromPrefixIntoList("tutor") { it.tutorMoves }
val legacyInterpreter = Interpreter.parseFromPrefixIntoList("legacy") { it.legacyMoves }
val specialInterpreter = Interpreter.parseFromPrefixIntoList("special") { it.specialMoves }
val formChangeInterpreter = Interpreter.parseFromPrefixIntoList("form_change") { it.formChangeMoves }
val levelUpInterpreter = Interpreter { element, learnset ->
val str = element.takeIf { it.isJsonPrimitive }?.asString ?: return@Interpreter false
@@ -72,8 +70,6 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
tmInterpreter,
eggInterpreter,
tutorInterpreter,
legacyInterpreter,
specialInterpreter,
levelUpInterpreter,
formChangeInterpreter
)
@@ -82,8 +78,6 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
val levelUpMoves = mutableMapOf<Int, MutableList<MoveTemplate>>()
val eggMoves = mutableListOf<MoveTemplate>()
val tutorMoves = mutableListOf<MoveTemplate>()
val legacyMoves = mutableListOf<MoveTemplate>()
val specialMoves = mutableListOf<MoveTemplate>()
val tmMoves = mutableListOf<MoveTemplate>()
/**
* Moves the species/form will have learnt when evolving into itself.
@@ -99,15 +93,6 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
.flatMap { it.value }
.toSet()
fun getAllLegalMoves(): Set<MoveTemplate> {
return levelUpMoves.values.flatten().toSet() +
eggMoves +
tutorMoves +
tmMoves +
formChangeMoves +
evolutionMoves
}
// We only sync level up moves atm
override fun shouldSynchronize(other: Learnset) = other.levelUpMoves != this.levelUpMoves
@@ -24,17 +24,6 @@ fun interface LearnsetQuery {
companion object {
val ANY = LearnsetQuery { move, learnset ->
learnset.levelUpMoves.values.any { it.contains(move) }
|| learnset.eggMoves.contains(move)
|| learnset.tutorMoves.contains(move)
|| learnset.tmMoves.contains(move)
|| learnset.formChangeMoves.contains(move)
|| learnset.evolutionMoves.contains(move)
|| learnset.legacyMoves.contains(move)
|| learnset.specialMoves.contains(move)
}
val LEGAL = LearnsetQuery { move, learnset ->
learnset.levelUpMoves.values.any { it.contains(move) }
|| learnset.eggMoves.contains(move)
|| learnset.tutorMoves.contains(move)
@@ -57,9 +46,6 @@ fun interface LearnsetQuery {
val EVOLUTION = LearnsetQuery { move, learnset -> learnset.evolutionMoves.contains(move) }
val LEGACY_MOVES = LearnsetQuery { move, learnset -> learnset.legacyMoves.contains(move) }
val SPECIAL_MOVES = LearnsetQuery { move, learnset -> learnset.specialMoves.contains(move) }
}
}
@@ -10,9 +10,7 @@ package com.cobblemon.mod.common.api.snowstorm
import com.cobblemon.mod.common.api.codec.CodecMapped
import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
import com.cobblemon.mod.common.api.orientation.OrientationController
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.math.hamiltonProduct
import com.cobblemon.mod.common.util.set
import com.mojang.math.Axis
@@ -28,7 +26,6 @@ import kotlin.math.pow
import kotlin.math.sqrt
import net.minecraft.world.phys.Vec3
import org.joml.AxisAngle4d
import org.joml.AxisAngle4f
import org.joml.Quaternionf
import org.joml.Vector3f
@@ -91,7 +88,7 @@ class RotateXYZCameraMode : ParticleCameraMode {
cameraPitch: Float,
viewDirection: Vec3
): Quaternionf {
val i = Mth.lerp(deltaTicks, prevAngle, angle)
val i = if (angle == 0.0f) 0F else Mth.lerp(deltaTicks, prevAngle, angle)
val q = Quaternionf(cameraAngle)
q.hamiltonProduct(Axis.ZP.rotationDegrees(i))
return q
@@ -125,7 +122,7 @@ class RotateYCameraMode : ParticleCameraMode {
cameraPitch: Float,
viewDirection: Vec3
): Quaternionf {
val i = Mth.lerp(deltaTicks, prevAngle, angle)
val i = if (angle == 0F) 0F else Mth.lerp(deltaTicks, prevAngle, angle)
val q2 = Axis.YP.rotationDegrees(-cameraYaw)
q2.hamiltonProduct(Axis.ZP.rotationDegrees(i))
q2.hamiltonProduct(Axis.YP.rotationDegrees(180F))
@@ -163,7 +160,7 @@ class LookAtXYZ : ParticleCameraMode {
cameraPitch: Float,
viewDirection: Vec3
): Quaternionf {
val i = Mth.lerp(deltaTicks, prevAngle, angle)
val i = if (angle == 0F) 0F else Mth.lerp(deltaTicks, prevAngle, angle)
val rotation = Quaternionf()
rotation.hamiltonProduct(Axis.YP.rotationDegrees(-cameraYaw - 180))
rotation.hamiltonProduct(Axis.XP.rotationDegrees(cameraPitch))
@@ -193,7 +190,7 @@ class LookAtY : ParticleCameraMode {
cameraPitch: Float,
viewDirection: Vec3
): Quaternionf {
val i = Mth.lerp(deltaTicks, prevAngle, angle)
val i = if (angle == 0F) 0F else Mth.lerp(deltaTicks, prevAngle, angle)
val q2 = Axis.YP.rotationDegrees(-cameraYaw + 180)
q2.hamiltonProduct(Axis.ZP.rotationDegrees(i))
return q2
@@ -260,11 +257,15 @@ class EmitterYZPlane : ParticleCameraMode {
cameraPitch: Float,
viewDirection: Vec3
): Quaternionf {
val i = Mth.lerp(deltaTicks, prevAngle, angle)
val forward = matrixWrapper.matrix.transformDirection(OrientationController.LEFT)
val q = Quaternionf(AxisAngle4f(i.toRadians(), forward))
q.mul(Axis.YP.rotationDegrees(90F))
return q
val rotation = Quaternionf(0F, 0F, 0F, 1F)
val quat = AxisAngle4d(rotation)
matrixWrapper.matrix.getRotation(quat)
rotation.set(quat)
rotation.hamiltonProduct(Axis.YP.rotationDegrees(180F)) // Don't worry about it.
rotation.hamiltonProduct(Axis.YP.rotationDegrees(90F))
return rotation
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
@@ -295,11 +296,14 @@ class EmitterXZPlane : ParticleCameraMode {
cameraPitch: Float,
viewDirection: Vec3
): Quaternionf {
val i = Mth.lerp(deltaTicks, prevAngle, angle)
val up = matrixWrapper.matrix.transformDirection(OrientationController.UP)
val q = Quaternionf(AxisAngle4f(-i.toRadians(), up))
q.mul(Axis.XP.rotationDegrees(-90F))
return q
val rotation = Quaternionf(0F, 0F, 0F, 1F)
val quat = AxisAngle4d(rotation)
matrixWrapper.matrix.getRotation(quat)
rotation.set(quat)
rotation.hamiltonProduct(Axis.YP.rotationDegrees(180F)) // Don't worry about it.
rotation.hamiltonProduct(Axis.XP.rotationDegrees(-90F))
return rotation
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
@@ -330,11 +334,15 @@ class EmitterXYPlane : ParticleCameraMode {
cameraPitch: Float,
viewDirection: Vec3
): Quaternionf {
val i = Mth.lerp(deltaTicks, prevAngle, angle)
val forward = matrixWrapper.matrix.transformDirection(OrientationController.FORWARDS)
val q = Quaternionf(AxisAngle4f(i.toRadians(), forward))
q.mul(Axis.ZP.rotationDegrees(90F))
return q
val rotation = Quaternionf(0F, 0F, 0F, 1F)
val quat = AxisAngle4d(rotation)
matrixWrapper.matrix.getRotation(quat)
rotation.set(quat)
rotation.hamiltonProduct(Axis.YP.rotationDegrees(180F)) // Don't worry about it.
return rotation
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
@@ -24,6 +24,7 @@ import com.mojang.serialization.DynamicOps
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.network.RegistryFriendlyByteBuf
import kotlin.math.abs
interface ParticleRotation : CodecMapped {
companion object : ArbitrarilyMappedSerializableCompanion<ParticleRotation, ParticleRotationType>(
@@ -39,7 +40,9 @@ interface ParticleRotation : CodecMapped {
val type: ParticleRotationType
fun getAngle(runtime: MoLangRuntime, age: Int): Double
fun getInitialRotation(runtime: MoLangRuntime): Double
fun getInitialAngularVelocity(runtime: MoLangRuntime): Double
fun getAngularVelocity(runtime: MoLangRuntime, angle: Double, angularVelocity: Double): Double
}
class ParametricParticleRotation(var expression: Expression = NumberExpression(0.0)): ParticleRotation {
@@ -56,12 +59,9 @@ class ParametricParticleRotation(var expression: Expression = NumberExpression(0
override val type = ParticleRotationType.PARAMETRIC
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun getAngle(
runtime: MoLangRuntime,
age: Int,
): Double {
return runtime.resolveDouble(expression)
}
override fun getInitialRotation(runtime: MoLangRuntime) = runtime.resolveDouble(expression)
override fun getInitialAngularVelocity(runtime: MoLangRuntime) = 0.0
override fun getAngularVelocity(runtime: MoLangRuntime, angle: Double, angularVelocity: Double) = runtime.resolveDouble(expression) - angle
override fun readFromBuffer(buffer: RegistryFriendlyByteBuf) {
expression = MoLang.createParser(buffer.readString()).parseExpression()
@@ -92,12 +92,19 @@ class DynamicParticleRotation(
}
}
var angularVelocity: Double? = null
override val type = ParticleRotationType.DYNAMIC
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun getAngle(runtime: MoLangRuntime, age: Int): Double {
return getInitialRotation(runtime) + getAngularVelocity(runtime) * age
override fun getInitialRotation(runtime: MoLangRuntime) = runtime.resolveDouble(startRotation)
override fun getInitialAngularVelocity(runtime: MoLangRuntime) = runtime.resolveDouble(speed) / 20
override fun getAngularVelocity(runtime: MoLangRuntime, angle: Double, angularVelocity: Double): Double {
val acceleration = runtime.resolveDouble(acceleration)
val nextVelocity = angularVelocity * 20 + acceleration
val drag = nextVelocity * runtime.resolveDouble(drag)
return angularVelocity + (if (abs(drag) > abs(nextVelocity)) {
0.0
} else {
nextVelocity - drag - angularVelocity * 20
})
}
override fun readFromBuffer(buffer: RegistryFriendlyByteBuf) {
@@ -113,18 +120,6 @@ class DynamicParticleRotation(
buffer.writeString(acceleration.getString())
buffer.writeString(drag.getString())
}
fun getInitialRotation(runtime: MoLangRuntime) = runtime.resolveDouble(startRotation)
fun getInitialAngularVelocity(runtime: MoLangRuntime) = runtime.resolveDouble(speed) / 20
fun getAngularVelocity(runtime: MoLangRuntime): Double {
if (angularVelocity == null) angularVelocity = getInitialAngularVelocity(runtime)
val drag = runtime.resolveDouble(drag)
var acceleration = runtime.resolveDouble(acceleration)
acceleration += -drag * angularVelocity!!
val nextVelocity = angularVelocity!! + acceleration / 20
return nextVelocity
}
}
enum class ParticleRotationType {
@@ -13,29 +13,18 @@ import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.client.effect.SaccharineLogBlockParticlesPacket
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.server.level.ServerLevel
import net.minecraft.sounds.SoundEvents
import net.minecraft.sounds.SoundSource
import net.minecraft.world.entity.Entity
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.level.block.RotatedPillarBlock
import org.joml.Vector3d
class SaccharineLogSlatheredInfluence(val pos: BlockPos? = null) : SpawningInfluence {
companion object {
const val HONEY_DRENCHED_ASPECT = "honey_drenched"
const val SACCHARINE_LOG_SLATHERED_MARKER = "saccharine_log_slathered"
const val HIDDEN_ABILITY_CHANCE = 0.05
const val SAFE_BLOCK_SEARCH_DISTANCE = 10
}
var activated = false
var used = false
val chanceForHA = 0.05
override fun affectSpawnablePosition(spawnablePosition: SpawnablePosition) {
spawnablePosition.markers.add(SACCHARINE_LOG_SLATHERED_MARKER)
@@ -43,93 +32,23 @@ class SaccharineLogSlatheredInfluence(val pos: BlockPos? = null) : SpawningInflu
override fun affectSpawn(action: SpawnAction<*>, entity: Entity) {
if (entity is PokemonEntity) {
if (Math.random() <= HIDDEN_ABILITY_CHANCE) {
if (Math.random() <= chanceForHA) {
FishingSpawnCause.alterHAAttempt(entity)
}
if (!activated) {
if (!used) {
val logPos = pos
val level = action.spawnablePosition.world.level
if (logPos != null) {
val blockState = level.getBlockState(logPos)
if (blockState.block == CobblemonBlocks.SACCHARINE_LOG_SLATHERED) {
val direction = blockState.getValue(HorizontalDirectionalBlock.FACING)
val safePos = attemptSafeMove(level, entity, logPos, direction)
if (safePos != null) entity.moveTo(safePos.toVec3d())
SaccharineLogBlockParticlesPacket(logPos, safePos).sendToPlayersAround(
logPos.x.toDouble(),
logPos.y.toDouble(),
logPos.z.toDouble(),
64.0,
level.dimension()
)
level.playSound(null, logPos, SoundEvents.PLAYER_BURP, SoundSource.NEUTRAL)
val axis = blockState.getValue(RotatedPillarBlock.AXIS)
val newState = CobblemonBlocks.SACCHARINE_LOG.defaultBlockState().setValue(RotatedPillarBlock.AXIS, axis)
level.setBlock(logPos, newState, 3)
}
}
entity.pokemon.forcedAspects += HONEY_DRENCHED_ASPECT
activated = true
used = true
}
}
}
fun attemptSafeMove(level: ServerLevel, entity: PokemonEntity, startPos: BlockPos, direction: Direction): BlockPos? {
val inWater = entity.isInWater
val originalPos = startPos.relative(direction)
val maxRadius = SAFE_BLOCK_SEARCH_DISTANCE
// For each radius from 0 up to max, increase radius after each full downward traversal
for (radius in 0..maxRadius) {
var currentPos = originalPos
repeat(SAFE_BLOCK_SEARCH_DISTANCE) {
val safeSpot = searchInHalfCircle(level, entity, currentPos, direction, radius, inWater)
if (safeSpot != null) {
return safeSpot
}
currentPos = currentPos.below()
}
}
return null
}
fun searchInHalfCircle(level: ServerLevel, entity: PokemonEntity, centerPos: BlockPos, direction: Direction, searchRadius: Int, allowWater: Boolean): BlockPos? {
for (dx in -searchRadius..searchRadius) {
for (dz in -searchRadius..searchRadius) {
if (dx * dx + dz * dz <= searchRadius * searchRadius) {
val candidatePos = centerPos.offset(dx, 0, dz)
val candidateVec = Vector3d(dx.toDouble(), 0.0, dz.toDouble())
val directionVec = Vector3d(direction.stepX.toDouble(), direction.stepY.toDouble(), direction.stepZ.toDouble())
// Check if position is in front of the direction
val dot = candidateVec.dot(directionVec)
if (dot >= 0) {
// Check collision at candidate position
val currentBox = entity.boundingBox.move(
candidatePos.x - entity.x,
candidatePos.y - entity.y,
candidatePos.z - entity.z
)
if (level.noCollision(entity, currentBox)) {
// Check block below
val blockBelowPos = candidatePos.below()
val blockState = level.getBlockState(blockBelowPos)
val isWater = blockState.block == Blocks.WATER
if (allowWater) {
if (isWater) return candidatePos
} else {
if (!blockState.isAir) return candidatePos
}
}
}
}
}
}
return null
}
}
@@ -30,12 +30,6 @@ object CobblemonBiomeTags {
val IS_TEMPERATE = create("is_temperate")
@JvmField
val IS_WINTER = create("has_season/winter")
@JvmField
val IS_SWAMP = create("is_swamp")
@JvmField
val IS_PLAINS = create("is_plains")
@JvmField
val IS_BEACH = create("is_beach")
// Has Feature tags
@JvmField
@@ -46,8 +40,6 @@ object CobblemonBiomeTags {
val HAS_APRICORNS_SPARSE = create("has_feature/apricorns_sparse")
@JvmField
val HAS_REVIVAL_HERBS = create("has_feature/revival_herbs")
@JvmField
val ALLOWED_BIOMES_SACCHARINE_TREE = create("has_feature/saccharine_tree")
// Has Ore tags
@JvmField
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
@@ -324,28 +323,6 @@ object BattleBuilder {
val npcParty = npcEntity.getPartyForChallenge(listOf(player))
val errors = ErroredBattleStart()
val adjustLevel = battleFormat.adjustLevel
val playerPartyStores = mutableListOf<PlayerPartyStore>()
val npcPartyStores = mutableListOf<NPCPartyStore>()
if (adjustLevel > 0) {
val tempStorePlayer = PlayerPartyStore(player.uuid)
playerTeam.forEachIndexed { index, battlePokemon ->
battlePokemon.effectedPokemon.level = adjustLevel
battlePokemon.effectedPokemon.heal()
tempStorePlayer.set(index, battlePokemon.effectedPokemon)
}
playerPartyStores.add(tempStorePlayer)
val tempStoreNpc = NPCPartyStore(npcEntity)
npcParty!!.forEachIndexed { index, battlePokemon ->
battlePokemon.level = adjustLevel
battlePokemon.heal()
tempStoreNpc.set(index, battlePokemon)
}
npcPartyStores.add(npcParty)
}
if (playerActor.pokemonList.size < battleFormat.battleType.slotsPerActor) {
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
actorEntity = player,
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.battles
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.getStringOrNull
import com.cobblemon.mod.common.util.readSizedInt
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeSizedInt
@@ -30,44 +29,19 @@ data class BattleFormat(
var adjustLevel: Int = -1, // Stop gap rule before a more general system for rules enforced by Cobblemon is implemented.
) {
companion object {
fun setBattleRules(
battleFormat: BattleFormat,
rules: Set<String>? = null
): BattleFormat {
val filteredRules = rules?.filter { it.isNotBlank() }?.toSet()
val ruleValues = filteredRules?.mapNotNull { ruleName ->
BattleRules::class.members
.filterIsInstance<kotlin.reflect.KProperty1<BattleRules, String>>()
.find { it.name == ruleName }
?.getter
?.call()
}.orEmpty()
return battleFormat.copy(
ruleSet = filteredRules ?: (battleFormat.ruleSet + ruleValues)
)
}
fun fromFormatIdentifier(id: String): BattleFormat = when (id) {
"single_battle", "single", "singles" -> BattleFormat.GEN_9_SINGLES
"double_battle", "double", "doubles" -> BattleFormat.GEN_9_DOUBLES
"triple_battle", "triple", "triples" -> BattleFormat.GEN_9_TRIPLES
else -> BattleFormat.GEN_9_SINGLES
}
val GEN_9_SINGLES = BattleFormat(
battleType = BattleTypes.SINGLES,
ruleSet = mutableSetOf(BattleRules.OBTAINABLE, BattleRules.PAST, BattleRules.UNOBTAINABLE)
ruleSet = setOf(BattleRules.OBTAINABLE, BattleRules.PAST, BattleRules.UNOBTAINABLE)
)
val GEN_9_DOUBLES = BattleFormat(
battleType = BattleTypes.DOUBLES,
ruleSet = mutableSetOf(BattleRules.OBTAINABLE, BattleRules.PAST, BattleRules.UNOBTAINABLE)
ruleSet = setOf(BattleRules.OBTAINABLE, BattleRules.PAST, BattleRules.UNOBTAINABLE)
)
val GEN_9_TRIPLES = BattleFormat(
battleType = BattleTypes.TRIPLES,
ruleSet = mutableSetOf(BattleRules.OBTAINABLE, BattleRules.PAST, BattleRules.UNOBTAINABLE)
ruleSet = setOf(BattleRules.OBTAINABLE, BattleRules.PAST, BattleRules.UNOBTAINABLE)
)
val GEN_9_MULTI = BattleFormat(
@@ -8,10 +8,8 @@
package com.cobblemon.mod.common.battles.ai
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.ai.BattleAI
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.DefaultActionResponse
import com.cobblemon.mod.common.battles.MoveActionResponse
import com.cobblemon.mod.common.battles.PassActionResponse
@@ -28,8 +26,6 @@ import com.cobblemon.mod.common.battles.SwitchActionResponse
class RandomBattleAI : BattleAI {
override fun choose(
activeBattlePokemon: ActiveBattlePokemon,
battle: PokemonBattle,
aiSide: BattleSide,
moveset: ShowdownMoveset?,
forceSwitch: Boolean
): ShowdownActionResponse {
File diff suppressed because it is too large. Load diff
@@ -1,295 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.battles.ai.strongBattleAI
import com.cobblemon.mod.common.api.abilities.Ability
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
/**
* Everything in this class will be eventually getting replaced by a dynamic datadriven approach
* when moves/abilities/types become data-packable or at least queryable from showdown
*/
object AIUtility {
fun getDamageMultiplier(attackerType: ElementalType, defenderType: ElementalType): Double {
return typeEffectiveness[attackerType]?.get(defenderType) ?: 1.0
}
val typeEffectiveness: Map<ElementalType, Map<ElementalType, Double>> = mapOf(
ElementalTypes.NORMAL to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 1.0, ElementalTypes.WATER to 1.0, ElementalTypes.ELECTRIC to 1.0, ElementalTypes.GRASS to 1.0,
ElementalTypes.ICE to 1.0, ElementalTypes.FIGHTING to 1.0, ElementalTypes.POISON to 1.0, ElementalTypes.GROUND to 1.0, ElementalTypes.FLYING to 1.0,
ElementalTypes.PSYCHIC to 1.0, ElementalTypes.BUG to 1.0, ElementalTypes.ROCK to 0.5, ElementalTypes.GHOST to 0.0, ElementalTypes.DRAGON to 1.0,
ElementalTypes.DARK to 1.0, ElementalTypes.STEEL to 0.5, ElementalTypes.FAIRY to 1.0
),
ElementalTypes.FIRE to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 0.5, ElementalTypes.WATER to 0.5, ElementalTypes.ELECTRIC to 1.0, ElementalTypes.GRASS to 2.0,
ElementalTypes.ICE to 2.0, ElementalTypes.FIGHTING to 1.0, ElementalTypes.POISON to 1.0, ElementalTypes.GROUND to 1.0, ElementalTypes.FLYING to 1.0,
ElementalTypes.PSYCHIC to 1.0, ElementalTypes.BUG to 2.0, ElementalTypes.ROCK to 0.5, ElementalTypes.GHOST to 1.0, ElementalTypes.DRAGON to 0.5,
ElementalTypes.DARK to 1.0, ElementalTypes.STEEL to 2.0, ElementalTypes.FAIRY to 1.0
),
ElementalTypes.WATER to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 2.0, ElementalTypes.WATER to 0.5, ElementalTypes.ELECTRIC to 1.0, ElementalTypes.GRASS to 0.5,
ElementalTypes.ICE to 1.0, ElementalTypes.FIGHTING to 1.0, ElementalTypes.POISON to 1.0, ElementalTypes.GROUND to 2.0, ElementalTypes.FLYING to 1.0,
ElementalTypes.PSYCHIC to 1.0, ElementalTypes.BUG to 1.0, ElementalTypes.ROCK to 2.0, ElementalTypes.GHOST to 1.0, ElementalTypes.DRAGON to 0.5,
ElementalTypes.DARK to 1.0, ElementalTypes.STEEL to 1.0, ElementalTypes.FAIRY to 1.0
),
ElementalTypes.ELECTRIC to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 1.0, ElementalTypes.WATER to 2.0, ElementalTypes.ELECTRIC to 0.5, ElementalTypes.GRASS to 0.5,
ElementalTypes.ICE to 1.0, ElementalTypes.FIGHTING to 1.0, ElementalTypes.POISON to 1.0, ElementalTypes.GROUND to 0.0, ElementalTypes.FLYING to 2.0,
ElementalTypes.PSYCHIC to 1.0, ElementalTypes.BUG to 1.0, ElementalTypes.ROCK to 1.0, ElementalTypes.GHOST to 1.0, ElementalTypes.DRAGON to 0.5,
ElementalTypes.DARK to 1.0, ElementalTypes.STEEL to 1.0, ElementalTypes.FAIRY to 1.0
),
ElementalTypes.GRASS to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 0.5, ElementalTypes.WATER to 2.0, ElementalTypes.ELECTRIC to 1.0, ElementalTypes.GRASS to 0.5,
ElementalTypes.ICE to 1.0, ElementalTypes.FIGHTING to 1.0, ElementalTypes.POISON to 0.5, ElementalTypes.GROUND to 2.0, ElementalTypes.FLYING to 0.5,
ElementalTypes.PSYCHIC to 1.0, ElementalTypes.BUG to 0.5, ElementalTypes.ROCK to 2.0, ElementalTypes.GHOST to 1.0, ElementalTypes.DRAGON to 0.5,
ElementalTypes.DARK to 1.0, ElementalTypes.STEEL to 0.5, ElementalTypes.FAIRY to 1.0
),
ElementalTypes.ICE to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 0.5, ElementalTypes.WATER to 0.5, ElementalTypes.ELECTRIC to 1.0, ElementalTypes.GRASS to 2.0,
ElementalTypes.ICE to 0.5, ElementalTypes.FIGHTING to 1.0, ElementalTypes.POISON to 1.0, ElementalTypes.GROUND to 2.0, ElementalTypes.FLYING to 2.0,
ElementalTypes.PSYCHIC to 1.0, ElementalTypes.BUG to 1.0, ElementalTypes.ROCK to 1.0, ElementalTypes.GHOST to 1.0, ElementalTypes.DRAGON to 2.0,
ElementalTypes.DARK to 1.0, ElementalTypes.STEEL to 0.5, ElementalTypes.FAIRY to 1.0
),
ElementalTypes.FIGHTING to mapOf(
ElementalTypes.NORMAL to 2.0, ElementalTypes.FIRE to 1.0, ElementalTypes.WATER to 1.0, ElementalTypes.ELECTRIC to 1.0, ElementalTypes.GRASS to 1.0,
ElementalTypes.ICE to 2.0, ElementalTypes.FIGHTING to 1.0, ElementalTypes.POISON to 0.5, ElementalTypes.GROUND to 1.0, ElementalTypes.FLYING to 0.5,
ElementalTypes.PSYCHIC to 0.5, ElementalTypes.BUG to 0.5, ElementalTypes.ROCK to 2.0, ElementalTypes.GHOST to 0.0, ElementalTypes.DRAGON to 1.0,
ElementalTypes.DARK to 2.0, ElementalTypes.STEEL to 2.0, ElementalTypes.FAIRY to 0.5
),
ElementalTypes.POISON to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 1.0, ElementalTypes.WATER to 1.0, ElementalTypes.ELECTRIC to 1.0, ElementalTypes.GRASS to 2.0,
ElementalTypes.ICE to 1.0, ElementalTypes.FIGHTING to 1.0, ElementalTypes.POISON to 0.5, ElementalTypes.GROUND to 0.5, ElementalTypes.FLYING to 1.0,
ElementalTypes.PSYCHIC to 1.0, ElementalTypes.BUG to 1.0, ElementalTypes.ROCK to 0.5, ElementalTypes.GHOST to 0.5, ElementalTypes.DRAGON to 1.0,
ElementalTypes.DARK to 1.0, ElementalTypes.STEEL to 0.0, ElementalTypes.FAIRY to 2.0
),
ElementalTypes.GROUND to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 2.0, ElementalTypes.WATER to 1.0, ElementalTypes.ELECTRIC to 2.0, ElementalTypes.GRASS to 0.5,
ElementalTypes.ICE to 1.0, ElementalTypes.FIGHTING to 1.0, ElementalTypes.POISON to 2.0, ElementalTypes.GROUND to 1.0, ElementalTypes.FLYING to 0.0,
ElementalTypes.PSYCHIC to 1.0, ElementalTypes.BUG to 0.5, ElementalTypes.ROCK to 2.0, ElementalTypes.GHOST to 1.0, ElementalTypes.DRAGON to 1.0,
ElementalTypes.DARK to 1.0, ElementalTypes.STEEL to 2.0, ElementalTypes.FAIRY to 1.0
),
ElementalTypes.FLYING to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 1.0, ElementalTypes.WATER to 1.0, ElementalTypes.ELECTRIC to 0.5, ElementalTypes.GRASS to 2.0,
ElementalTypes.ICE to 1.0, ElementalTypes.FIGHTING to 2.0, ElementalTypes.POISON to 1.0, ElementalTypes.GROUND to 1.0, ElementalTypes.FLYING to 1.0,
ElementalTypes.PSYCHIC to 1.0, ElementalTypes.BUG to 2.0, ElementalTypes.ROCK to 0.5, ElementalTypes.GHOST to 1.0, ElementalTypes.DRAGON to 1.0,
ElementalTypes.DARK to 1.0, ElementalTypes.STEEL to 0.5, ElementalTypes.FAIRY to 1.0
),
ElementalTypes.PSYCHIC to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 1.0, ElementalTypes.WATER to 1.0, ElementalTypes.ELECTRIC to 1.0, ElementalTypes.GRASS to 1.0,
ElementalTypes.ICE to 1.0, ElementalTypes.FIGHTING to 2.0, ElementalTypes.POISON to 2.0, ElementalTypes.GROUND to 1.0, ElementalTypes.FLYING to 1.0,
ElementalTypes.PSYCHIC to 0.5, ElementalTypes.BUG to 1.0, ElementalTypes.ROCK to 1.0, ElementalTypes.GHOST to 1.0, ElementalTypes.DRAGON to 1.0,
ElementalTypes.DARK to 0.0, ElementalTypes.STEEL to 0.5, ElementalTypes.FAIRY to 1.0
),
ElementalTypes.BUG to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 0.5, ElementalTypes.WATER to 1.0, ElementalTypes.ELECTRIC to 1.0, ElementalTypes.GRASS to 2.0,
ElementalTypes.ICE to 1.0, ElementalTypes.FIGHTING to 0.5, ElementalTypes.POISON to 0.5, ElementalTypes.GROUND to 1.0, ElementalTypes.FLYING to 0.5,
ElementalTypes.PSYCHIC to 2.0, ElementalTypes.BUG to 1.0, ElementalTypes.ROCK to 1.0, ElementalTypes.GHOST to 0.5, ElementalTypes.DRAGON to 1.0,
ElementalTypes.DARK to 2.0, ElementalTypes.STEEL to 0.5, ElementalTypes.FAIRY to 0.5
),
ElementalTypes.ROCK to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 2.0, ElementalTypes.WATER to 1.0, ElementalTypes.ELECTRIC to 1.0, ElementalTypes.GRASS to 1.0,
ElementalTypes.ICE to 2.0, ElementalTypes.FIGHTING to 0.5, ElementalTypes.POISON to 1.0, ElementalTypes.GROUND to 0.5, ElementalTypes.FLYING to 2.0,
ElementalTypes.PSYCHIC to 1.0, ElementalTypes.BUG to 2.0, ElementalTypes.ROCK to 1.0, ElementalTypes.GHOST to 1.0, ElementalTypes.DRAGON to 1.0,
ElementalTypes.DARK to 1.0, ElementalTypes.STEEL to 0.5, ElementalTypes.FAIRY to 1.0
),
ElementalTypes.GHOST to mapOf(
ElementalTypes.NORMAL to 0.0, ElementalTypes.FIRE to 1.0, ElementalTypes.WATER to 1.0, ElementalTypes.ELECTRIC to 1.0, ElementalTypes.GRASS to 1.0,
ElementalTypes.ICE to 1.0, ElementalTypes.FIGHTING to 1.0, ElementalTypes.POISON to 1.0, ElementalTypes.GROUND to 1.0, ElementalTypes.FLYING to 1.0,
ElementalTypes.PSYCHIC to 2.0, ElementalTypes.BUG to 1.0, ElementalTypes.ROCK to 1.0, ElementalTypes.GHOST to 2.0, ElementalTypes.DRAGON to 1.0,
ElementalTypes.DARK to 0.5, ElementalTypes.STEEL to 1.0, ElementalTypes.FAIRY to 1.0
),
ElementalTypes.DRAGON to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 1.0, ElementalTypes.WATER to 1.0, ElementalTypes.ELECTRIC to 1.0, ElementalTypes.GRASS to 1.0,
ElementalTypes.ICE to 1.0, ElementalTypes.FIGHTING to 1.0, ElementalTypes.POISON to 1.0, ElementalTypes.GROUND to 1.0, ElementalTypes.FLYING to 1.0,
ElementalTypes.PSYCHIC to 1.0, ElementalTypes.BUG to 1.0, ElementalTypes.ROCK to 1.0, ElementalTypes.GHOST to 1.0, ElementalTypes.DRAGON to 2.0,
ElementalTypes.DARK to 1.0, ElementalTypes.STEEL to 0.5, ElementalTypes.FAIRY to 0.0
),
ElementalTypes.DARK to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 1.0, ElementalTypes.WATER to 1.0, ElementalTypes.ELECTRIC to 1.0, ElementalTypes.GRASS to 1.0,
ElementalTypes.ICE to 1.0, ElementalTypes.FIGHTING to 0.5, ElementalTypes.POISON to 1.0, ElementalTypes.GROUND to 1.0, ElementalTypes.FLYING to 1.0,
ElementalTypes.PSYCHIC to 2.0, ElementalTypes.BUG to 1.0, ElementalTypes.ROCK to 1.0, ElementalTypes.GHOST to 2.0, ElementalTypes.DRAGON to 1.0,
ElementalTypes.DARK to 0.5, ElementalTypes.STEEL to 1.0, ElementalTypes.FAIRY to 0.5
),
ElementalTypes.STEEL to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 0.5, ElementalTypes.WATER to 0.5, ElementalTypes.ELECTRIC to 0.5, ElementalTypes.GRASS to 1.0,
ElementalTypes.ICE to 2.0, ElementalTypes.FIGHTING to 1.0, ElementalTypes.POISON to 1.0, ElementalTypes.GROUND to 1.0, ElementalTypes.FLYING to 1.0,
ElementalTypes.PSYCHIC to 1.0, ElementalTypes.BUG to 1.0, ElementalTypes.ROCK to 2.0, ElementalTypes.GHOST to 1.0, ElementalTypes.DRAGON to 1.0,
ElementalTypes.DARK to 1.0, ElementalTypes.STEEL to 0.5, ElementalTypes.FAIRY to 2.0
),
ElementalTypes.FAIRY to mapOf(
ElementalTypes.NORMAL to 1.0, ElementalTypes.FIRE to 0.5, ElementalTypes.WATER to 1.0, ElementalTypes.ELECTRIC to 1.0, ElementalTypes.GRASS to 1.0,
ElementalTypes.ICE to 1.0, ElementalTypes.FIGHTING to 2.0, ElementalTypes.POISON to 0.5, ElementalTypes.GROUND to 1.0, ElementalTypes.FLYING to 1.0,
ElementalTypes.PSYCHIC to 1.0, ElementalTypes.BUG to 1.0, ElementalTypes.ROCK to 1.0, ElementalTypes.GHOST to 1.0, ElementalTypes.DRAGON to 2.0,
ElementalTypes.DARK to 2.0, ElementalTypes.STEEL to 0.5, ElementalTypes.FAIRY to 1.0
)
)
val multiHitMoves: Map<String, Pair<Int, Int>> = mapOf(
// 2 - 5 hit moves
"armthrust" to Pair(2, 5),
"barrage" to Pair(2, 5),
"bonerush" to Pair(2, 5),
"bulletseed" to Pair(2, 5),
"cometpunch" to Pair(2, 5),
"doubleslap" to Pair(2, 5),
"furyattack" to Pair(2, 5),
"furyswipes" to Pair(2, 5),
"iciclespear" to Pair(2, 5),
"pinmissile" to Pair(2, 5),
"rockblast" to Pair(2, 5),
"scaleshot" to Pair(2, 5),
"spikecannon" to Pair(2, 5),
"tailslap" to Pair(2, 5),
"watershuriken" to Pair(2, 5),
// fixed hit count
"bonemerang" to Pair(2, 2),
"doublehit" to Pair(2, 2),
"doubleironbash" to Pair(2, 2),
"doublekick" to Pair(2, 2),
"dragondarts" to Pair(2, 2),
"dualchop" to Pair(2, 2),
"dualwingbeat" to Pair(2, 2),
"geargrind" to Pair(2, 2),
"twinbeam" to Pair(2, 2),
"twineedle" to Pair(2, 2),
"suringstrikes" to Pair(3, 3),
"tripledive" to Pair(3, 3),
"watershuriken" to Pair(3, 3),
// accuracy based multi-hit moves
"tripleaxel" to Pair(1, 3),
"triplekick" to Pair(1, 3),
"populationbomb" to Pair(1, 10)
)
val statusMoves: Map<String, String> = mapOf(
"willowisp" to Statuses.BURN.showdownName,
"scald" to Statuses.BURN.showdownName,
"scorchingsands" to Statuses.BURN.showdownName,
"glare" to Statuses.PARALYSIS.showdownName,
"nuzzle" to Statuses.PARALYSIS.showdownName,
"stunspore" to Statuses.PARALYSIS.showdownName,
"thunderwave" to Statuses.PARALYSIS.showdownName,
"darkvoid" to Statuses.SLEEP.showdownName,
"hypnosis" to Statuses.SLEEP.showdownName,
"lovelykiss" to Statuses.SLEEP.showdownName,
"relicsong" to Statuses.SLEEP.showdownName,
"sing" to Statuses.SLEEP.showdownName,
"sleeppower" to Statuses.SLEEP.showdownName,
"spore" to Statuses.SLEEP.showdownName,
"yawn" to Statuses.SLEEP.showdownName,
"chatter" to Statuses.CONFUSE.showdownName,
"confuseray" to Statuses.CONFUSE.showdownName,
"dynamicpunch" to Statuses.CONFUSE.showdownName,
"flatter" to Statuses.CONFUSE.showdownName,
"supersonic" to Statuses.CONFUSE.showdownName,
"swagger" to Statuses.CONFUSE.showdownName,
"sweetkiss" to Statuses.CONFUSE.showdownName,
"teeterdance" to Statuses.CONFUSE.showdownName,
"poisongas" to Statuses.POISON.showdownName,
"poisonpowder" to Statuses.POISON.showdownName,
"toxic" to Statuses.POISON_BADLY.showdownName,
"toxicthread" to Statuses.POISON.showdownName,
"curse" to "cursed",
"leechseed" to "leech"
)
val boostFromMoves: Map<String, Map<Stat, Int>> = mapOf(
"bellydrum" to mapOf(Stats.ATTACK to 6),
"bulkup" to mapOf(Stats.ATTACK to 1, Stats.DEFENCE to 1),
"clangoroussoul" to mapOf(Stats.ATTACK to 1, Stats.DEFENCE to 1, Stats.SPECIAL_ATTACK to 1, Stats.SPECIAL_DEFENCE to 1, Stats.SPEED to 1),
"coil" to mapOf(Stats.ATTACK to 1, Stats.DEFENCE to 1, Stats.ACCURACY to 1),
"dragondance" to mapOf(Stats.ATTACK to 1, Stats.SPEED to 1),
"extremeevoboost" to mapOf(Stats.ATTACK to 2, Stats.DEFENCE to 2, Stats.SPECIAL_ATTACK to 2, Stats.SPECIAL_DEFENCE to 2, Stats.SPEED to 2),
"clangoroussoulblaze" to mapOf(Stats.ATTACK to 1, Stats.DEFENCE to 1, Stats.SPECIAL_ATTACK to 1, Stats.SPECIAL_DEFENCE to 1, Stats.SPEED to 1),
"filletaway" to mapOf(Stats.ATTACK to 2, Stats.SPECIAL_ATTACK to 2, Stats.SPEED to 2),
"honeclaws" to mapOf(Stats.ATTACK to 1, Stats.ACCURACY to 1),
"noretreat" to mapOf(Stats.ATTACK to 1, Stats.DEFENCE to 1, Stats.SPECIAL_ATTACK to 1, Stats.SPECIAL_DEFENCE to 1, Stats.SPEED to 1),
"shellsmash" to mapOf(Stats.ATTACK to 2, Stats.DEFENCE to -1, Stats.SPECIAL_ATTACK to 2, Stats.SPECIAL_DEFENCE to -1, Stats.SPEED to 2),
"shiftgear" to mapOf(Stats.ATTACK to 1, Stats.SPEED to 2),
"swordsdance" to mapOf(Stats.ATTACK to 2),
"tidyup" to mapOf(Stats.ATTACK to 1, Stats.SPEED to 1),
"victorydance" to mapOf(Stats.ATTACK to 1, Stats.DEFENCE to 1, Stats.SPEED to 1),
"acidarmor" to mapOf(Stats.DEFENCE to 2),
"barrier" to mapOf(Stats.DEFENCE to 2),
"cottonguard" to mapOf(Stats.DEFENCE to 3),
"defensecurl" to mapOf(Stats.DEFENCE to 1),
"irondefense" to mapOf(Stats.DEFENCE to 2),
"shelter" to mapOf(Stats.DEFENCE to 2, Stats.EVASION to 1),
"stockpile" to mapOf(Stats.DEFENCE to 1, Stats.SPECIAL_DEFENCE to 1),
"stuffcheeks" to mapOf(Stats.DEFENCE to 2),
"amnesia" to mapOf(Stats.SPECIAL_DEFENCE to 2),
"calmmind" to mapOf(Stats.SPECIAL_ATTACK to 1, Stats.SPECIAL_DEFENCE to 1),
"geomancy" to mapOf(Stats.SPECIAL_ATTACK to 2, Stats.SPECIAL_DEFENCE to 2, Stats.SPEED to 2),
"nastyplot" to mapOf(Stats.SPECIAL_ATTACK to 2),
"quiverdance" to mapOf(Stats.SPECIAL_ATTACK to 1, Stats.SPECIAL_DEFENCE to 1, Stats.SPEED to 1),
"tailglow" to mapOf(Stats.SPECIAL_ATTACK to 3),
"takeheart" to mapOf(Stats.SPECIAL_ATTACK to 1, Stats.SPECIAL_DEFENCE to 1),
"agility" to mapOf(Stats.SPEED to 2),
"autotomize" to mapOf(Stats.SPEED to 2),
"rockpolish" to mapOf(Stats.SPEED to 2),
"curse" to mapOf(Stats.ATTACK to 1, Stats.DEFENCE to 1, Stats.SPEED to -1),
"minimize" to mapOf(Stats.EVASION to 2)
)
val entryHazards = listOf("spikes", "stealthrock", "stickyweb", "toxicspikes")
val antiHazardsMoves = listOf("rapidspin", "defog", "tidyup")
val antiBoostMoves = listOf("slearsmog","haze")
val pivotMoves = listOf("uturn", "flipturn", "partingshot", "batonpass", "shedtail", "voltswitch", "teleport")
val setupMoves = setOf("tailwind", "trickroom", "auroraveil", "lightscreen", "reflect")
val selfRecoveryMoves = listOf("healorder", "milkdrink", "recover", "rest", "roost", "slackoff", "softboiled")
val weatherSetupMoves = mapOf(
"chillyreception" to "Snow",
"hail" to "Hail",
"raindance" to "RainDance",
"sandstorm" to "Sandstorm",
"snowscape" to "Snow",
"sunnyday" to "SunnyDay"
)
val accuracyLoweringMoves = setOf("flash", "kinesis", "leaftornado", "mirrorshot", "mudbomb", "mudslap", "muddywater", "nightgaze", "octazooka", "sandattack", "secretpower", "smokescreen")
val protectMoves = setOf("protect", "banefulbunker", "obstruct", "craftyshield", "detect", "quickguard", "spikyshield", "silktrap")
val statusImmunityMap = mapOf(
Statuses.BURN.showdownName to (setOf("fire") to setOf("waterbubble", "waterveil", "flareboost", "guts", "magicguard")),
Statuses.PARALYSIS.showdownName to (setOf("electric") to setOf("limber", "guts")),
Statuses.SLEEP.showdownName to (setOf("grass") to setOf("insomnia", "sweetveil")),
Statuses.CONFUSE.showdownName to (setOf("fire") to setOf("owntempo", "oblivious")),
Statuses.POISON.showdownName to (setOf("poison", "steel") to setOf("immunity", "poisonheal", "guts", "magicguard")),
Statuses.POISON_BADLY.showdownName to (setOf("poison", "steel") to setOf("immunity", "poisonheal", "guts", "magicguard")),
"cursed" to (setOf("ghost") to setOf("magicguard")),
"leech" to (setOf("grass") to setOf("liquidooze", "magicguard")),
)
fun canAffectWithStatus(status: String, types: Iterable<ElementalType>, ability: Ability? = null): Boolean {
val (typing, abilities) = statusImmunityMap[status] ?: return true // custom status? *shrug*
return types.none { it.name in typing } && ability?.name !in abilities
}
val typeImmuneAbilities = mapOf(
"lightingrod" to ElementalTypes.ELECTRIC,
"flashfire" to ElementalTypes.FIRE,
"levitate" to ElementalTypes.GROUND,
"sapsipper" to ElementalTypes.GRASS,
"motordrive" to ElementalTypes.ELECTRIC,
"stormdrain" to ElementalTypes.WATER,
"voltabsorb" to ElementalTypes.ELECTRIC,
"waterabsorb" to ElementalTypes.WATER,
"immunity" to ElementalTypes.POISON,
"eartheater" to ElementalTypes.GROUND
)
}
@@ -1,221 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.battles.ai.strongBattleAI
import com.cobblemon.mod.common.api.abilities.Ability
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.moves.Move
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.Species
import java.util.UUID
import kotlin.collections.firstOrNull
import kotlin.collections.getOrDefault
import kotlin.collections.groupingBy
import kotlin.collections.lastOrNull
import kotlin.collections.map
class ActiveTracker {
var currentRoom: String? = null
var currentTerrain: String? = null
var currentWeather: String? = null
val alliedSide: TrackerSide = TrackerSide()
val opponentSide: TrackerSide = TrackerSide()
val isInitialized: Boolean
get() = alliedSide.actors.isNotEmpty() && opponentSide.actors.isNotEmpty()
fun initialize(alliedSide: BattleSide) {
this.alliedSide.actors.clear()
this.alliedSide.actors.addAll(alliedSide.actors.map { TrackerActor.initializeAlly(it) })
this.opponentSide.actors.clear()
this.opponentSide.actors.addAll(alliedSide.getOppositeSide().actors.map { TrackerActor.initializeOpponent(it) })
}
fun updateActiveState(alliedSide: BattleSide) {
this.alliedSide.updateAlliedActiveState(alliedSide)
this.opponentSide.updateOpponentActiveState(alliedSide.getOppositeSide())
}
}
class TrackerSide {
var screenCondition: String? = null
var tailwindCondition: String? = null
var sideHazards: List<String> = emptyList()
val actors : MutableList<TrackerActor> = mutableListOf()
val activePokemon: MutableList<TrackerPokemon>
get() = actors.flatMap { it.activePokemon }.toMutableList()
fun updateAlliedActiveState(side: BattleSide) {
actors.associateWith { side.actors.first { actor -> actor.uuid == it.uuid} }
.forEach { (trackerActor, actor) -> trackerActor.updateAlliedActivePokemon(actor)}
val activePokemon = side.activePokemon.mapNotNull { it.battlePokemon }
for (pokemon in activePokemon) {
val trackMon = this.activePokemon.first { it.id == pokemon.uuid }
val boostPerStat = pokemon.contextManager.get(BattleContext.Type.BOOST)?.groupingBy { it.id }
?.eachCount()?.mapKeys { Stats.getStat(it.key) } ?: emptyMap()
val unboostPerStat = pokemon.contextManager.get(BattleContext.Type.UNBOOST)?.groupingBy { it.id }
?.eachCount()?.mapKeys { Stats.getStat(it.key) } ?: emptyMap()
trackMon.boosts.putAll(Stats.ALL.filterNot {it == Stats.HP}
.associateWith { stat -> boostPerStat.getOrDefault(stat, 0) - unboostPerStat.getOrDefault(stat, 0) })
trackMon.currentStatus = pokemon.effectedPokemon.status?.status?.showdownName ?: pokemon.contextManager.get(BattleContext.Type.STATUS)?.lastOrNull()?.id
trackMon.currentVolatile = pokemon.contextManager.get(BattleContext.Type.VOLATILE)?.lastOrNull()?.id
trackMon.pokemon = pokemon.effectedPokemon
trackMon.species = pokemon.effectedPokemon.species
trackMon.form = pokemon.effectedPokemon.form
trackMon.currentHp = pokemon.maxHealth
trackMon.currentHpPercent = pokemon.health.toDouble() / pokemon.maxHealth.toDouble()
trackMon.currentAbility = pokemon.effectedPokemon.ability
trackMon.moves = pokemon.effectedPokemon.moveSet.toList()
}
//TODO count turns where applicable?
sideHazards = side.contextManager.get(BattleContext.Type.HAZARD)?.map { it.id } ?: emptyList()
tailwindCondition = side.contextManager.get(BattleContext.Type.TAILWIND)?.firstOrNull()?.id
screenCondition = side.contextManager.get(BattleContext.Type.SCREEN)?.firstOrNull()?.id
}
fun updateOpponentActiveState(side: BattleSide) {
actors.associateWith { side.actors.first { actor -> actor.uuid == it.uuid} }
.forEach { (trackerActor, actor) -> trackerActor.updateOpponentActivePokemon(actor)}
val activePokemon = side.activePokemon.mapNotNull { it.battlePokemon }
for (pokemon in activePokemon) {
val trackMon = this.activePokemon.first { it.id == pokemon.uuid }
val boostPerStat = pokemon.contextManager.get(BattleContext.Type.BOOST)?.groupingBy { it.id }
?.eachCount()?.mapKeys { Stats.getStat(it.key) } ?: emptyMap()
val unboostPerStat = pokemon.contextManager.get(BattleContext.Type.UNBOOST)?.groupingBy { it.id }
?.eachCount()?.mapKeys { Stats.getStat(it.key) } ?: emptyMap()
trackMon.boosts.putAll(Stats.ALL.filterNot {it == Stats.HP}
.associateWith { stat -> boostPerStat.getOrDefault(stat, 0) - unboostPerStat.getOrDefault(stat, 0) })
trackMon.currentStatus = pokemon.effectedPokemon.status?.status?.showdownName ?: pokemon.contextManager.get(BattleContext.Type.STATUS)?.lastOrNull()?.id
trackMon.currentVolatile = pokemon.contextManager.get(BattleContext.Type.VOLATILE)?.lastOrNull()?.id
val resetAbility = trackMon.species != (pokemon.entity?.exposedSpecies ?: pokemon.effectedPokemon.species) ||
trackMon.form != (pokemon.entity?.exposedForm ?: pokemon.effectedPokemon.form)
trackMon.species = pokemon.entity?.exposedSpecies ?: pokemon.effectedPokemon.species
trackMon.form = pokemon.entity?.exposedForm ?: pokemon.effectedPokemon.form
if (trackMon.currentAbility != null || resetAbility) {
trackMon.currentAbility = (trackMon.form?.abilities ?: trackMon.species!!.abilities).first().template.create() //educated guess, abilities of a pokemon are known
}
trackMon.currentHpPercent = pokemon.health.toDouble() / pokemon.maxHealth.toDouble()
}
//TODO count turns where applicable?
sideHazards = side.contextManager.get(BattleContext.Type.HAZARD)?.map { it.id } ?: emptyList()
tailwindCondition = side.contextManager.get(BattleContext.Type.TAILWIND)?.firstOrNull()?.id
screenCondition = side.contextManager.get(BattleContext.Type.SCREEN)?.firstOrNull()?.id
}
}
// Tracker Actor with a Party of Tracker Pokemon in a Party
class TrackerActor(val uuid: UUID) {
val party: MutableList<TrackerPokemon> = mutableListOf()
val activePokemon: MutableList<TrackerPokemon> = mutableListOf()
val remainingMons: Int
get() = (party + activePokemon).count { (it.currentHp?.toDouble() ?: it.currentHpPercent) > 0 }
fun updateAlliedActivePokemon(actor: BattleActor) {
actor.activePokemon.filter { newActive -> activePokemon.none { it.id == newActive.battlePokemon?.uuid } }.forEach {
val active = party.firstOrNull { trackMon -> trackMon.id == it.battlePokemon?.uuid } ?: return@forEach
party.remove(active)
activePokemon.add(active)
}
val swappedOut = activePokemon.filter { trackMon -> actor.activePokemon.none { it.battlePokemon?.uuid == trackMon.id } }
swappedOut.forEach {
activePokemon.remove(it)
party.add(it)
}
}
fun updateOpponentActivePokemon(actor: BattleActor) {
actor.activePokemon.filter { newActive -> activePokemon.none { it.id == newActive.battlePokemon?.uuid } }.forEach {
val active = party.firstOrNull { trackMon -> trackMon.id == it.battlePokemon?.uuid } ?: return@forEach
party.remove(active)
activePokemon.add(active)
}
val swappedOut = activePokemon.filter { trackMon -> actor.activePokemon.none { it.battlePokemon?.uuid == trackMon.id } }
swappedOut.forEach {
activePokemon.remove(it)
party.add(it)
}
}
companion object {
fun initializeAlly(actor: BattleActor) : TrackerActor {
val trackerActor = TrackerActor(actor.uuid)
trackerActor.party.clear()
//we assume that AI knows it all about our allies
trackerActor.party.addAll(actor.pokemonList.map { createAllied(it) })
actor.activePokemon.forEach {
val active = trackerActor.party.first { trackMon -> trackMon.id == it.battlePokemon!!.uuid }
trackerActor.party.remove(active)
trackerActor.activePokemon.add(active)
}
return trackerActor
}
fun initializeOpponent(actor: BattleActor) : TrackerActor {
val trackerActor = TrackerActor(actor.uuid)
trackerActor.party.clear()
//only know basics of what we see right now
trackerActor.party.addAll(actor.pokemonList.map { createOpposing(it) })
actor.activePokemon.forEach {
val active = trackerActor.party.first { trackMon -> trackMon.id == it.battlePokemon!!.uuid }
trackerActor.party.remove(active)
trackerActor.activePokemon.add(active)
}
return trackerActor
}
}
}
// Tracker Pokemon within the Party
data class TrackerPokemon(
var id: UUID? = null,
var pokemon: Pokemon? = null,
var species: Species? = null,
var form: FormData? = null,
var currentHp: Int? = null,
var currentHpPercent: Double = 0.0,
var boosts: MutableMap<Stat, Int> = mutableMapOf(),
var currentVolatile: String? = null,
var currentStatus: String? = null,
var currentAbility: Ability? = null, //TODO PICK UP FROM BATTLE ACTIVATION
var moves: List<Move> = listOf(), //TODO PICK UP FROM BATTLE USEAGE
var firstTurn: Boolean = true,
var protectCount: Int = 0 //TODO MAYBE PICK UP FROM BATTLE USAGE
)
fun createAllied(pokemon: BattlePokemon) : TrackerPokemon {
val trackerMon = TrackerPokemon()
trackerMon.id = pokemon.uuid
trackerMon.pokemon = pokemon.effectedPokemon
trackerMon.species = pokemon.effectedPokemon.species
trackerMon.form = pokemon.effectedPokemon.form
trackerMon.currentHp = pokemon.maxHealth
trackerMon.currentHpPercent = pokemon.health.toDouble() / pokemon.maxHealth.toDouble()
trackerMon.currentAbility = pokemon.effectedPokemon.ability
trackerMon.moves = pokemon.effectedPokemon.moveSet.toList()
return trackerMon
}
fun createOpposing(pokemon: BattlePokemon) : TrackerPokemon {
val trackerMon = TrackerPokemon()
trackerMon.id = pokemon.uuid //mainly used as unique reference
//everything else gets set on a "seen" basis
return trackerMon
}
@@ -60,10 +60,10 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
)
val AGE_TO_SHAPE_TOP = arrayOf(
box(1.0, 0.0, 1.0, 15.0, 1.0, 15.0), // Stage 0
box(1.0, 0.0, 1.0, 15.0, 1.0, 15.0), // Stage 1
box(1.0, 0.0, 1.0, 15.0, 1.0, 15.0), // Stage 2
box(1.0, 0.0, 1.0, 15.0, 1.0, 15.0), // Stage 3
box(0.0, 0.0, 0.0, 0.0, 0.0, 0.0), // Stage 0
box(0.0, 0.0, 0.0, 0.0, 0.0, 0.0), // Stage 1
box(0.0, 0.0, 0.0, 0.0, 0.0, 0.0), // Stage 2
box(0.0, 0.0, 0.0, 0.0, 0.0, 0.0), // Stage 3
box(0.0, 0.0, 0.0, 16.0, 4.0, 16.0), // Stage 4
box(0.0, 0.0, 0.0, 16.0, 11.0, 16.0), // Stage 5
box(0.0, 0.0, 0.0, 16.0, 15.0, 16.0), // Stage 6
@@ -233,6 +233,17 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
}
}
private fun isWaterNearby(level: LevelReader, pos: BlockPos): Boolean {
val floorBlock = pos.below()
for (i in -1..1) {
for (j in -1..1) {
val fluidState = level.getFluidState(floorBlock.offset(i, 0, j))
if (fluidState.`is`(FluidTags.WATER) && fluidState.isSource) return true
}
}
return false
}
private fun getLowerHalf(level: LevelReader, pos: BlockPos, state: BlockState): Pair<BlockPos, BlockState>? {
if (isLower(state)) {
return Pair(pos, state)
@@ -55,7 +55,7 @@ class RevivalHerbBlock(settings: Properties) : CropBlock(settings), Mulchable {
val floor = world.getBlockState(pos.below())
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.`is`(BlockTags.REPLACEABLE_BY_TREES) || block.`is`(BlockTags.AIR) || block.`is`(this)) && ((state.getValue(IS_WILD) && floor.`is`(BlockTags.DIRT)) || this.mayPlaceOn(floor, world, pos))
return (block.`is`(BlockTags.REPLACEABLE_BY_TREES) || block.`is`(Blocks.AIR) || block.`is`(this)) && ((state.getValue(IS_WILD) && floor.`is`(BlockTags.DIRT)) || this.mayPlaceOn(floor, world, pos))
}
override fun getShape(
@@ -161,19 +161,19 @@ class SaccharineLeafBlock(settings: Properties) : LeavesBlock(settings) {
val particleCount = random.nextInt(3)
if (state.getValue(AGE) == 1) {
if (state.getValue(AGE) == 2) {
repeat(particleCount) {
spawnHoneyParticles(level, pos, state, 0.025F)
spawnHoneyParticles(level, pos, state, 0.75F)
}
} else if (state.getValue(AGE) == 2) {
} else if (state.getValue(AGE) == 1) {
repeat(particleCount) {
spawnHoneyParticles(level, pos, state, 0.05F)
spawnHoneyParticles(level, pos, state, 0.9F)
}
}
}
private fun spawnHoneyParticles(level: Level, pos: BlockPos, state: BlockState, rate: Float) {
if (state.fluidState.isEmpty && (level.random.nextFloat() < rate)) {
if (state.fluidState.isEmpty && !(level.random.nextFloat() < rate)) {
val voxelShape = state.getCollisionShape(level, pos)
val d = voxelShape.max(Direction.Axis.Y)
if (d >= 1.0 && !state.`is`(BlockTags.IMPERMEABLE)) {
@@ -161,16 +161,13 @@ object CobblemonClient {
if (event.client.isPaused) {
return@subscribe
}
val nearbyPokemon = player.level().getEntities(
val nearbyShinies = player.level().getEntities(
player,
AABB.ofSize(player.position(), 16.0, 16.0, 16.0)
) { it is PokemonEntity }
nearbyPokemon?.forEach { entity ->
if (entity is PokemonEntity && !entity.isSilent) {
if (player.isLookingAt(entity) && !player.isSpectator && entity.pokemon.shiny) entity.delegate.spawnShinyParticle(player)
entity.delegate.spawnAspectParticle()
) { it is PokemonEntity && it.pokemon.shiny && !it.isSilent }
nearbyShinies?.firstOrNull { player.isLookingAt(it) && !player.isSpectator }.let {
if (it is PokemonEntity) {
it.delegate.spawnShinyParticle(player)
}
}
}
@@ -17,9 +17,7 @@ import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.aspect.aspectParticleMap
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.aspect.ParticleData
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
import com.cobblemon.mod.common.api.scheduling.afterOnClient
@@ -50,6 +48,7 @@ import net.minecraft.sounds.SoundSource
import net.minecraft.world.InteractionHand
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.Entity.MoveFunction
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec3
import org.joml.Vector3f
@@ -443,37 +442,6 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
}
}
override fun spawnAspectParticle() {
currentAspects = currentEntity.entityData.get(PokemonEntity.ASPECTS)
currentAspects.forEach { aspect ->
aspectParticleMap[aspect]?.let { particleData ->
val random = currentEntity.level().random
when (particleData) {
is ParticleData.SnowstormParticle -> {
val locator = particleData.locators.firstOrNull { this.locatorStates[it] != null } ?: "root"
if (particleData.chance > random.nextDouble()) {
repeat(particleData.amount) {
runtime.resolve("q.particle('${particleData.particle}', '$locator')".asExpressionLike())
}
}
}
is ParticleData.MinecraftParticle -> {
if (particleData.chance > random.nextDouble()) {
val box = currentEntity.boundingBox
repeat(particleData.amount) {
val x = box.minX + (box.maxX - box.minX) * random.nextDouble()
val y = box.minY + (box.maxY - box.minY) * random.nextDouble()
val z = box.minZ + (box.maxZ - box.minZ) * random.nextDouble()
currentEntity.level().addParticle(particleData.particle, x, y, z, 0.0, 0.0, 0.0)
}
}
}
}
}
}
}
fun cry() {
val model = currentModel ?: return
if (cryAnimation != null && (cryAnimation in activeAnimations || cryAnimation == primaryAnimation)) {
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.pokemon.PokemonSortMode
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.storage.pc.search.Search
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.italicise
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.CobblemonResources
@@ -603,11 +602,9 @@ class PCGUI(
scale = SCALE
)
val iv = pokemon.ivs.get(stats[i])
val effectiveIv = pokemon.ivs.getEffectiveBattleIV(stats[i])
drawScaledText(
context = context,
text = if (iv != effectiveIv) effectiveIv.toString().text().italicise() else iv.toString().text(),
text = pokemon.ivs.get(stats[i]).toString().text(),
x = x + 65,
y = y + 139 + (10 * i),
centered = true,
@@ -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.client.net.effect
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.block.SaccharineLogSlatheredBlock
import com.cobblemon.mod.common.net.messages.client.effect.SaccharineLogBlockParticlesPacket
import net.minecraft.client.Minecraft
import net.minecraft.core.particles.BlockParticleOption
import net.minecraft.core.particles.ParticleTypes
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.HorizontalDirectionalBlock
object SaccharineLogBlockParticlesHandler : ClientNetworkPacketHandler<SaccharineLogBlockParticlesPacket> {
override fun handle(packet: SaccharineLogBlockParticlesPacket, client: Minecraft) {
val level = Minecraft.getInstance().level ?: return
val blockState = level.getBlockState(packet.blockPos)
val block = blockState.block
if (block is SaccharineLogSlatheredBlock) {
val direction = blockState.getValue(HorizontalDirectionalBlock.FACING)
block.spawnParticlesAtBlockFace(BlockParticleOption(ParticleTypes.BLOCK, Blocks.HONEY_BLOCK.defaultBlockState()), level, packet.blockPos, direction, 15)
block.spawnParticlesAtBlockFace(ParticleTypes.FALLING_HONEY, level, packet.blockPos, direction, 15)
}
val entityPos = packet.entityPos
if (entityPos != null) {
val random = level.random
repeat(10) {
level.addParticle(
ParticleTypes.POOF,
entityPos.x + random.nextDouble(),
entityPos.y + random.nextDouble(),
entityPos.z + random.nextDouble(),
0.0,
0.0,
0.0
)
}
}
}
}
@@ -60,6 +60,7 @@ class SnowstormParticle(
val sprite = getSpriteFromAtlas()
val particleTextureSheet: ParticleRenderType
var angularVelocity = 0.0
var colliding = false
var texture = storm.effect.particle.texture
@@ -109,8 +110,9 @@ class SnowstormParticle(
init {
setParticleSpeed(initialVelocity.x, initialVelocity.y, initialVelocity.z)
roll = -storm.effect.particle.rotation.getAngle(runtime, 0).toFloat()
roll = -storm.effect.particle.rotation.getInitialRotation(runtime).toFloat()
oRoll = roll
angularVelocity = storm.effect.particle.rotation.getInitialAngularVelocity(runtime)
friction = 1F
lifetime = (runtime.resolveDouble(storm.effect.particle.maxAge) * 20).toInt()
storm.particles.add(this)
@@ -245,8 +247,9 @@ class SnowstormParticle(
setParticleAgeInRuntime()
storm.effect.curves.forEach { it.apply(runtime) }
runtime.execute(storm.effect.particle.updateExpressions)
angularVelocity = storm.effect.particle.rotation.getAngularVelocity(runtime, -roll.toDouble(), angularVelocity) / 20
if (age > lifetime || runtime.resolveBoolean(storm.effect.particle.killExpression)) {
if (age >= lifetime || runtime.resolveBoolean(storm.effect.particle.killExpression)) {
runExpirationEvents()
remove()
return
@@ -258,8 +261,8 @@ class SnowstormParticle(
yd = velocity.y
zd = velocity.z
oRoll = roll
// negative because Bedrock particles are counter-clockwise and Java Edition is clockwise.
roll = -storm.effect.particle.rotation.getAngle(runtime, age).toFloat()
// Subtract because Bedrock particles are counter-clockwise and Java Edition is clockwise.
roll = oRoll - angularVelocity.toFloat()
}
viewDirection = storm.effect.particle.viewDirection.getDirection(
@@ -11,7 +11,12 @@ package com.cobblemon.mod.common.command
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.pokedex.*
import com.cobblemon.mod.common.api.pokedex.CaughtCount
import com.cobblemon.mod.common.api.pokedex.CaughtPercent
import com.cobblemon.mod.common.api.pokedex.Dexes
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.api.pokedex.SeenCount
import com.cobblemon.mod.common.api.pokedex.SeenPercent
import com.cobblemon.mod.common.api.pokedex.def.PokedexDef
import com.cobblemon.mod.common.api.pokedex.def.SimplePokedexDef
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
@@ -31,7 +36,6 @@ import net.minecraft.commands.Commands
import net.minecraft.commands.arguments.EntityArgument
import net.minecraft.commands.arguments.selector.EntitySelector
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
object PokedexCommand {
@@ -43,8 +47,9 @@ object PokedexCommand {
val grantCommandBuilder = Commands.literal(GRANT_NAME).then(
Commands.argument("player", EntityArgument.player())
.then(Commands.literal("all").then(
Commands.argument("dex", DexArgumentType.dex()).executes(::executeGrantAll)
))
Commands.argument("dex", DexArgumentType.dex()).executes(::executeGrantAll)
)
)
.then(Commands.literal("only").then(
Commands.argument("species", SpeciesArgumentType.species()).then(
Commands.argument("form", FormArgumentType.form()).executes(::executeGrantOnly)
@@ -55,12 +60,13 @@ object PokedexCommand {
Commands.argument("player", EntityArgument.player())
.then(Commands.literal("all").then(
Commands.argument("dex", DexArgumentType.dex()).executes(::executeRemoveAll)
))
.then(Commands.literal("only").then(
Commands.argument("species", SpeciesArgumentType.species()).then(
Commands.argument("form", FormArgumentType.form()).executes(::executeRemoveOnly)
)
))
)
)
.then(Commands.literal("only").then(
Commands.argument("species", SpeciesArgumentType.species()).then(
Commands.argument("form", FormArgumentType.form()).executes(::executeRemoveOnly)
)
))
)
commandArgumentBuilder
.permission(CobblemonPermissions.POKEDEX)
@@ -68,8 +74,7 @@ object PokedexCommand {
.then(revokeCommandBuilder)
.then(Commands.literal("printcalculations")
.then(Commands.argument("player", EntityArgument.player())
.executes(::printNationalDexValues)
.then(Commands.literal("all").executes(::printValuesGlobal))
.executes(::printValuesGlobal)
.then(Commands.argument("dex", DexArgumentType.dex())
.executes(::printValues))))
@@ -176,19 +181,7 @@ object PokedexCommand {
var calculators = listOf(SeenCount, CaughtCount, SeenPercent, CaughtPercent)
calculators.forEach {
var value = dex.getGlobalCalculatedValue(it)
val percentString = if (it.outputIsPercentage) "%" else ""
context.source.sendSystemMessage(Component.literal("${it.javaClass.simpleName} result: $value$percentString"))
}
Dexes.dexEntryMap.forEach { (dexId, dexDef) ->
val seenCompletion = dex.getDexCalculatedValue(dexId, SeenPercent)
val caughtCompletion = dex.getDexCalculatedValue(dexId, CaughtPercent)
// Send system message with completion percentages for each Dex
context.source.sendSystemMessage(
Component.literal("Completion for ${dexId}: Seen: ${seenCompletion}% Caught: ${caughtCompletion}%")
)
context.source.sendSystemMessage(Component.literal("${it.javaClass.simpleName} result: $value"))
}
return 1
@@ -201,24 +194,7 @@ object PokedexCommand {
var calculators = listOf(SeenCount, CaughtCount, SeenPercent, CaughtPercent)
calculators.forEach {
var value = dex.getDexCalculatedValue(dexDef.id, it)
val percentString = if (it.outputIsPercentage) "%" else ""
context.source.sendSystemMessage(Component.literal("${it.javaClass.simpleName} result for ${dexDef.id}: $value$percentString"))
}
return 1
}
private fun printNationalDexValues (context: CommandContext<CommandSourceStack>) : Int {
val player = context.getArgument("player", EntitySelector::class.java).findSinglePlayer(context.source)
val dex = Cobblemon.playerDataManager.getPokedexData(player)
val dexDef = Dexes.dexEntryMap[ResourceLocation("cobblemon", "national")] ?: throw IllegalStateException("No National Dex")
var calculators = listOf(SeenCount, CaughtCount, SeenPercent, CaughtPercent)
calculators.forEach {
var value = dex.getDexCalculatedValue(dexDef.id, it)
val percentString = if (it.outputIsPercentage) "%" else ""
context.source.sendSystemMessage(Component.literal("${it.javaClass.simpleName} result for ${dexDef.id}: $value$percentString"))
context.source.sendSystemMessage(Component.literal("${it.javaClass.simpleName} result: $value"))
}
return 1
@@ -59,10 +59,6 @@ class OmniPathNavigation(val world: Level, val entity: Mob) : GroundPathNavigati
var navigationContext = NavigationContext()
companion object {
val verticallyPreciseNodeTypes = setOf(PathType.WATER, PathType.OPEN, PathType.LAVA)
}
override fun createPathFinder(range: Int): PathFinder {
this.nodeEvaluator = OmniPathNodeMaker()
nodeEvaluator.setCanOpenDoors(false)
@@ -130,9 +126,7 @@ class OmniPathNavigation(val world: Level, val entity: Mob) : GroundPathNavigati
val d = abs(mob.x - targetVec3d.x)
val e = abs(mob.y - targetVec3d.y)
val f = abs(mob.z - targetVec3d.z)
val closeEnough = d < maxDistanceToWaypoint.toDouble()
&& f < this.maxDistanceToWaypoint.toDouble()
&& e < (if (currentNode.type in verticallyPreciseNodeTypes) maxDistanceToWaypoint else 1.0).toDouble()
val closeEnough = d < maxDistanceToWaypoint.toDouble() && f < this.maxDistanceToWaypoint.toDouble() && e < 1.0
// Corner cutting is commented out because it makes pokemon and NPCs 'cut' the corner and fall into water or lava
if (closeEnough) {// || mob.navigation.canCutCorner(path!!.nextNode.type) && shouldTargetNextNodeInDirection(vec3d)) {
@@ -224,10 +218,6 @@ class OmniPathNavigation(val world: Level, val entity: Mob) : GroundPathNavigati
override fun getGroundY(vec: Vec3): Double {
val blockGetter: BlockGetter = level
val blockPos = BlockPos.containing(vec)
if (pather.isFlying() && world.getBlockState(blockPos).isPathfindable(PathComputationType.AIR)) {
// If we can fly and we're airborne, return the current Y position
return vec.y
}
return if ((canFloat()) && blockGetter.getFluidState(blockPos).`is`(FluidTags.WATER)) vec.y + 0.5 else WalkNodeEvaluator.getFloorLevel(blockGetter, blockPos)
}
@@ -0,0 +1,128 @@
/*
* 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.entity.ai
/*
* 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 com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.google.common.collect.ImmutableMap
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.ai.behavior.Behavior
import net.minecraft.world.entity.ai.behavior.EntityTracker
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.MemoryStatus
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.entity.ai.util.LandRandomPos
import net.minecraft.world.entity.monster.piglin.Piglin
import java.util.*
class PiglinDanceTarget : Behavior<Piglin>(
ImmutableMap.of(
CobblemonMemories.NEAREST_LOVED_ENTITY, MemoryStatus.VALUE_PRESENT,
MemoryModuleType.WALK_TARGET, MemoryStatus.REGISTERED,
),
Int.MAX_VALUE,
Int.MAX_VALUE
) {
var lovedPokemon: PokemonEntity? = null
val wayTooFar = 20F
var tooFar = 5F
var closeEnough = 3F
var tooClose = 1.5F
override fun checkExtraStartConditions(level: ServerLevel, owner: Piglin): Boolean {
lovedPokemon = (owner.brain.getMemory(CobblemonMemories.NEAREST_LOVED_ENTITY).orElse(null) ?: return false)
return lovedPokemon != null
}
override fun canStillUse(level: ServerLevel, entity: Piglin, gameTime: Long) : Boolean {
return lovedPokemon != null && lovedPokemon?.isAlive == true && (lovedPokemon?.distanceTo(entity) ?: 999F) < wayTooFar
}
override fun tick(level: ServerLevel, entity: Piglin, gameTime: Long) {
val leader : PokemonEntity = lovedPokemon ?: return
if (entity.brain.hasMemoryValue(MemoryModuleType.WALK_TARGET)) {
return // We're busy goin' n' shit
}
if (leader.distanceTo(entity) > tooFar) {
entity.brain.setMemory(
MemoryModuleType.WALK_TARGET,
CobblemonWalkTarget(
pos = leader.blockPosition(),
speedModifier = 0.6F,
completionRange = closeEnough.toInt()
)
)
} else if (leader.distanceTo(entity) < tooClose) {
// Give the dude some space
for (j in 0..9) {
val vec3 = LandRandomPos.getPosAway(entity, 5, 7, leader.position())
if (vec3 != null) {
entity.brain.setMemory(
MemoryModuleType.WALK_TARGET,
WalkTarget(
vec3,
0.4F,
closeEnough.toInt()
)
)
}
}
} else if (leader.brain.hasMemoryValue(MemoryModuleType.WALK_TARGET)) {
// Go to the leader's walk target, roughly.
val walkTarget = leader.brain.getMemory(MemoryModuleType.WALK_TARGET).orElse(null) ?: return
entity.brain.setMemory(
MemoryModuleType.WALK_TARGET,
CobblemonWalkTarget(
pos = walkTarget.target.currentBlockPosition(),
speedModifier = walkTarget.speedModifier,
nodeTypeFilter = (walkTarget as? CobblemonWalkTarget)?.nodeTypeFilter ?: { true },
completionRange = closeEnough.toInt()
)
)
} else {
// Maybe wander around and mingle
if (entity.random.nextFloat() < 0.01f) {
val vec3 = LandRandomPos.getPosTowards(entity,5, 7, leader.position())
if (vec3 != null) {
entity.brain.setMemory(
MemoryModuleType.WALK_TARGET,
WalkTarget(
vec3,
0.5F,
1
)
)
}
}
}
}
override fun start(level: ServerLevel, entity: Piglin, gameTime: Long) {
val pokemonEntity = lovedPokemon ?: return
entity.brain.setMemory(MemoryModuleType.DANCING, true)
entity.brain.setMemory(MemoryModuleType.CELEBRATE_LOCATION, Optional.of(pokemonEntity.blockPosition()))
entity.brain.setMemory(MemoryModuleType.LOOK_TARGET, EntityTracker(pokemonEntity, true))
}
override fun stop(level: ServerLevel, entity: Piglin, gameTime: Long) {
entity.brain.setMemory(MemoryModuleType.DANCING, false)
entity.brain.eraseMemory(MemoryModuleType.CELEBRATE_LOCATION)
}
}
@@ -8,8 +8,6 @@
package com.cobblemon.mod.common.entity.npc
import com.cobblemon.mod.common.CobblemonBehaviours
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.config.ApplyBehaviours
import com.cobblemon.mod.common.api.ai.config.BehaviourConfig
@@ -18,14 +16,12 @@ import com.mojang.serialization.Dynamic
object NPCBrain {
fun configure(npcEntity: NPCEntity, npcClass: NPCClass, dynamic: Dynamic<*>) {
var behaviourConfigurations: List<BehaviourConfig> = CobblemonBehaviours.autoPokemonBehaviours.flatMap { it.configurations } + npcClass.behaviours
var behaviourConfigurations: List<BehaviourConfig> = npcClass.behaviours
if (npcEntity.behavioursAreCustom) {
behaviourConfigurations = listOf(ApplyBehaviours().apply { behaviours.addAll(npcEntity.behaviours) })
}
val ctx = BehaviourConfigurationContext()
ctx.addMemories(CobblemonMemories.DIALOGUES)
ctx.addMemories(CobblemonMemories.NPC_BATTLING)
ctx.apply(npcEntity, behaviourConfigurations, dynamic)
npcEntity.behaviours.clear()
npcEntity.behaviours.addAll(ctx.appliedBehaviours)
@@ -15,7 +15,9 @@ import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonEntities
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.CobblemonSensors
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.entity.PokemonSender
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
@@ -53,6 +55,7 @@ import com.cobblemon.mod.common.util.getBattleState
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.makeEmptyBrainDynamic
import com.cobblemon.mod.common.util.withNPCValue
import com.google.common.collect.ImmutableList
import com.mojang.authlib.GameProfile
import com.mojang.authlib.ProfileLookupCallback
import com.mojang.serialization.Dynamic
@@ -22,13 +22,9 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.battles.BattleBuilder
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.asUUID
import com.cobblemon.mod.common.util.getBooleanOrNull
import com.cobblemon.mod.common.util.getIntOrNull
import com.cobblemon.mod.common.util.getStringOrNull
import net.minecraft.world.entity.LivingEntity
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.ai.memory.MemoryModuleType
@@ -81,34 +77,9 @@ class NPCServerDelegate : NPCSideDelegate {
entity.server!!.playerList.getPlayerByName(paramString) ?: return@addFunction DoubleValue.ZERO
}
}
val format = (params.getStringOrNull(1)
?.let(BattleFormat::fromFormatIdentifier)
?: BattleFormat.GEN_9_SINGLES)
val setLevel = params.getIntOrNull(2) ?: -1
format.adjustLevel = setLevel
val rules = params.getStringOrNull(5)
?.split(",")
?.toSet()
?: emptySet()
val modifiedBattleFormat = BattleFormat.setBattleRules(
battleFormat = format,
rules = rules
)
val cloneParties = (setLevel != -1) || (params.getBooleanOrNull(3) ?: false)
val healFirst = params.getBooleanOrNull(4) ?: false
println("Cobblemon: Starting battle with format: $modifiedBattleFormat, level: ${modifiedBattleFormat.adjustLevel}")
val battleStartResult = BattleBuilder.pvn(
player = opponent,
npcEntity = entity,
battleFormat = modifiedBattleFormat,
cloneParties = cloneParties,
healFirst = healFirst
npcEntity = entity
)
var returnValue: MoValue = DoubleValue.ZERO
@@ -60,7 +60,6 @@ import com.cobblemon.mod.common.api.scheduling.afterOnServer
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.battles.BattleBuilder
import com.cobblemon.mod.common.battles.BattleRegistry
@@ -132,7 +131,6 @@ import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.damagesource.DamageTypes
import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.effect.MobEffects
import net.minecraft.world.entity.*
import net.minecraft.world.entity.ai.Brain
@@ -521,23 +519,6 @@ open class PokemonEntity(
super.removePassenger(passenger)
}
override fun thunderHit(level: ServerLevel, lightning: LightningBolt) {
// Deals with special cases in which Pokemon should either be immune or buffed by lightning strikes.
when (pokemon.ability.name) {
"lightningrod" -> {
this.addEffect(MobEffectInstance(MobEffects.DAMAGE_BOOST, 1200, 1))
}
"motordrive" -> {
this.addEffect(MobEffectInstance(MobEffects.MOVEMENT_SPEED, 1200, 0))
}
"voltabsorb" -> {
this.addEffect(MobEffectInstance(MobEffects.HEAL, 1, 1))
}
// Ground types shouldn't take lightning damage
else -> if (this.pokemon.types.none { it == ElementalTypes.GROUND }) super.thunderHit(level, lightning)
}
}
override fun tick() {
/* Addresses watchdog hanging that is completely bloody inexplicable. */
yBodyRot = Mth.wrapDegrees(yBodyRot)
@@ -1,99 +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.entity.pokemon.ai.sensors
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.block.SaccharineLeafBlock
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.ai.sensing.Sensor
import net.minecraft.world.level.block.BeehiveBlock
import net.minecraft.world.level.block.Blocks
class BeeHiveSensor : Sensor<PokemonEntity>(300) {
override fun requires() = setOf(CobblemonMemories.HIVE_LOCATION)
override fun doTick(world: ServerLevel, entity: PokemonEntity) {
val brain = entity.brain
val currentHive = brain.getMemory(CobblemonMemories.HIVE_LOCATION).orElse(null)
if (currentHive != null) {
val state = world.getBlockState(currentHive)
if (!isValidHiveOrLeaf(state) || !hasReachableAdjacentSide(world, entity, currentHive)) {
brain.eraseMemory(CobblemonMemories.HIVE_LOCATION)
} else if (isAtMaxHoney(state)) {
brain.eraseMemory(CobblemonMemories.HIVE_LOCATION)
} else {
return // We already have a valid hive, no need to search for another
}
}
// Search for nearest hive/nest
val searchRadius = 16 // is this too big for us to use?
val centerPos = entity.blockPosition()
var closestHivePos: BlockPos? = null
var closestDistance = Double.MAX_VALUE
BlockPos.betweenClosedStream(
centerPos.offset(-searchRadius, -2, -searchRadius),
centerPos.offset(searchRadius, 2, searchRadius)
).forEach { pos ->
val state = world.getBlockState(pos)
if (isValidHiveOrLeaf(state) && !isAtMaxHoney(state) && hasReachableAdjacentSide(world, entity, pos)) {
val distance = pos.distToCenterSqr(centerPos.x + 0.5, centerPos.y + 0.5, centerPos.z + 0.5)
if (distance < closestDistance) {
closestDistance = distance
closestHivePos = pos.immutable()
}
}
}
if (closestHivePos != null) {
brain.setMemory(CobblemonMemories.HIVE_LOCATION, closestHivePos)
}
}
private fun isAtMaxHoney(state: net.minecraft.world.level.block.state.BlockState): Boolean {
return when {
isHiveBlock(state) -> state.getValue(BeehiveBlock.HONEY_LEVEL) == BeehiveBlock.MAX_HONEY_LEVELS
isSaccharineLeafBlock(state) -> state.getValue(SaccharineLeafBlock.AGE) == SaccharineLeafBlock.MAX_AGE
else -> true // Unknown block type
}
}
private fun isValidHiveOrLeaf(state: net.minecraft.world.level.block.state.BlockState): Boolean {
return isHiveBlock(state) || isSaccharineLeafBlock(state)
}
private fun isSaccharineLeafBlock(state: net.minecraft.world.level.block.state.BlockState): Boolean {
return state.block is SaccharineLeafBlock
}
private fun isHiveBlock(state: net.minecraft.world.level.block.state.BlockState): Boolean {
return state.`is`(Blocks.BEEHIVE) || state.`is`(Blocks.BEE_NEST)
}
private fun hasReachableAdjacentSide(world: ServerLevel, entity: PokemonEntity, pos: BlockPos): Boolean {
for (dir in Direction.entries) {
val adjacentPos = pos.relative(dir)
if (world.isEmptyBlock(adjacentPos)) {
val nav = entity.navigation
val path = nav.createPath(adjacentPos, 0)
if (path != null && path.canReach()) {
return true
}
}
}
return false
}
}
@@ -1,55 +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.entity.pokemon.ai.sensors
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.tags.BlockTags
import net.minecraft.world.entity.ai.sensing.Sensor
import net.minecraft.world.level.block.state.BlockState
class FlowerSensor : Sensor<PokemonEntity>(120) {
override fun requires() = setOf(CobblemonMemories.NEARBY_FLOWERS)
override fun doTick(world: ServerLevel, entity: PokemonEntity) {
val brain = entity.brain
val searchRadius = 32
val centerPos = entity.blockPosition()
val flowers = mutableListOf<BlockPos>()
BlockPos.betweenClosedStream(
centerPos.offset(-searchRadius, -2, -searchRadius),
centerPos.offset(searchRadius, 2, searchRadius)
).forEach { pos ->
val state = world.getBlockState(pos)
if (isFlower(state) && isPathfindableTo(entity, pos)) {
flowers += pos.immutable()
}
}
if (!flowers.isEmpty()) {
brain.setMemory(CobblemonMemories.NEARBY_FLOWERS, flowers)
} else {
brain.eraseMemory(CobblemonMemories.NEARBY_FLOWERS)
}
}
private fun isFlower(state: BlockState): Boolean {
return state.`is`(BlockTags.FLOWERS)
}
private fun isPathfindableTo(entity: PokemonEntity, pos: BlockPos): Boolean {
val nav = entity.navigation
val path = nav.createPath(pos, 0)
return path != null && path.canReach()
}
}
@@ -0,0 +1,41 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.entity.pokemon.ai.sensors
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities
import net.minecraft.world.entity.ai.sensing.Sensor
/**
* Senses when the Pokémon is near an adult of the same species.
*
* @author JazzMcNade
* @since May 15th, 2024
*/
class PokemonPiglinLovedSensor : Sensor<LivingEntity>(100) {
override fun requires() = setOf(CobblemonMemories.NEAREST_LOVED_ENTITY, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES)
override fun doTick(world: ServerLevel, entity: LivingEntity) {
entity.brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES).ifPresent { visibleMobs ->
setNearestPokemonAdult(entity, visibleMobs)
}
}
private fun setNearestPokemonAdult(entity: LivingEntity, visibleMobs: NearestVisibleLivingEntities) {
val nearestGoldenIdol = visibleMobs.findClosest { mob ->
mob is PokemonEntity && mob.exposedSpecies.behaviour.entityInteract.lovedByPiglin
}.map { mob -> mob as PokemonEntity }
entity.brain.setMemory(CobblemonMemories.NEAREST_LOVED_ENTITY, nearestGoldenIdol)
}
}
@@ -1,77 +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.entity.pokemon.ai.tasks
import com.cobblemon.mod.common.CobblemonMemories
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.behavior.OneShot
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.phys.Vec3
object PathToBeeHiveTask {
fun create(): OneShot<in LivingEntity> {
return BehaviorBuilder.create {
it.group(
it.registered(MemoryModuleType.LOOK_TARGET),
it.absent(MemoryModuleType.WALK_TARGET),
it.present(CobblemonMemories.POLLINATED),
it.present(CobblemonMemories.HIVE_LOCATION),
it.absent(CobblemonMemories.HIVE_COOLDOWN)
).apply(it) { lookTarget, walkTarget, pollinated, hiveMemory, hiveCooldown ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isAlive) {
return@Trigger false
}
val hiveLocation = it.get(hiveMemory)
val sidesByClosest = listOf(
hiveLocation.north(),
hiveLocation.south(),
hiveLocation.east(),
hiveLocation.west()
).sortedBy { it.distSqr(entity.blockPosition()) }
var openSide = BlockPos.ZERO
for (side in sidesByClosest) {
if (world.getBlockState(side).isAir) {
openSide = side
break
}
}
if (openSide == BlockPos.ZERO) {
if (world.getBlockState(hiveLocation.above()).isAir) {
// If the above position is also air, we can use it
openSide = hiveLocation.above() // Fallback to above if no open sides found
} else if (world.getBlockState(hiveLocation.below()).isAir) {
// If the below position is also air, we can use it
openSide = hiveLocation.below() // Fallback to below if no open sides found
} else {
// If no open sides or above/below positions are found, we cannot proceed
return@Trigger false
}
}
val targetVec = Vec3.atCenterOf(openSide)
// Set path target toward hive
walkTarget.set(WalkTarget(targetVec, 0.35F, 0))
lookTarget.set(BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
return@Trigger true
}
}
}
}
}
@@ -1,59 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.entity.pokemon.ai.tasks
import com.cobblemon.mod.common.CobblemonMemories
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.behavior.OneShot
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.phys.Vec3
object PathToFlowerTask {
fun create(): OneShot<in LivingEntity> {
return BehaviorBuilder.create {
it.group(
it.registered(MemoryModuleType.LOOK_TARGET),
it.absent(MemoryModuleType.WALK_TARGET),
it.present(CobblemonMemories.NEARBY_FLOWERS),
it.absent(CobblemonMemories.POLLINATED),
it.absent(CobblemonMemories.HIVE_COOLDOWN)
).apply(it) { lookTarget, walkTarget, flowerMemory, pollinated, hiveCooldown ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
val flowerLocations = it.get(flowerMemory).map(Vec3::atCenterOf)
if (flowerLocations.isEmpty()) {
return@Trigger false
}
val sortedByDistance = flowerLocations.sortedBy { it.distanceTo(entity.position()) }
val closestHalf = sortedByDistance.subList(0, flowerLocations.size / 2 + 1)
if (closestHalf.isEmpty()) {
return@Trigger false
}
val targetVec = closestHalf.random()
// if we're really close to one then forget it, pollination is gonna occur
if (flowerLocations.any { it.distanceToSqr(entity.x, entity.y, entity.z) <= 1.0 }) {
return@Trigger false
}
walkTarget.set(WalkTarget(targetVec, 0.3F, 0))
lookTarget.set(BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
return@Trigger true
}
}
}
}
}
@@ -1,68 +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.entity.pokemon.ai.tasks
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.block.SaccharineLeafBlock
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.OneShot
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.level.block.BeehiveBlock
import net.minecraft.world.phys.Vec3
object PlaceHoneyInHiveTask {
fun create(): OneShot<in LivingEntity> {
return BehaviorBuilder.create {
it.group(
it.absent(MemoryModuleType.WALK_TARGET),
it.present(CobblemonMemories.POLLINATED),
it.present(CobblemonMemories.HIVE_LOCATION),
it.absent(CobblemonMemories.HIVE_COOLDOWN)
).apply(it) { walkTarget, pollinated, hiveMemory, hiveCooldown ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob) {
return@Trigger false
}
val hiveCooldown = 1200L
val hiveLocation = it.get(hiveMemory)
val targetVec = Vec3.atCenterOf(hiveLocation)
// if we are not close to it then end early
if (entity.distanceToSqr(targetVec) > 2.0) {
return@Trigger false
}
val state = world.getBlockState(hiveLocation)
val block = state.block
if (block is BeehiveBlock) {
val currentLevel = state.getValue(BeehiveBlock.HONEY_LEVEL)
if (currentLevel < BeehiveBlock.MAX_HONEY_LEVELS) {
world.setBlock(hiveLocation, state.setValue(BeehiveBlock.HONEY_LEVEL, currentLevel + 1), 3)
entity.brain.setMemoryWithExpiry(CobblemonMemories.HIVE_COOLDOWN, true, hiveCooldown)
entity.brain.eraseMemory(CobblemonMemories.POLLINATED)
}
} else if (block is SaccharineLeafBlock) {
val currentAge = state.getValue(SaccharineLeafBlock.AGE)
if (currentAge < SaccharineLeafBlock.MAX_AGE) {
world.setBlock(hiveLocation, state.setValue(SaccharineLeafBlock.AGE, currentAge + 1), 3)
entity.brain.setMemoryWithExpiry(CobblemonMemories.HIVE_COOLDOWN, true, hiveCooldown)
entity.brain.eraseMemory(CobblemonMemories.POLLINATED)
}
}
return@Trigger true
}
}
}
}
}
@@ -1,46 +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.entity.pokemon.ai.tasks
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.OneShot
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.phys.Vec3
object PollinateFlowerTask {
fun create(): OneShot<in LivingEntity> {
return BehaviorBuilder.create {
it.group(
it.present(CobblemonMemories.NEARBY_FLOWERS),
it.absent(MemoryModuleType.WALK_TARGET),
it.absent(CobblemonMemories.POLLINATED),
it.absent(CobblemonMemories.HIVE_COOLDOWN)
).apply(it) { flowerMemory, walkTarget, pollinated, hiveCooldown ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
val flowerLocations = it.get(flowerMemory)
if (flowerLocations.any { it.distSqr(entity.blockPosition()) < 1 }) {
entity.brain.setMemoryWithExpiry(CobblemonMemories.POLLINATED, true, 20 * 20L) // 20 seconds to dump the pollen
if (entity is PokemonEntity) {
entity.pokemon.updateAspects()
}
}
return@Trigger true
}
}
}
}
}
@@ -36,29 +36,16 @@ object FlowHandler {
CobblemonEvents.BATTLE_STARTED_PRE.subscribe { CobblemonFlows.run(cobblemonResource("battle_started_pre"), it.context, it.functions) }
CobblemonEvents.BATTLE_STARTED_POST.subscribe { CobblemonFlows.run(cobblemonResource("battle_started_post"), it.context) }
CobblemonEvents.APRICORN_HARVESTED.subscribe { CobblemonFlows.run(cobblemonResource("apricorn_harvested"), it.context) }
CobblemonEvents.BERRY_HARVEST.subscribe { CobblemonFlows.run(cobblemonResource("berry_harvested"), it.context) }
CobblemonEvents.THROWN_POKEBALL_HIT.subscribe { CobblemonFlows.run(cobblemonResource("thrown_pokeball_hit"), it.context) }
CobblemonEvents.POKEMON_HEALED.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_healed"), it.context, it.functions) }
CobblemonEvents.POKEMON_ASPECTS_CHANGED.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_aspects_changed"), it.context) }
CobblemonEvents.LEVEL_UP_EVENT.subscribe { CobblemonFlows.run(cobblemonResource("level_up"), it.context, it.functions) }
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe { CobblemonFlows.run(cobblemonResource("friendship_updated"), it.context, it.functions) }
CobblemonEvents.FULLNESS_UPDATED.subscribe { CobblemonFlows.run(cobblemonResource("fullness_updated"), it.context, it.functions) }
CobblemonEvents.HYPER_TRAINED_IV_PRE.subscribe { CobblemonFlows.run(cobblemonResource("hyper_trained_iv_pre"), it.context, it.functions) }
CobblemonEvents.HYPER_TRAINED_IV_POST.subscribe { CobblemonFlows.run(cobblemonResource("hyper_trained_iv_post"), it.context) }
CobblemonEvents.EV_GAINED_EVENT_PRE.subscribe { CobblemonFlows.run(cobblemonResource("ev_gained_pre"), it.context, it.functions) }
CobblemonEvents.EV_GAINED_EVENT_POST.subscribe { CobblemonFlows.run(cobblemonResource("ev_gained_post"), it.context) }
CobblemonEvents.EXPERIENCE_GAINED_EVENT_PRE.subscribe { CobblemonFlows.run(cobblemonResource("experience_gained_pre"), it.context, it.functions) }
CobblemonEvents.EXPERIENCE_GAINED_EVENT_POST.subscribe { CobblemonFlows.run(cobblemonResource("experience_gained_post"), it.context) }
CobblemonEvents.LOOT_DROPPED.subscribe { CobblemonFlows.run(cobblemonResource("loot_dropped"), it.context) }
CobblemonEvents.POKEMON_FAINTED.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_fainted"), it.context) }
CobblemonEvents.POKEMON_GAINED.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_gained"), it.context, it.functions) }
CobblemonEvents.POKEMON_SEEN.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_seen"), it.context, it.functions) }
CobblemonEvents.POKEMON_SCANNED.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_scanned"), it.context) }
CobblemonEvents.POKEMON_SENT_PRE.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_sent_pre"), it.context, it.functions) }
CobblemonEvents.POKEMON_SENT_POST.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_sent_post"), it.context) }
CobblemonEvents.POKEMON_RELEASED_EVENT_PRE.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_released_pre"), it.getContext(), it.functions) }
CobblemonEvents.POKEMON_RELEASED_EVENT_POST.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_released_post"), it.getContext()) }
CobblemonEvents.POKEMON_CATCH_RATE.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_catch_rate_calculated"), it.context, it.functions) }
CobblemonEvents.POKE_BALL_CAPTURE_CALCULATED.subscribe { CobblemonFlows.run(cobblemonResource("poke_ball_capture_calculated"), it.context, it.functions) }
CobblemonEvents.EVOLUTION_TESTED.subscribe { CobblemonFlows.run(cobblemonResource("evolution_tested"), it.context, it.functions) }
CobblemonEvents.EVOLUTION_ACCEPTED.subscribe { CobblemonFlows.run(cobblemonResource("evolution_accepted"), it.context, it.functions) }
@@ -66,25 +53,10 @@ object FlowHandler {
CobblemonEvents.POKEMON_NICKNAMED.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_nicknamed"), it.getContext()) }
CobblemonEvents.HELD_ITEM_PRE.subscribe { CobblemonFlows.run(cobblemonResource("held_item_pre"), it.getContext(), it.functions) }
CobblemonEvents.HELD_ITEM_POST.subscribe { CobblemonFlows.run(cobblemonResource("held_item_post"), it.getContext()) }
CobblemonEvents.COSMETIC_ITEM_PRE.subscribe { CobblemonFlows.run(cobblemonResource("cosmetic_item_pre"), it.getContext(), it.functions) }
CobblemonEvents.COSMETIC_ITEM_POST.subscribe { CobblemonFlows.run(cobblemonResource("cosmetic_item_post"), it.getContext()) }
CobblemonEvents.FOSSIL_REVIVED.subscribe { CobblemonFlows.run(cobblemonResource("fossil_revived"), it.context) }
CobblemonEvents.BAIT_SET.subscribe { CobblemonFlows.run(cobblemonResource("bait_set"), it.context, it.functions) }
CobblemonEvents.BAIT_SET_PRE.subscribe { CobblemonFlows.run(cobblemonResource("bait_set_pre"), it.context, it.functions) }
CobblemonEvents.BAIT_CONSUMED.subscribe { CobblemonFlows.run(cobblemonResource("bait_consumed"), it.context) }
CobblemonEvents.POKEROD_CAST_PRE.subscribe { CobblemonFlows.run(cobblemonResource("pokerod_cast_pre"), it.context, it.functions) }
CobblemonEvents.POKEROD_CAST_POST.subscribe { CobblemonFlows.run(cobblemonResource("pokerod_cast_post"), it.context) }
CobblemonEvents.POKEROD_REEL.subscribe { CobblemonFlows.run(cobblemonResource("pokerod_reel"), it.context, it.functions) }
CobblemonEvents.BOBBER_SPAWN_POKEMON_PRE.subscribe { CobblemonFlows.run(cobblemonResource("bobber_spawn_pokemon_pre"), it.context, it.functions) }
CobblemonEvents.BOBBER_SPAWN_POKEMON_POST.subscribe { CobblemonFlows.run(cobblemonResource("bobber_spawn_pokemon_post"), it.context) }
CobblemonEvents.TRADE_COMPLETED.subscribe { CobblemonFlows.run(cobblemonResource("trade_completed"), it.context) }
CobblemonEvents.WALLPAPER_UNLOCKED_EVENT.subscribe { CobblemonFlows.run(cobblemonResource("wallpaper_unlocked"), it.context, it.functions) }
CobblemonEvents.CHANGE_PC_BOX_WALLPAPER_EVENT_PRE.subscribe { CobblemonFlows.run(cobblemonResource("change_pc_box_wallpaper_pre"), it.context, it.functions) }
CobblemonEvents.CHANGE_PC_BOX_WALLPAPER_EVENT_POST.subscribe { CobblemonFlows.run(cobblemonResource("change_pc_box_wallpaper"), it.context) }
CobblemonEvents.COLLECT_EGG.subscribe { CobblemonFlows.run(cobblemonResource("collect_egg"), it.context, it.functions) }
CobblemonEvents.HATCH_EGG_PRE.subscribe { CobblemonFlows.run(cobblemonResource("hatch_egg_pre"), it.context, it.functions) }
CobblemonEvents.HATCH_EGG_POST.subscribe { CobblemonFlows.run(cobblemonResource("hatch_egg_post"), it.context) }
CobblemonEvents.SHOULDER_MOUNT.subscribe { CobblemonFlows.run(cobblemonResource("shoulder_mount"), it.context, it.functions) }
PlatformEvents.SERVER_PLAYER_LOGIN.subscribe(priority = Priority.LOW) { CobblemonFlows.run(cobblemonResource("player_logged_in"), it.context) }
PlatformEvents.SERVER_PLAYER_LOGOUT.subscribe(priority = Priority.HIGH) { CobblemonFlows.run(cobblemonResource("player_logged_out"), it.context) }
@@ -1,39 +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.net.messages.client.effect
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.client.net.effect.SaccharineLogBlockParticlesHandler
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.core.BlockPos
import net.minecraft.network.RegistryFriendlyByteBuf
/**
* Handled by [SaccharineLogBlockParticlesHandler].
*/
class SaccharineLogBlockParticlesPacket(
val blockPos: BlockPos,
val entityPos: BlockPos?
) : NetworkPacket<SaccharineLogBlockParticlesPacket> {
override val id = ID
companion object {
val ID = cobblemonResource("saccharine_log_block_particles")
fun decode(buffer: RegistryFriendlyByteBuf): SaccharineLogBlockParticlesPacket =
SaccharineLogBlockParticlesPacket(
buffer.readBlockPos(),
buffer.readNullable { buffer.readBlockPos() }
)
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeBlockPos(blockPos)
buffer.writeNullable(entityPos) { _, v -> buffer.writeBlockPos(v) }
}
}
@@ -1155,11 +1155,7 @@ open class Pokemon : ShowdownIdentifiable {
*/
fun swapHeldItem(stack: ItemStack, decrement: Boolean = true): ItemStack {
val existing = this.heldItem()
val event = HeldItemEvent.Pre(this, stack, existing, decrement)
if (!isClient) {
CobblemonEvents.HELD_ITEM_PRE.post(event)
}
if (!event.isCanceled) {
CobblemonEvents.HELD_ITEM_PRE.postThen(HeldItemEvent.Pre(this, stack, existing, decrement), ifSucceeded = { event ->
val giving = event.receiving.copy().apply { count = 1 }
if (event.decrement) {
event.receiving.shrink(1)
@@ -1170,37 +1166,13 @@ open class Pokemon : ShowdownIdentifiable {
StashHandler.giveHeldItem(it)
}
return event.returning
}
})
return stack
}
/**
* Returns a copy of the cosmetic item.
* In order to change the [ItemStack] use [swapCosmeticItem].
*
* @return A copy of the [ItemStack] cosmetic item held by this Pokémon.
*/
fun cosmeticItem(): ItemStack = this.cosmeticItem.copy()
/**
* Swaps out the current [cosmeticItem] for the given [stack].
* The assigned [cosmeticItem] will always have the [ItemStack.count] of 1.
*
* The behavior of this method may be modified by third party, please see the [HeldItemEvent].
*
* @param stack The new [ItemStack] being set as the cosmetic item.
* @param decrement If the given [stack] should have [ItemStack.decrement] invoked with the parameter of 1. Default is true.
* @return The existing [ItemStack] being held or the [stack] if [HeldItemEvent.Pre] is canceled.
*
* @see [HeldItemEvent]
*/
fun swapCosmeticItem(stack: ItemStack, decrement: Boolean = true): ItemStack {
val existing = this.cosmeticItem.copy()
val event = HeldItemEvent.Pre(this, stack, existing, decrement)
if (!isClient) {
CobblemonEvents.COSMETIC_ITEM_PRE.post(event)
}
if (!event.isCanceled) {
CobblemonEvents.COSMETIC_ITEM_PRE.postThen(HeldItemEvent.Pre(this, stack, existing, decrement), ifSucceeded = { event ->
val giving = event.receiving.copy().apply { count = 1 }
if (event.decrement) {
event.receiving.shrink(1)
@@ -1208,7 +1180,7 @@ open class Pokemon : ShowdownIdentifiable {
this.cosmeticItem = giving
CobblemonEvents.COSMETIC_ITEM_POST.post(HeldItemEvent.Post(this, this.cosmeticItem.copy(), event.returning.copy(), event.decrement))
return event.returning
}
})
return stack
}
@@ -1219,13 +1191,6 @@ open class Pokemon : ShowdownIdentifiable {
*/
fun removeHeldItem(): ItemStack = this.swapHeldItem(ItemStack.EMPTY)
/**
* Swaps out the current [cosmeticItem] for an [ItemStack.EMPTY].
*
* @return The existing [ItemStack] being held.
*/
fun removeCosmeticItem(): ItemStack = this.swapCosmeticItem(ItemStack.EMPTY)
fun addPotentialMark(mark: Mark) {
potentialMarks.add(mark)
if (!isClient) onChange(MarkPotentialAddUpdatePacket({ this }, mark))
@@ -1706,58 +1671,6 @@ open class Pokemon : ShowdownIdentifiable {
return ability
}
/**
* Teaches all moves that are potentially available to this Pokémon.
*
* TO-DO: Implement Legacy source moves.
* @param includeLegacy If moves that were only learnable in previous versions should be considered valid and taught.
*/
fun teachLearnableMoves(includeLegacy: Boolean = true) {
// Get all learnable moves
val possibleMoves = form.moves.getAllLegalMoves()
// Add all possible moves to the moveset
val possibleMovesSet = HashSet<BenchedMove>()
val query = if (includeLegacy) LearnsetQuery.ANY else LearnsetQuery.LEGAL
for (move in possibleMoves) {
if (query.canLearn(move, this.form.moves) && moveSet.none { it.template == move }) {
possibleMovesSet.add(BenchedMove(move, 0))
}
}
this.benchedMoves.addAll(possibleMovesSet)
this.benchedMoves.update()
moveSet.update()
}
/**
* Validates the moveset of this Pokémon, removing any invalid moves.
* This will also remove any benched moves that cannot be learned by the current form.
*
* TO-DO: Implement Legacy source moves.
* @param includeLegacy If moves that were only learnable in previous versions should be considered valid.
*/
fun validateMoveset(includeLegacy: Boolean = true) {
// Validate the moveset, removing any invalid moves
val query = if (includeLegacy) LearnsetQuery.ANY else LearnsetQuery.LEGAL
moveSet.doWithoutEmitting {
benchedMoves.doWithoutEmitting {
for (i in 0 until MoveSet.MOVE_COUNT) {
val move = this.moveSet[i]
if (move != null && !query.canLearn(move.template, this.form.moves)) {
this.moveSet.setMove(i, null)
}
}
val benchedIterator = this.benchedMoves.iterator()
while (benchedIterator.hasNext()) {
val benchedMove = benchedIterator.next()
if (!query.canLearn(benchedMove.moveTemplate, this.form.moves)) {
benchedIterator.remove()
}
}
}
}
moveSet.update()
}
fun initializeMoveset(preferLatest: Boolean = true) {
val possibleMoves = form.moves.getLevelUpMovesUpTo(level).toMutableList()
moveSet.doWithoutEmitting {
@@ -1787,30 +1700,6 @@ open class Pokemon : ShowdownIdentifiable {
moveSet.update()
}
/**
* Removes the specified [move] from the Pokémon's current move set if it exists.
*/
fun unlearnMove(move: MoveTemplate) {
moveSet.doWithoutEmitting {
for (i in 0 until MoveSet.MOVE_COUNT) {
val currentMove = this.moveSet[i]
if (currentMove != null && currentMove.template == move) {
this.moveSet.setMove(i, null)
}
}
this.benchedMoves.doWithoutEmitting {
val benchedIterator = this.benchedMoves.iterator()
while (benchedIterator.hasNext()) {
val benchedMove = benchedIterator.next()
if (benchedMove.moveTemplate == move) {
benchedIterator.remove()
}
}
}
}
moveSet.update()
}
fun getMaxRideBoost(stat: RidingStat): Int {
return form.riding.stats[stat]?.ranges?.maxOf { it.value.endInclusive } ?: 0
}
@@ -2035,7 +1924,6 @@ open class Pokemon : ShowdownIdentifiable {
if (packet != null && storeCoordinates.get() != null) {
notify(packet)
}
changeObservable.emit(this)
}
@@ -22,12 +22,16 @@ class EntityBehaviour {
val avoidedByCreeper = false
val avoidedByPhantom = false
val avoidedBySkeleton = false
val lovedByPiglin = false
val avoidedByPiglin = false
@Transient
val struct = ObjectValue(this).also {
it.addFunction("avoided_by_creeper") { DoubleValue(avoidedByCreeper) }
it.addFunction("avoided_by_phantom") { DoubleValue(avoidedByPhantom) }
it.addFunction("avoided_by_skeleton") { DoubleValue(avoidedBySkeleton) }
it.addFunction("loved_by_piglin") { DoubleValue(lovedByPiglin) }
it.addFunction("avoid_by_piglin") { DoubleValue(avoidedByPiglin) }
}
companion object {
@@ -68,9 +68,7 @@ object PokemonBrain {
CobblemonSensors.POKEMON_DROWSY,
CobblemonSensors.POKEMON_ADULT,
SensorType.IS_IN_WATER,
CobblemonSensors.NEARBY_GROWABLE_CROPS,
CobblemonSensors.NEARBY_BEE_HIVE,
CobblemonSensors.NEARBY_FLOWER
CobblemonSensors.NEARBY_GROWABLE_CROPS
// CobblemonSensors.BATTLING_POKEMON,
// CobblemonSensors.NPC_BATTLING
@@ -186,11 +184,6 @@ object PokemonBrain {
MemoryModuleType.AVOID_TARGET,
CobblemonMemories.POKEMON_SLEEPING,
CobblemonMemories.RECENTLY_ATE_GRASS,
CobblemonMemories.HIVE_LOCATION,
CobblemonMemories.HIVE_COOLDOWN,
CobblemonMemories.NEARBY_FLOWERS,
CobblemonMemories.POLLINATED,
CobblemonMemories.RECENTLY_ATE_GRASS,
CobblemonMemories.HERD_LEADER,
CobblemonMemories.HERD_SIZE,
CobblemonMemories.ATTACK_TARGET_DATA
@@ -9,13 +9,11 @@
package com.cobblemon.mod.common.pokemon.aspects
import com.cobblemon.mod.common.CobblemonCosmeticItems
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.pokemon.aspect.SingleConditionalAspectProvider
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.getMemorySafely
import com.cobblemon.mod.common.util.server
val SHINY_ASPECT = object : SingleConditionalAspectProvider {
@@ -40,9 +38,6 @@ val GENDER_ASPECT = object : AspectProvider {
val COSMETIC_SLOT_ASPECT = object : AspectProvider {
override fun provide(pokemon: Pokemon): Set<String> {
val server = server() ?: return emptySet()
if (pokemon.cosmeticItem.isEmpty) {
return emptySet()
}
val cosmeticItem = CobblemonCosmeticItems.findValidCosmeticForPokemonAndItem(server.registryAccess(), pokemon, pokemon.cosmeticItem)
return cosmeticItem?.aspects?.toSet() ?: emptySet()
}
@@ -41,11 +41,6 @@ object CobblemonPlacedFeatures {
val BERRY_GROVE = of("berry_groves")
val SWAMP_GRAINS = of("swamp_grains")
val PLAINS_GRAINS = of("plains_grains")
val GALARICA_NUTS = of("galarica_nuts")
fun register() {
// We don't need to pass in any tags, the feature implementation handles it, while not a perfect system it works
Cobblemon.implementation.addFeatureToWorldGen(APRICORN_TREES, GenerationStep.Decoration.VEGETAL_DECORATION, null)
@@ -55,9 +50,6 @@ object CobblemonPlacedFeatures {
Cobblemon.implementation.addFeatureToWorldGen(BIG_ROOT, GenerationStep.Decoration.VEGETAL_DECORATION, BiomeTags.IS_OVERWORLD)
Cobblemon.implementation.addFeatureToWorldGen(REVIVAL_HERB, GenerationStep.Decoration.VEGETAL_DECORATION, CobblemonBiomeTags.HAS_REVIVAL_HERBS)
Cobblemon.implementation.addFeatureToWorldGen(BERRY_GROVE, GenerationStep.Decoration.VEGETAL_DECORATION, BiomeTags.IS_OVERWORLD)
Cobblemon.implementation.addFeatureToWorldGen(SWAMP_GRAINS, GenerationStep.Decoration.VEGETAL_DECORATION, CobblemonBiomeTags.IS_SWAMP)
Cobblemon.implementation.addFeatureToWorldGen(PLAINS_GRAINS, GenerationStep.Decoration.VEGETAL_DECORATION, CobblemonBiomeTags.IS_PLAINS)
Cobblemon.implementation.addFeatureToWorldGen(GALARICA_NUTS, GenerationStep.Decoration.VEGETAL_DECORATION, CobblemonBiomeTags.IS_BEACH)
}
private fun of(id: String): ResourceKey<PlacedFeature> = ResourceKey.create(Registries.PLACED_FEATURE, cobblemonResource(id))
@@ -39,9 +39,6 @@ class SaccharineTreeFeature : Feature<BlockStateConfiguration>(BlockStateConfigu
if (isGenerating) {
val biome = worldGenLevel.getBiome(origin)
if (!biome.`is`(CobblemonBiomeTags.ALLOWED_BIOMES_SACCHARINE_TREE)) {
return false
}
val multiplier = when {
biome.`is`(CobblemonBiomeTags.HAS_APRICORNS_SPARSE) -> 0.1F
biome.`is`(CobblemonBiomeTags.HAS_APRICORNS_DENSE) -> 10F
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 807 B

Loaded 100 of 1063 files, more files were not shown because too many files have changed in this diff. Show more