Compare commits

..
1250 changed files with 4216 additions and 20658 deletions
-2
View File
@@ -12,11 +12,9 @@
### Developer
- baseScale in NPCs has been removed from classes and presets, and in the entity it is replaced with hitboxScale and renderScale.
- Added `EntityCallbacks` to `NPCEntity` and `PokemonEntity`. You can trigger custom callback types by using `[Entity].callbacks.process(...)`
### MoLang & Datapacks
- Added a `behaviours` datapack folder which motivates the Behaviour Editor screen on NPCs and Pokémon.
- Added "callback" support on PokemonEntity and NPCEntity that allows MoLang scripts to be executed in response to certain scenarios.
- Added very many behaviours to choose from.
- Removed many NPC presets that now exist as behaviours. This is a breaking change if you are using NPCs with these presets.
- NPCs with these presets should be modified to instead list under `"behaviours"` all the equivalent behaviours.
+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:
+2 -52
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
@@ -194,17 +187,6 @@
### Changes
- Changed pokemon caught and seen count to update based on the current pokedex being looked
- Renamed `chargeGainedPerTick` config to `secondsToChargeHealingMachine`.
- Added config setting `maxPokedexScanningDetectionRange` to control from what distance the player can scan Pokémon using the Pokédex.
- Added the `is_standing_on_blocks(depth, blocks...)` MoLang function, which returns whether the specified entity is standing on a specific block or set of blocks. Example usage: `q.is_standing_on_blocks(2, minecraft:sand)`
- Added config setting `hideUnimplementedPokemonInThePokedex` which hides unimplemented Pokémon from the Pokédex when set to true.
- Added molang particle queries for getting distance to targeted entities
- Added debug renderer for poseable entity locators
- Added time zone as a config option
- Added an optional MoValue to either use system timezone when set as true, or use the supplied string as a timezone for MoLang functions `date_local_time` and `date_of`. If the timezone supplied fails to parse this function now returns `0.0`.
### Additions
- Added time zone as a config option
- Added optional boolean to use time zone for MoLang function "date_local_time" and "date_of"
- Made Blocks of Gold count as Big Nuggets when held by a Pokémon (for Fling functionality)
- Players can now eat Sweet and Tart Apples, Whipped Dreams, and the Alcremie Sweets.
- Updated Sweet and Tart Apple sprites
@@ -249,8 +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.
- Removed Braised Vivichoke
### Fixes
- Fixed game crashing when removing national pokedex using datapacks
@@ -280,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
@@ -316,10 +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
- Fixed a vulnerability that could cause party and PC rollbacks under specific circumstances.
- Fixed a rare edge case where sorting your PC could be rolled back later.
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
@@ -338,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
{
@@ -354,14 +324,12 @@
- 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.
- Added `Pokemon#hyperTrainIV()` and `IVs#setHyperTrainedIV(Stat, Int)`
- `ElementalType` now implements `ShowdownIdentifiable` to ensure the communcation with showdown stays consistent (also in regards to TeraTypes)
- Pokemon no longer have a change observable
- `ElementalType` now implelments `ShowdownIdentifiable` to ensure the communcation with showdown stays consistent (also in regards to TeraTypes)
### MoLang & Datapacks
- The following usages for item predicates can now use item conditions like advancements do, you can learn about them in the [Minecraft wiki](https://minecraft.wiki/w/Advancement_definition#minecraft:filled_bucket)
@@ -380,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.
@@ -392,24 +357,9 @@
- 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.
- Added `prepare_{effect}` and `damage_{effect}` action effect hooks in battles for more battle particle effects.
- Added `q.has_argument(<argument_name>, [argument_value])` MoLang function to several battle-related action effect contexts.
- Added `q.has_argument_at(<index>, [argument_value])` MoLang function to several battle-related action effect contexts.
- Added `q.hit_count` MoLang function to move action effect contexts.
- Added `is_included`, `to_lower`, `to_upper`, and `string_length` as available Molang functions.
## [1.6.1 (January 26th, 2025)](#1-6-1)
@@ -1,17 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.duck;
public interface ChannelDuck {
void cobblemon$applyLowPassFilter(float gain, float hfGain);
void cobblemon$inverseAttenuation(float rolloffFactor);
void cobblemon$clearFilters();
// void cobblemon$exponentialAttenuation(float rolloff, float refDistance, float maxDistance);
}
@@ -1,69 +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.duck.ChannelDuck;
import com.mojang.blaze3d.audio.Channel;
import org.lwjgl.openal.AL10;
import org.lwjgl.openal.EXTEfx;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(Channel.class)
public abstract class ChannelMixin implements ChannelDuck {
@Shadow
private int source;
// Id of filter applied when sound is obstructed
private int cobblemon$lowPassFilterId = 0;
@Override
public void cobblemon$applyLowPassFilter(float gain, float hfGain) {
// Delete old filter if it exists
if (cobblemon$lowPassFilterId != 0) {
AL10.alSourcei(source, EXTEfx.AL_DIRECT_FILTER, 0); // Detach filter from source
EXTEfx.alDeleteFilters(cobblemon$lowPassFilterId); // Delete old filter
cobblemon$lowPassFilterId = 0;
}
// Generate a fresh filter
int filterId = EXTEfx.alGenFilters();
if (!EXTEfx.alIsFilter(filterId)) {
System.err.println("Failed to create OpenAL low-pass filter");
return;
}
EXTEfx.alFilteri(filterId, EXTEfx.AL_FILTER_TYPE, EXTEfx.AL_FILTER_LOWPASS);
EXTEfx.alFilterf(filterId, EXTEfx.AL_LOWPASS_GAIN, gain);
EXTEfx.alFilterf(filterId, EXTEfx.AL_LOWPASS_GAINHF, hfGain);
AL10.alSourcei(source, EXTEfx.AL_DIRECT_FILTER, filterId);
// Save the new filter ID
cobblemon$lowPassFilterId = filterId;
}
@Override
public void cobblemon$inverseAttenuation(float rolloffFactor) {
AL10.alSourcei(source, AL10.AL_DISTANCE_MODEL, AL10.AL_INVERSE_DISTANCE);
AL10.alSourcef(source, AL10.AL_ROLLOFF_FACTOR, rolloffFactor);
AL10.alSourcef(source, AL10.AL_REFERENCE_DISTANCE, 1.0f);
AL10.alSourcef(source, AL10.AL_MAX_DISTANCE, 0.0f);
}
@Override
public void cobblemon$clearFilters() {
AL10.alSourcei(source, EXTEfx.AL_DIRECT_FILTER, 0);
if (EXTEfx.alIsFilter(cobblemon$lowPassFilterId)) {
EXTEfx.alDeleteFilters(cobblemon$lowPassFilterId);
cobblemon$lowPassFilterId = 0;
}
}
}
@@ -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);
}
}
@@ -8,33 +8,23 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.api.riding.sound.RideAttenuationModel;
import com.cobblemon.mod.common.api.riding.sound.RideLoopSound;
import com.cobblemon.mod.common.client.sound.BattleMusicController;
import com.cobblemon.mod.common.duck.ChannelDuck;
import com.cobblemon.mod.common.duck.SoundEngineDuck;
import com.google.common.collect.Multimap;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.audio.Channel;
import net.minecraft.client.resources.sounds.Sound;
import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.client.resources.sounds.TickableSoundInstance;
import net.minecraft.client.sounds.ChannelAccess;
import net.minecraft.client.sounds.SoundEngine;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import java.util.Iterator;
import java.util.Map;
@Mixin(SoundEngine.class)
@@ -55,86 +45,6 @@ public abstract class SoundEngineMixin implements SoundEngineDuck {
@Shadow
public abstract boolean isActive(SoundInstance sound);
@Inject(method = "play",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/sounds/ChannelAccess$ChannelHandle;execute(Ljava/util/function/Consumer;)V"
),
cancellable = true
)
private void cobblemon$overrideChannelExecute(
SoundInstance sound,
CallbackInfo ci,
@Local(name = "h") float h,
@Local(name = "i") float i,
@Local(name = "g") float g,
@Local(name = "bl2") boolean bl2,
@Local(name = "bl3") boolean bl3,
@Local(name = "bl") boolean bl,
@Local(name = "vec3") Vec3 vec3,
@Local(name = "channelHandle") ChannelAccess.ChannelHandle handle
) {
if (!(sound instanceof RideLoopSound rideSound)) return;
if (handle == null) return;
handle.execute((channel) -> {
channel.setPitch(i);
channel.setVolume(h);
if (rideSound.getRideAttenuation() == RideAttenuationModel.LINEAR) {
channel.linearAttenuation(g);
} else if (rideSound.getRideAttenuation() == RideAttenuationModel.EXPONENTIAL){
((ChannelDuck) channel).cobblemon$inverseAttenuation(g);
} else {
channel.disableAttenuation();
}
// Apply low pass filter if the rideSound dictate that it should be muffled
if (rideSound.getShouldMuffle()) {
((ChannelDuck) channel).cobblemon$applyLowPassFilter(1.0f, rideSound.getMuffleAmount());
} else {
((ChannelDuck) channel).cobblemon$clearFilters();
}
channel.setLooping(bl2 && !bl3);
channel.setSelfPosition(vec3);
channel.setRelative(bl);
});
}
@Inject(
method = "tickNonPaused",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/sounds/ChannelAccess$ChannelHandle;execute(Ljava/util/function/Consumer;)V",
ordinal = 0
)
)
private void cobblemon$tickableSoundTick(
CallbackInfo ci,
@Local(name = "tickableSoundInstance") TickableSoundInstance instance,
@Local(name = "channelHandle", ordinal = 0) ChannelAccess.ChannelHandle handle,
@Local(name = "f", ordinal = 0) float volume,
@Local(name = "g", ordinal = 0) float pitch,
@Local(name = "vec3", ordinal = 0) Vec3 vec3
) {
if (!(instance instanceof RideLoopSound rideSound)) return;
handle.execute(channel -> {
channel.setVolume(volume);
channel.setPitch(pitch);
channel.setSelfPosition(vec3);
// Apply low pass filter if the rideSound dictate that it should be muffled
if (rideSound.getShouldMuffle()) {
((ChannelDuck) channel).cobblemon$applyLowPassFilter(1.0f, rideSound.getMuffleAmount());
} else {
((ChannelDuck) channel).cobblemon$clearFilters();
}
});
}
/** Resumes the Source belonging to the queried SoundInstance. */
private void resume(SoundInstance sound) {
ChannelAccess.ChannelHandle sourceManager = this.instanceToChannel.get(sound);
@@ -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;
}
}
@@ -75,7 +75,6 @@ import com.cobblemon.mod.common.config.constraint.IntConstraint
import com.cobblemon.mod.common.config.starter.StarterConfig
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.events.AdvancementHandler
import com.cobblemon.mod.common.events.EntityCallbackHandler
import com.cobblemon.mod.common.events.FlowHandler
import com.cobblemon.mod.common.events.PokedexHandler
import com.cobblemon.mod.common.events.ServerTickHandler
@@ -314,7 +313,6 @@ object Cobblemon {
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit(Unit)
FlowHandler.setup()
EntityCallbackHandler.setup()
ifDedicatedServer {
isDedicatedServer = true
@@ -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 {
@@ -389,28 +389,40 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val SWEET_HEART = noSettingsItem("sweet_heart") // todo make a SweetHeartItem class for breeding purposes
@JvmField
val TASTY_TAIL = create("tasty_tail", foodItem(3, 0.3f))
val TASTY_TAIL = noSettingsItem("tasty_tail")
@JvmField
val PEWTER_CRUNCHIES = regionalFoodItem("pewter_crunchies", 16, 7, 0.343f, false)
@JvmField
val RAGE_CANDY_BAR = regionalFoodItem("rage_candy_bar", 16, 8, 0.2f, false)
@JvmField
val LAVA_COOKIE = regionalFoodItem("lava_cookie", 16, 7, 0.343f, false)
@JvmField
val OLD_GATEAU = regionalFoodItem("old_gateau", 16, 9, 0.1335f, false)
@JvmField
val CASTELIACONE = regionalFoodItem("casteliacone", 16, 5, .28f, false)
@JvmField
val LUMIOSE_GALETTE = regionalFoodItem("lumiose_galette", 16, 8, 0.2f, false)
@JvmField
val BIG_MALASADA = regionalFoodItem("big_malasada", 16, 7, 0.343f, false)
@JvmField
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 16, 10, 0.6f, false, ItemStack(Items.BOWL, 1))
@JvmField
val JUBILIFE_MUFFIN = regionalFoodItem("jubilife_muffin", 16, 8, 0.2f, false)
@JvmField
val OPEN_FACED_SANDWICH = regionalFoodItem("open_faced_sandwich", 16, 13, 0.5f, false)
@JvmField // todo get final values for this from vera
val PEWTER_CRUNCHIES = regionalFoodItem("pewter_crunchies", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f, false)
@JvmField // todo get final values for this from vera
val RAGE_CANDY_BAR = regionalFoodItem("rage_candy_bar", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f, false)
@JvmField // todo get final values for this from vera
val LAVA_COOKIE = regionalFoodItem("lava_cookie", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
@JvmField // todo get final values for this from vera
val OLD_GATEAU = regionalFoodItem("old_gateau", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
@JvmField // todo get final values for this from vera
val CASTELIACONE = regionalFoodItem("casteliacone", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
@JvmField // todo get final values for this from vera
val SHALOUR_SABLE = regionalFoodItem("shalour_sable", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
@JvmField // todo get final values for this from vera
val LUMIOSE_GALETTE = regionalFoodItem("lumiose_galette", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
@JvmField // todo get final values for this from vera
val BIG_MALASADA = regionalFoodItem("big_malasada", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
@JvmField // todo get final values for this from vera
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f, false, ItemStack(Items.BOWL, 1))
@JvmField // todo get final values for this from vera
val JUBILIFE_MUFFIN = regionalFoodItem("jubilife_muffin", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
@JvmField // todo get final values for this from vera
val OPEN_FACED_SANDWICH = regionalFoodItem("open_faced_sandwich", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
// todo we might need to wait on these for later? These impact battles and may be harder to do
/*@JvmField
@@ -423,22 +435,30 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val POTATO_MOCHI = create("potato_mochi", CobblemonItem(Properties().stacksTo(16)
.food(FoodProperties.Builder()
.nutrition(8)
.saturationModifier(0.2375F)
.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
.alwaysEdible()
.build())))
@JvmField
val CANDIED_APPLE = create("candied_apple", CobblemonItem(Properties().stacksTo(16)
.food(FoodProperties.Builder()
.nutrition(6)
.saturationModifier(0.2335F)
.usingConvertsTo(Items.STICK)
.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 CANDIED_BERRY = create("candied_berry", CobblemonItem(Properties().stacksTo(16)
.food(FoodProperties.Builder()
.nutrition(5)
.saturationModifier(0.22F)
.usingConvertsTo(Items.STICK)
.nutrition(10) // todo get final values for this from vera
.saturationModifier(1.2F) // todo get final values for this from vera
.alwaysEdible()
.build())))
//@JvmField
@@ -665,12 +685,14 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val MEDICINAL_LEEK = heldItem("medicinal_leek", MedicinalLeekItem(CobblemonBlocks.MEDICINAL_LEEK, Item.Properties().food(FoodProperties.Builder().fast().nutrition(1).saturationModifier(0.2f).build())), "leek")
@JvmField
val ROASTED_LEEK = compostableItem("roasted_leek", CobblemonItem(Item.Properties().food(FoodProperties.Builder().fast().nutrition(3).saturationModifier(0.2f).build())), 0.85f)
val ROASTED_LEEK = compostableItem("roasted_leek", CobblemonItem(Item.Properties().food(FoodProperties.Builder().fast().nutrition(3).saturationModifier(0.3f).build())), 0.85f)
@JvmField
val BRAISED_VIVICHOKE = compostableItem("braised_vivichoke", foodItem(6, 0.6F), 0.85f)
@JvmField
val VIVICHOKE_DIP = create("vivichoke_dip", object : CobblemonItem(Properties().stacksTo(1)
.food(FoodProperties.Builder()
.nutrition(10)
.saturationModifier(0.6F)
.saturationModifier(1.2F)
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.usingConvertsTo(Items.BOWL)
@@ -681,7 +703,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
}
})
@JvmField
val ENERGY_ROOT = compostableItem("energy_root", EnergyRootItem(CobblemonBlocks.ENERGY_ROOT, Properties()), 0.60f)
val ENERGY_ROOT = compostableItem("energy_root", EnergyRootItem(CobblemonBlocks.ENERGY_ROOT, Item.Properties().food(FoodProperties.Builder().nutrition(1).fast().saturationModifier(0.2f).build())), 0.60f)
@JvmField
val REVIVAL_HERB = compostableItem("revival_herb", RevivalHerbItem(CobblemonBlocks.REVIVAL_HERB))
@JvmField
@@ -711,7 +733,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val HEAL_POWDER = create("heal_powder", HealPowderItem())
@JvmField
val LEEK_AND_POTATO_STEW = create("leek_and_potato_stew", CobblemonItem(Item.Properties().food(FoodProperties.Builder().nutrition(6).saturationModifier(0.6f).usingConvertsTo(Items.BOWL).build()).stacksTo(1)))
val LEEK_AND_POTATO_STEW = create("leek_and_potato_stew", CobblemonItem(Item.Properties().food(FoodProperties.Builder().nutrition(8).saturationModifier(0.6f).usingConvertsTo(Items.BOWL).build()).stacksTo(1)))
@JvmField
val REVIVE = create("revive", ReviveItem(max = false))
@JvmField
@@ -1522,6 +1544,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
stacksTo: Int,
nutrition: Int,
saturationModifier: Float,
effect: MobEffectInstance? = null,
effectChance: Float? = null,
alwaysEdible: Boolean = false,
convertsToOnUse: ItemStack? = null
): RegionalFoodItem {
@@ -1529,6 +1553,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
.nutrition(nutrition)
.saturationModifier(saturationModifier)
if (effect != null && effectChance != null) {
foodPropertiesBuilder.effect(effect, effectChance)
}
if (alwaysEdible == true) {
foodPropertiesBuilder.alwaysEdible()
}
@@ -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)
@@ -275,11 +275,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
RELIC_COIN_SACK_STEP
)
@JvmField
val RIDE_LOOP_LEATHER = create("ride.loop.leather")
@JvmField
val RIDE_LOOP_PLUMAGE = create("ride.loop.plumage")
@JvmField
val MOVE_QUICKATTACK_TARGET = this.create("move.quickattack.target")
@JvmField
@@ -324,8 +319,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val IMPACT_WATER = this.create("impact.water")
@JvmField
val TUMBLESTONE_SOUNDS = SoundType(1f, 1.1f,
TUMBLESTONE_BREAK,
@@ -8,15 +8,8 @@
package com.cobblemon.mod.common.api.ai
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.ai.config.BehaviourConfig
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
import com.cobblemon.mod.common.util.resolve
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.serialization.Dynamic
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.LivingEntity
@@ -35,9 +28,6 @@ class BehaviourConfigurationContext {
val memories = mutableSetOf<MemoryModuleType<*>>()
val sensors = mutableSetOf<SensorType<*>>()
val onAddScripts = mutableListOf<ResourceLocation>()
val onAdd = mutableListOf<ExpressionLike>()
fun getOrCreateActivity(activity: Activity): ActivityConfigurationContext {
return activities.firstOrNull { it.activity == activity } ?: ActivityConfigurationContext(activity).also(activities::add)
}
@@ -58,14 +48,6 @@ class BehaviourConfigurationContext {
this.sensors.addAll(sensors)
}
fun addOnAddScript(script: ResourceLocation) {
onAddScripts.add(script)
}
fun addOnAddScript(script: ExpressionLike) {
onAdd.add(script)
}
fun apply(entity: LivingEntity, behaviourConfigs: List<BehaviourConfig>, dynamic: Dynamic<*>) {
if (entity is MoLangScriptingEntity) {
@@ -80,11 +62,6 @@ class BehaviourConfigurationContext {
// Apply the brain config
if (entity is MoLangScriptingEntity) {
brain = entity.assignNewBrainWithMemoriesAndSensors(dynamic, memories, sensors)
val runtime = MoLangRuntime().setup()
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
onAddScripts.forEach { CobblemonScripts.run(it, runtime) }
onAdd.forEach { runtime.resolve(it) }
}
activities.forEach { it.apply(entity) }
brain.setCoreActivities(coreActivities)
@@ -9,17 +9,7 @@
package com.cobblemon.mod.common.api.ai
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.resolve
import net.minecraft.world.entity.Entity
/**
* This is used in the AI system to store optional additional information about the attack target,
@@ -40,32 +30,5 @@ import net.minecraft.world.entity.Entity
*/
class CobblemonAttackTargetData(
/** An expression that returns false when the target is no longer worth attacking. */
var shouldContinue: Expression = "true".asExpression(),
var hitsUntilDisengage: Int = -1, // -1 means no limit
var disengageAction: (entity: Entity) -> Unit = {}
) {
val struct = ObjectValue<CobblemonAttackTargetData>(this).also {
it.addFunction("hits_until_disengage") { DoubleValue(hitsUntilDisengage.toDouble()) }
it.addFunction("set_hits_until_disengage") { params ->
val value = params.getDouble(0)
hitsUntilDisengage = value.toInt()
DoubleValue(hitsUntilDisengage.toDouble())
}
it.addFunction("set_disengage_action") { params ->
val script = params.getString(0).asIdentifierDefaultingNamespace()
val function = CobblemonScripts.scripts[script] ?: run {
Cobblemon.LOGGER.warn("set_disengage_action was given script '$script' which does not exist.")
return@addFunction DoubleValue.ZERO
}
disengageAction = { entity ->
val runtime = MoLangRuntime().setup()
runtime.environment.query.addFunction("entity") { entity.asMostSpecificMoLangValue() }
runtime.resolve(function)
}
DoubleValue.ONE
}
it.addFunction("unset_disengage_action") { params ->
disengageAction = {}
}
}
}
val shouldContinue: Expression = "true".asExpression()
)
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.resolve
import com.cobblemon.mod.common.util.withQueryValue
import com.google.gson.annotations.SerializedName
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.LivingEntity
@@ -29,35 +28,23 @@ class CobblemonBehaviour(
val visible: Boolean = true,
val entityType: ResourceLocation? = null,
val configurations: List<BehaviourConfig> = mutableListOf(),
// I feel like the onAdd+onAddScript etc could be merged using some interface and a clever deserializer,
// detect if it's a ResourceLocation and failing that, Expression. The on[..]Script fields are kinda fringe though.
@SerializedName("onRemove", alternate = ["undo"])
val onRemove: ExpressionLike? = null,
@SerializedName("onRemoveScript", alternate = ["undoScript"])
val onRemoveScript: ResourceLocation? = null,
val onAdd: ExpressionLike? = null,
val onAddScript: ResourceLocation? = null,
val undo: ExpressionLike? = null,
val undoScript: ResourceLocation? = null,
) {
fun canBeApplied(entity: LivingEntity) = entityType?.let { entityType == entity.type.builtInRegistryHolder().unwrapKey().get().location() } != false
fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (onAdd != null) {
behaviourConfigurationContext.addOnAddScript(onAdd)
}
if (onAddScript != null) {
behaviourConfigurationContext.addOnAddScript(onAddScript)
}
configurations.forEach { it.configure(entity, behaviourConfigurationContext) }
}
/** Undoes anything that needs undoing once this configuration is being removed from an entity that had it before. */
fun onRemove(entity: LivingEntity) {
fun undo(entity: LivingEntity) {
val runtime = MoLangRuntime().setup()
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (onRemoveScript != null) {
CobblemonScripts.run(onRemoveScript, runtime)
if (undoScript != null) {
CobblemonScripts.run(undoScript, runtime)
}
if (onRemove != null) {
runtime.resolve(onRemove)
if (undo != null) {
runtime.resolve(undo)
}
}
}
@@ -1,29 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.phys.Vec3
/**
* Wraps around [BlockPosTracker] to add additional flags for Cobblemon-specific behavior.
*
* @author Hiroku
* @since August 4th, 2025
*/
class CobblemonBlockPosTracker(
pos: Vec3,
val flags: Set<String> = emptySet(),
) : BlockPosTracker(pos) {
companion object {
const val BATTLE_LOOK_BYPASS_FLAG = "battle_look_bypass"
}
fun isBattleLookBypass() = flags.contains(BATTLE_LOOK_BYPASS_FLAG)
}
@@ -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,56 +54,35 @@ 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))
@@ -13,7 +13,6 @@ import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.CobblemonBuildDetails
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
@@ -78,7 +77,7 @@ open class PokemonBattle(
val side2: BattleSide
) {
/** Whether logging will be silenced for this battle. */
var mute = !CobblemonBuildDetails.SNAPSHOT
var mute = true
val struct = this.asMoLangValue()
val runtime = MoLangRuntime().also { it.environment.query = struct }
@@ -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
}
)
}
)
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.events.pokeball
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.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -21,9 +20,8 @@ class ThrownPokeballHitEvent(
val pokeBall : EmptyPokeBallEntity,
val pokemon : PokemonEntity
) : Cancelable() {
val functions = moLangFunctionMap(
"pokeball" to { pokeBall.struct },
"pokemon" to { pokemon.asMoLangValue() },
cancelFunc
val context = mutableMapOf(
"pokeball" to pokeBall.struct,
"pokemon" to pokemon.asMoLangValue()
)
}
@@ -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,18 +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.CobblemonBlockPosTracker
import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
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.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
@@ -51,14 +45,11 @@ 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
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.api.storage.PokemonStore
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
@@ -68,13 +59,10 @@ 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.BattleRegistry
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
import com.cobblemon.mod.common.entity.npc.NPCEntity
@@ -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
@@ -143,12 +130,6 @@ import net.minecraft.world.level.levelgen.Heightmap
import net.minecraft.world.level.pathfinder.PathType
import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec3
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.util.*
/**
* Holds a bunch of useful MoLang trickery that can be used or extended in API
@@ -174,20 +155,6 @@ object MoLangFunctions {
val replace = params.getString(2)
return@Function StringValue(text.replace(search, replace))
},
"is_included" to java.util.function.Function { params ->
val text = params.getString(0)
val search = params.getString(1)
return@Function DoubleValue(text.contains(search))
},
"to_lower" to java.util.function.Function { params ->
return@Function StringValue(params.getString(0).lowercase())
},
"to_upper" to java.util.function.Function { params ->
return@Function StringValue(params.getString(0).uppercase())
},
"string_length" to java.util.function.Function { params ->
return@Function DoubleValue(params.getString(0).length)
},
"is_blank" to java.util.function.Function { params ->
val arg = params.get<MoValue>(0)
return@Function DoubleValue((arg is StringValue && (arg.value.isBlank() || arg.value.toDoubleOrNull() == 0.0)) || (arg is DoubleValue && arg.value == 0.0))
@@ -225,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
@@ -295,44 +220,16 @@ object MoLangFunctions {
DoubleValue(System.currentTimeMillis())
},
// the rest of the world use dd/MM/yyyy grow up america (this comment was generated by copilot)
"date_local_time" to java.util.function.Function { params ->
val useTimeZone = params.get<MoValue>(0)
var timezone: ZoneId? = null
if (useTimeZone is DoubleValue){
timezone = if (useTimeZone.value == 1.0) Cobblemon.config.timeZoneId else null
} else if (useTimeZone is StringValue){
try {
timezone = ZoneId.of(useTimeZone.value)
} catch (_: Exception) {
Cobblemon.LOGGER.error("Error parsing timezone: ${useTimeZone.value}")
return@Function DoubleValue.ZERO
}
}
val date: LocalDateTime = if (timezone != null) LocalDateTime.now(timezone) else LocalDateTime.now()
val formatted = DateTimeFormatter.ofPattern("dd/MM/uuuu").format(date)
"date_local_time" to java.util.function.Function { _ ->
val time = System.currentTimeMillis()
val date = java.util.Date(time)
val formatted = java.text.SimpleDateFormat("DD/MM/YYYY").format(date)
StringValue(formatted)
},
"date_of" to java.util.function.Function { params ->
val time = params.getDouble(0).toLong()
val useTimeZone = params.get<MoValue>(1)
var timezone: ZoneId? = null
if (useTimeZone is DoubleValue){
timezone = if (useTimeZone.value == 1.0) Cobblemon.config.timeZoneId else null
} else if (useTimeZone is StringValue){
try {
timezone = ZoneId.of(useTimeZone.value)
} catch (_: Exception) {
Cobblemon.LOGGER.error("Error parsing timezone: ${useTimeZone.value}")
return@Function DoubleValue.ZERO
}
}
val date = java.util.Date(time)
val instant: Instant = date.toInstant()
val formatted = DateTimeFormatter.ofPattern("dd/MM/uuuu").format(LocalDateTime.ofInstant(instant,
timezone ?: ZoneOffset.systemDefault()
))
val formatted = java.text.SimpleDateFormat("DD/MM/YYYY").format(date)
StringValue(formatted)
},
"date_is_after" to java.util.function.Function { params ->
@@ -353,12 +250,6 @@ object MoLangFunctions {
{ worldHolder ->
val world = worldHolder.value()
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("is_time_of_day") { params ->
val timeOfDay = TimeRange.timeRanges[params.getString(0).lowercase()]
?: return@put DoubleValue.ZERO
val time = world.dayTime % 24000
return@put DoubleValue(timeOfDay.contains(time.toInt()))
}
map.put("game_time") { _ -> DoubleValue(world.gameTime.toDouble()) }
map.put("time_of_day") {
val time = world.dayTime % 24000
@@ -527,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) }
@@ -646,55 +529,11 @@ 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
}
)
val itemStackFunctions = mutableListOf<(ItemStack, RegistryAccess) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>>(
val itemStackFunctions = mutableListOf<(ItemStack, RegistryAccess) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ stack, registryAccess ->
val itemRegistry = registryAccess.registryOrThrow(Registries.ITEM)
val holder = itemRegistry.wrapAsHolder(stack.item)
@@ -713,7 +552,7 @@ object MoLangFunctions {
}
)
val entityFunctions: MutableList<(Entity) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
val entityFunctions: MutableList<(Entity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ entity ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("uuid") { _ -> StringValue(entity.uuid.toString()) }
@@ -742,34 +581,6 @@ object MoLangFunctions {
return@put owner?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO
}
if (entity is MoLangScriptingEntity) {
map.put("add_callback") { params ->
val type = params.getString(0).asIdentifierDefaultingNamespace()
val callback = params.getString(1).asIdentifierDefaultingNamespace()
val allowDuplicates = params.getBooleanOrNull(2) ?: false
entity.callbacks.addCallback(type, callback, allowDuplicates)
}
map.put("remove_callback") { params ->
val type = params.getString(0).asIdentifierDefaultingNamespace()
val callback = params.getString(1).asIdentifierDefaultingNamespace()
entity.callbacks.removeCallback(type, callback)
}
map.put("has_callback") { params ->
val type = params.getString(0).asIdentifierDefaultingNamespace()
val callback = params.getString(1).asIdentifierDefaultingNamespace()
return@put DoubleValue(callback in (entity.callbacks[type] ?: emptySet()))
}
map.put("clear_callbacks") { params ->
val type = params.getStringOrNull(0)?.asIdentifierDefaultingNamespace()
if (type != null) {
entity.callbacks[type]?.clear()
} else {
entity.callbacks.clear()
}
return@put DoubleValue.ONE
}
}
if (entity is PathfinderMob) {
map.put("walk_to") { params ->
val x = params.getDouble(0)
@@ -951,7 +762,7 @@ object MoLangFunctions {
}
)
val livingEntityFunctions: MutableList<(LivingEntity) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>>(
val livingEntityFunctions: MutableList<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ entity ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("is_living_entity") { DoubleValue.ONE }
@@ -959,80 +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("look_at_position") { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
val duration = params.getIntOrNull(3) ?: 20
val flags = params.params.subList(4, params.params.size).map { it.asString() }
val brain = entity.brain
brain.setMemoryWithExpiry(MemoryModuleType.LOOK_TARGET, CobblemonBlockPosTracker(Vec3(x, y, z), flags.toSet()), duration.toLong())
}
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("set_uuid_memory") { params ->
val memory = params.getString(0).asIdentifierDefaultingNamespace().let(BuiltInRegistries.MEMORY_MODULE_TYPE::get) as MemoryModuleType<UUID>
val uuid = params.getString(1).asUUID ?: return@put DoubleValue.ZERO
val expiry = params.getIntOrNull(2) ?: -1
if (expiry != -1) {
entity.brain.setMemoryWithExpiry(memory, uuid, expiry.toLong())
} else {
entity.brain.setMemory(memory, uuid)
}
return@put DoubleValue.ONE
}
map.put("erase_memory") { params ->
val memories = params.params.map { it.asString().asIdentifierDefaultingNamespace() }.map(BuiltInRegistries.MEMORY_MODULE_TYPE::get)
memories.forEach { entity.brain.eraseMemory(it) }
return@put DoubleValue.ONE
}
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
@@ -1339,23 +1076,6 @@ object MoLangFunctions {
}
)
val battleMessageFunctions = mutableListOf<(BattleMessage) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ message ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("has_argument_at") { params ->
val index = params.getInt(0)
val value = params.getStringOrNull(1) ?: ""
return@put DoubleValue(value == message.argumentAt(index))
}
map.put("has_argument") { params ->
val argumentName = params.getString(0)
val value = params.getStringOrNull(1) ?: ""
return@put DoubleValue(value == message.optionalArgument(argumentName))
}
return@mutableListOf map
}
)
val pokemonFunctions = mutableListOf<(Pokemon) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ pokemon ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
@@ -1365,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) {
@@ -1410,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())
}
@@ -1505,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].
@@ -1540,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()
@@ -1593,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
}
)
@@ -1802,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
})
@@ -1820,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
@@ -2005,22 +1568,7 @@ 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) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
val pokemonPropertiesFunctions: MutableList<(PokemonProperties) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ props ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
@@ -2078,7 +1626,7 @@ object MoLangFunctions {
}
)
val evolutionFunctions: MutableList<(Evolution) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
val evolutionFunctions: MutableList<(Evolution) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ evolution ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
@@ -2148,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,
@@ -2314,14 +1853,6 @@ object MoLangFunctions {
return this
}
fun QueryStruct.addBattleMessageFunctions(battleMessage: BattleMessage): QueryStruct {
val addedFunctions = battleMessageFunctions
.flatMap { it.invoke(battleMessage).entries }
.associate { it.key to it.value }
functions.putAll(addedFunctions)
return this
}
fun QueryStruct.addPokemonEntityFunctions(pokemonEntity: PokemonEntity): QueryStruct {
val addedFunctions = pokemonEntityFunctions
.flatMap { it.invoke(pokemonEntity).entries }
@@ -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
@@ -37,7 +37,6 @@ import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.types.ElementalType
@@ -128,7 +127,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
.registerTypeAdapter(RidingBehaviourSettings::class.java, RidingBehaviourSettingsAdapter)
.registerTypeAdapter(RidingStatDefinition::class.java, RidingStatDefinitionAdapter)
.registerTypeAdapter(RideSoundSettingsList::class.java, RideSoundSettingsListAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.disableHtmlEscaping()
.enableComplexMapKeySerialization()
@@ -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) }
}
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.api.riding.behaviour
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.resources.ResourceLocation
@@ -89,8 +88,6 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
fun shouldRotatePlayerHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
fun getRideSounds(settings: Settings, state: State, vehicle: PokemonEntity): RideSoundSettingsList
fun maxUpStep(settings: Settings, state: State, vehicle: PokemonEntity): Float? = null
fun canStopRiding(settings: Settings, state: State, vehicle: PokemonEntity, passenger: Player): Boolean = true
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.api.riding.behaviour
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.resources.ResourceLocation
@@ -191,11 +190,6 @@ class RidingController<Settings : RidingBehaviourSettings, State : RidingBehavio
return behaviour.maxUpStep(settings, state, vehicle)
}
override fun getRideSounds(settings: Settings, state: State, vehicle: PokemonEntity): RideSoundSettingsList {
if (!isActive(settings, state, vehicle)) return RideSoundSettingsList()
return behaviour.getRideSounds(settings, state, vehicle)
}
override fun createDefaultState(settings: Settings): State {
return behaviour.createDefaultState(settings)
}
@@ -16,8 +16,6 @@ import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideLoopSound
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
@@ -25,7 +23,6 @@ import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvent
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
@@ -472,14 +469,6 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
return true
}
override fun getRideSounds(
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: BirdSettings) = BirdState()
}
@@ -502,11 +491,8 @@ class BirdSettings : RidingBehaviourSettings {
var glidespeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 2.0, 1.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(infiniteAltitude)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(glidespeedExpr)
@@ -518,7 +504,6 @@ class BirdSettings : RidingBehaviourSettings {
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
infiniteAltitude = buffer.readExpression()
infiniteStamina = buffer.readExpression()
glidespeedExpr = buffer.readExpression()
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -188,10 +187,6 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
return false
}
override fun getRideSounds(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: GliderSettings) = RidingBehaviourState()
}
@@ -207,18 +202,14 @@ class GliderSettings : RidingBehaviourSettings {
var canStrafe: Expression = "false".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(glideSpeed)
buffer.writeExpression(speed)
buffer.writeExpression(canStrafe)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
glideSpeed = buffer.readExpression()
speed = buffer.readExpression()
canStrafe = buffer.readExpression()
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
@@ -273,10 +272,6 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
return false
}
override fun getRideSounds(settings: HelicopterSettings, state: RidingBehaviourState, vehicle: PokemonEntity): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: HelicopterSettings) = RidingBehaviourState()
}
@@ -296,11 +291,8 @@ class HelicopterSettings : RidingBehaviourSettings {
var speed: Expression = "1.0".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(gravity)
buffer.writeExpression(horizontalAcceleration)
buffer.writeExpression(verticalVelocity)
@@ -308,7 +300,6 @@ class HelicopterSettings : RidingBehaviourSettings {
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
gravity = buffer.readExpression()
horizontalAcceleration = buffer.readExpression()
verticalVelocity = buffer.readExpression()
@@ -13,8 +13,6 @@ import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideLoopSound
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
@@ -460,24 +458,12 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
return false
}
override fun getRideSounds(
settings: HoverSettings,
state: HoverState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: HoverSettings) = HoverState()
}
class HoverSettings : RidingBehaviourSettings {
override val key = HoverBehaviour.KEY
var rideSound: ResourceLocation = "ride.loop.saucer".asIdentifierDefaultingNamespace()
var volumeExpr: Expression = "math.pow(math.min(q.ride_velocity() / 0.8, 1.0),2)".asExpression()
var pitchExpr: Expression = "math.pow(math.min(1.0 + 0.2*(q.ride_velocity() / 0.8), 1.2),2)".asExpression()
var canJump = "true".asExpression()
private set
@@ -500,11 +486,8 @@ class HoverSettings : RidingBehaviourSettings {
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
@@ -513,7 +496,6 @@ class HoverSettings : RidingBehaviourSettings {
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
@@ -21,8 +21,6 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
@@ -382,22 +380,10 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
return false
}
override fun shouldRotatePlayerHead(
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity
): Boolean {
override fun shouldRotatePlayerHead(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
return false
}
override fun getRideSounds(
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: JetSettings) = JetState()
override fun damageOnCollision(
@@ -442,11 +428,8 @@ class JetSettings : RidingBehaviourSettings {
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 60.0, 10.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(gravity)
buffer.writeExpression(minSpeed)
buffer.writeExpression(handlingYawExpr)
@@ -460,7 +443,6 @@ class JetSettings : RidingBehaviourSettings {
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
gravity = buffer.readExpression()
minSpeed = buffer.readExpression()
handlingYawExpr = buffer.readExpression()
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.composite
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies.CompositeRidingStrategies
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.adapters.RidingBehaviourSettingsAdapter
@@ -328,16 +327,6 @@ class CompositeBehaviour : RidingBehaviour<CompositeSettings, CompositeState> {
}
}
override fun getRideSounds(
settings: CompositeSettings,
state: CompositeState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
behaviour.getRideSounds(behaviourSettings, behaviourState, vehicle)
}
}
override fun isActive(
settings: CompositeSettings,
state: CompositeState,
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
@@ -440,14 +439,6 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
return true
}
override fun getRideSounds(
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: HorseSettings) = HorseState()
}
@@ -476,11 +467,8 @@ class HorseSettings : RidingBehaviourSettings {
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
@@ -489,7 +477,6 @@ class HorseSettings : RidingBehaviourSettings {
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
@@ -15,7 +15,6 @@ import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -270,14 +269,6 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
return false
}
override fun getRideSounds(
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: VehicleSettings) = VehicleState()
}
@@ -308,11 +299,8 @@ class VehicleSettings : RidingBehaviourSettings {
var lookYawLimit = "101".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(canJump)
buffer.writeExpression(jumpVector[0])
buffer.writeExpression(jumpVector[1])
@@ -326,7 +314,6 @@ class VehicleSettings : RidingBehaviourSettings {
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
canJump = buffer.readExpression()
jumpVector = listOf(
buffer.readExpression(),
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -338,14 +337,6 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
return true
}
override fun getRideSounds(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun maxUpStep(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity) = 0f
override fun createDefaultState(settings: BoatSettings) = BoatState()
@@ -354,7 +345,6 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
class BoatSettings : RidingBehaviourSettings {
override val key = BoatBehaviour.KEY
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
var terminalVelocity = "-2.0".asExpression()
private set
@@ -364,12 +354,10 @@ class BoatSettings : RidingBehaviourSettings {
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(terminalVelocity)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
terminalVelocity = buffer.readExpression()
}
}
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
@@ -223,14 +222,6 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
return false
}
override fun getRideSounds(
settings: BurstSettings,
state: BurstState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: BurstSettings) = BurstState()
}
@@ -240,16 +231,12 @@ class BurstSettings : RidingBehaviourSettings {
var dashSpeed = 1F
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeFloat(dashSpeed)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
dashSpeed = buffer.readFloat()
}
}
@@ -15,7 +15,6 @@ import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
@@ -262,14 +261,6 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
return false
}
override fun getRideSounds(
settings: DolphinSettings,
state: DolphinState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: DolphinSettings) = DolphinState()
}
@@ -295,11 +286,8 @@ class DolphinSettings : RidingBehaviourSettings {
var strafeFactor = "0.2".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(canJump)
buffer.writeExpression(jumpVector[0])
buffer.writeExpression(jumpVector[1])
@@ -311,7 +299,6 @@ class DolphinSettings : RidingBehaviourSettings {
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
canJump = buffer.readExpression()
jumpVector = listOf(
buffer.readExpression(),
@@ -1,24 +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.riding.sound;
/**
* Enum of attenuations models for ride sounds
*
* @author Jackowes
* @since May 11th, 2025
*/
enum class RideAttenuationModel {
NONE,
LINEAR,
EXPONENTIAL
}
@@ -1,190 +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.riding.sound
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.resolveDouble
import net.minecraft.client.Minecraft
import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance
import net.minecraft.client.resources.sounds.SoundInstance
import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundSource
import net.minecraft.util.Mth
import net.minecraft.util.Mth.lerp
import net.minecraft.world.level.ClipContext
import net.minecraft.world.phys.HitResult
import net.minecraft.world.phys.Vec3
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sin
/**
* Class to handle looping ride sounds for clients.
*
* @author Jackowes
* @since April 21st, 2025
*/
class RideLoopSound(val ride: PokemonEntity, val soundSettings: RideSoundSettings) :
AbstractTickableSoundInstance(SoundEvent.createVariableRangeEvent(soundSettings.soundLocation), SoundSource.NEUTRAL, SoundInstance.createUnseededRandom()) {
var shouldStop: Boolean = false
var fade: Float = 0.0f
var shouldMuffle: Boolean = soundSettings.muffleEnabled
var rideAttenuation: RideAttenuationModel = soundSettings.attenuationModel
var muffleAmount: Float = 1.0f // Currently slightly misleading. 1.0 is on muffle and 0.0 is full hFgain muting
// TODO: revisit below flag once multi passenger support is enabled
var isPassenger: Boolean = Minecraft.getInstance().player?.id == ride.controllingPassenger?.id
init {
this.looping = true
this.delay = 0
this.volume = 0.1f
this.attenuation = SoundInstance.Attenuation.NONE // This class performs its own attenuation
if (this.isPassenger) this.relative = true
}
override fun tick() {
// Check if the sound should fade in/out or stop
this.runFade()
// If the ride is out of attenuation distance then don't play
if (this.outOfRange()) {
this.volume = 0.0f
return
}
// Perform data driven pitch and volume calculation
this.volume = ((soundSettings.volumeExpr.let { ride.runtime.resolveDouble(it) })).toFloat()
this.volume *= this.fade
this.pitch = ((soundSettings.pitchExpr.let { ride.runtime.resolveDouble(it) })).toFloat()
if (!this.isPassenger && this.soundSettings.playForNonPassengers) {
this.setPos()
// This should now be done fully through openal. Add to RideAttneuationModel and the
// ChannelMixin to expand on the attenuation models.
// Attenuate due to distance
// this.volume *= attenuation()
// Calculate influence due to the doppler effect
this.pitch *= calcDopplerInfluence().toFloat()
// Calculate muffling due to occlusion
val soundOc = soundOcclusion()
if (soundOc != 0.0f) {
val newMuffle = lerp(soundOc,1.0f, 0.2f).coerceIn(0.2f,1.0f)
// lerp muffle smoothly
this.muffleAmount = lerp(0.1f, this.muffleAmount, newMuffle)
this.shouldMuffle = true
this.volume *= muffleAmount
} else {
this.shouldMuffle = true
this.muffleAmount = lerp(0.1f, this.muffleAmount, 1.0f)
this.volume *= muffleAmount
}
}
}
fun runFade() {
if (this.shouldStop && this.fade == 0.0f) {
this.stop()
} else if(this.shouldStop) {
this.fade = max(0.0f, this.fade - 0.05f)
} else if(!this.shouldStop && this.fade != 1.0f){
this.fade = min(1.0f, this.fade + 0.05f)
}
}
fun outOfRange(): Boolean {
val attenDist = this.sound.attenuationDistance
val listenerPosition = Minecraft.getInstance().player?.eyePosition ?: return false
return listenerPosition.distanceTo(ride.position()) > attenDist
}
fun calcDopplerInfluence(): Double {
val rideVel = ride.getRideVelocity()
// TODO: Expand this to multiple players
val listener = Minecraft.getInstance().player ?: return 1.0
val listenerVel = listener.deltaMovement//listener.position().subtract(Vec3(listener.xOld, listener.yOld, listener.zOld))
// Tweaked constant based off speed of sound in room temp air. Exaggerated for a better effect in minecraft
val waveSpeed = 343.0f / 50.0f
// listener->ride los velocity
val lineOfSightToRide = ride.position().subtract(listener.position()).normalize()
//val lineOfSightToPlayer = listener.position().subtract(ride.position()).normalize()
// Speed of the observer towards the sound source
val velObserver = listenerVel.dot(lineOfSightToRide)
// Speed of the sound source towards the observer
val velSrc = rideVel.dot(lineOfSightToRide.scale(-1.0))
// Use the doppler effect equation to determine the pitch change
val pitchFactor = (waveSpeed + velObserver) / (waveSpeed - velSrc)
return pitchFactor
}
fun soundOcclusion(): Float {
val listener = Minecraft.getInstance().player ?: return 0.0f
val level = listener.level()
val maxAttenDist = this.sound.attenuationDistance
var totalMuffle = 0.0
val center = ride.boundingBox.center
val hit = level.clip(
ClipContext(
listener.eyePosition,
center,
ClipContext.Block.COLLIDER,
ClipContext.Fluid.NONE,
listener
)
)
if(hit.type != HitResult.Type.MISS) {
//Calculate the distance the hit was away from the listener
val hitDist = hit.location.distanceTo(listener.eyePosition)
//Calculate the influence based on distance of this particular ray hit
val hitMuffle = lerp((1 - (hitDist / maxAttenDist)), 0.3, 1.0)
totalMuffle += hitMuffle
}
return totalMuffle.toFloat()
}
// fun attenuation(): Float {
// val listener = Minecraft.getInstance().player ?: return 1.0f
// val soundDistance = (ride.position().subtract(listener.eyePosition)).length()
// val maxAttenDistance = this.sound.attenuationDistance
//
// return (1 - (soundDistance / maxAttenDistance).pow(2)).toFloat()
// }
fun setPos() {
this.x = ride.x
this.y = ride.y
this.z = ride.z
}
fun stopSound() {
this.shouldStop = true
//Minecraft.getInstance().soundManager.stop(this)
}
}
@@ -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.api.riding.sound
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.Minecraft
import net.minecraft.resources.ResourceLocation
/**
* Class to control playing all ride sounds
*
* @author Jackowes
* @since April 26th, 2025
*/
class RideSoundManager(
private val ride: PokemonEntity
) {
private val activeSounds = mutableListOf<RideLoopSound>()
private var rideActive: Boolean = false
private var currKey: ResourceLocation = cobblemonResource("no_key")
fun tick() {
val currActive = ride.hasControllingPassenger()
if( !currActive && !this.rideActive) return
// Remove stopped sounds
activeSounds.removeIf { it.isStopped}
if (!currActive && this.rideActive) {
this.rideActive = false
this.currKey = cobblemonResource("no_key")
this.stop()
} else if (currActive && !this.rideActive) {
this.rideActive = true
this.currKey = getActiveRideKey()
this.getBehaviourSounds()
} else if (currActive && this.rideActive && this.currKey != getActiveRideKey()) {
this.currKey = getActiveRideKey()
this.stop()
this.getBehaviourSounds()
}
}
fun getBehaviourSounds() {
val rideSounds = ride.ifRidingAvailableSupply(null) { behaviour, settings, state ->
behaviour.getRideSounds(settings, state, ride)
}
rideSounds?.sounds?.forEach {
val loopingRideSound = RideLoopSound(ride,it)
activeSounds.add(loopingRideSound)
Minecraft.getInstance().soundManager.play(loopingRideSound)
}
}
fun getActiveRideKey(): ResourceLocation {
val key = ride.ridingBehaviourSettings?.key ?: cobblemonResource("no_key")
if (key == CompositeBehaviour.KEY) {
return (ride.ridingState as? CompositeState)?.activeBehaviour?.get() ?: cobblemonResource("no_key")
} else {
return key
}
}
fun stop() {
activeSounds.forEach { it.stopSound() }
}
}
@@ -1,79 +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.riding.sound;
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readExpression
import com.cobblemon.mod.common.util.writeExpression
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation;
/**
* Class to store data for looping sounds played during riding.
*
* @author Jackowes
* @since April 26th, 2025
*/
data class RideSoundSettings(
val soundLocation: ResourceLocation,
val volumeExpr: Expression = "1.0".asExpression(),
val pitchExpr: Expression = "1.0".asExpression(),
val playForNonPassengers: Boolean = false,
val muffleEnabled: Boolean = false,
val attenuationModel: RideAttenuationModel = RideAttenuationModel.NONE
): Encodable {
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(soundLocation)
buffer.writeExpression(volumeExpr)
buffer.writeExpression(pitchExpr)
buffer.writeBoolean(playForNonPassengers)
buffer.writeBoolean(muffleEnabled)
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): RideSoundSettings {
// Try to read if available, fallback to default otherwise
val soundLocation = buffer.readResourceLocation()
val volumeExpr = buffer.readExpression()
val pitchExpr = buffer.readExpression()
val playForNonPassengers = buffer.readBoolean()
val muffleEnabled = buffer.readBoolean()
val attenuationModel = RideAttenuationModel.EXPONENTIAL
return RideSoundSettings(
soundLocation,
volumeExpr,
pitchExpr,
playForNonPassengers,
muffleEnabled,
attenuationModel
)
}
}
}
class RideSoundSettingsList(var sounds: List<RideSoundSettings> = mutableListOf()) {
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeCollection(sounds) { _, sound -> sound.encode(buffer) }
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): RideSoundSettingsList {
val list = RideSoundSettingsList()
list.sounds = buffer.readList { RideSoundSettings.decode(buffer) }.toMutableList()
return list
}
}
}
@@ -25,7 +25,7 @@ import org.joml.Vector3f
*/
class RidingAnimationData(val ride: PokemonEntity)
class RidingAnimationData
{
var prevOrientation: Matrix3f = Matrix3f()
var prevRot: Vec3 = Vec3.ZERO
@@ -34,30 +34,29 @@ class RidingAnimationData(val ride: PokemonEntity)
var rotDeltaSpring: Vec3Spring = Vec3Spring()
var localVelocitySpring: Vec3Spring = Vec3Spring()
fun update() {
val activeRide = ride.hasControllingPassenger()
if (!activeRide) return
fun update(entity: PokemonEntity) {
var currRot = Vec3.ZERO
var angDelta = Vec3.ZERO
val stiffness = 90.0
val damping = 18.0
// val stiffness = 16.0
// val damping = 8.0
// Update velocity first
val currentVelocity = Vec3(
ride.x - ride.xOld,
ride.y - ride.yOld,
ride.z - ride.zOld
entity.x - entity.xOld,
entity.y - entity.yOld,
entity.z - entity.zOld
)
val r = ride.ifRidingAvailableSupply(false) { behaviour, settings, state ->
behaviour.shouldRoll(settings, state, ride);
val r = entity.ifRidingAvailableSupply(false) { behaviour, settings, state ->
behaviour.shouldRoll(settings, state, entity);
}
if( r ) {
//TODO: should this return?
val controller = (ride.firstPassenger as? OrientationControllable)?.orientationController ?: return
val controller = (entity.firstPassenger as? OrientationControllable)?.orientationController ?: return
val orientation = controller.orientation ?: Matrix3f()
val currPitch = controller.pitch
@@ -106,8 +105,8 @@ class RidingAnimationData(val ride: PokemonEntity)
// Update previous values
prevOrientation.set(currOrientation)
} else {
val currPitch = ride.xRot
val currYaw = ride.yRot
val currPitch = entity.xRot
val currYaw = entity.yRot
val currRoll = 0.0f
currRot = Vec3(currPitch.toDouble(), currYaw.toDouble(), currRoll.toDouble())
@@ -121,7 +120,7 @@ class RidingAnimationData(val ride: PokemonEntity)
/******************************************************
* Calculate ride local velocity
*****************************************************/
val yaw360 = -1.0 * (ride.yRot + 360.0f) % 360.0f
val yaw360 = -1.0 * (entity.yRot + 360.0f) % 360.0f
val orientationNonRollable = Matrix3f().rotateY(yaw360.toRadians())
//Convert to Vector3f
@@ -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
}
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.storage
import com.cobblemon.mod.common.api.reactive.Observable.Companion.stopAfter
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.DataKeys
@@ -44,6 +45,9 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
override fun initialize() {
pokemon.forEachIndexed { index, pokemon ->
pokemon.storeCoordinates.set(StoreCoordinates(this, BottomlessPosition(index)))
pokemon.changeObservable.pipe(
stopAfter { pokemon.storeCoordinates.get()?.store != this }
).subscribe { storeChangeObservable.emit(Unit) }
}
}
@@ -112,8 +116,4 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
storeChangeObservable.emit(Unit)
}
}
override fun onPokemonChanged(pokemon: Pokemon) {
this.storeChangeObservable.emit(Unit)
}
}
@@ -46,8 +46,6 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
abstract fun getObservingPlayers(): Iterable<ServerPlayer>
/** Sends the contents of this store to a player as if they've never seen it before. This initializes the store then sends each contained Pokémon. */
abstract fun sendTo(player: ServerPlayer)
/** Notifies the store that the provided Pokémon has changed in some way that would require persisting. */
abstract fun onPokemonChanged(pokemon: Pokemon)
/**
* Runs initialization logic for this store, knowing that it has just been constructed in a [PokemonStoreFactory].
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.storage.party
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.Observable.Companion.stopAfter
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.storage.InvalidSpeciesException
import com.cobblemon.mod.common.api.storage.PokemonStore
@@ -61,10 +62,22 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
throw IllegalArgumentException("Slot position is out of bounds")
} else {
slots[position.slot] = pokemon
if (pokemon != null) {
if (pokemon.storeCoordinates.get()?.store != this) {
// It's new to this store. Attach the listener
trackPokemon(pokemon)
}
}
anyChangeObservable.emit(Unit)
}
}
fun trackPokemon(pokemon: Pokemon) {
pokemon.changeObservable
.pipe(stopAfter { pokemon.storeCoordinates.get()?.store != this })
.subscribe { anyChangeObservable.emit(Unit) }
}
override fun getFirstAvailablePosition(): PartyPosition? {
for (i in slots.indices) {
if (slots[i] == null) {
@@ -135,13 +148,10 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
for (slot in slots.indices) {
val pokemon = get(slot) ?: continue
pokemon.storeCoordinates.set(StoreCoordinates(this, PartyPosition(slot)))
trackPokemon(pokemon)
}
}
override fun onPokemonChanged(pokemon: Pokemon) {
anyChangeObservable.emit(Unit)
}
fun toGappyList() = slots.toList()
/** Maps the slots of the party using the giving mapper function, but preserving the nulls in the party at the right spots. */
@@ -62,7 +62,14 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
open operator fun set(index: Int, pokemon: Pokemon?) {
if (index in 0 until POKEMON_PER_BOX) {
this.pokemon[index] = pokemon
pokemon?.storeCoordinates?.set(StoreCoordinates(pc, PCPosition(boxNumber, index)))
if (pokemon != null) {
val previousCoordinates = pokemon.storeCoordinates.get()
val position = previousCoordinates?.position
pokemon.storeCoordinates.set(StoreCoordinates(pc, PCPosition(boxNumber, index)))
if (previousCoordinates?.store !is PCStore || previousCoordinates.store.uuid != pc.uuid || (position as PCPosition).box != boxNumber) {
trackPokemon(pokemon)
}
}
if (emit) {
boxChangeEmitter.emit(Unit)
}
@@ -90,6 +97,7 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
if (pokemon != null) {
val position = PCPosition(box, slot)
pokemon.storeCoordinates.set(StoreCoordinates(pc, position))
trackPokemon(pokemon)
}
}
boxChangeEmitter.subscribe { pc.pcChangeObservable.emit(Unit) }
@@ -100,7 +108,17 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
pokemon.forEachIndexed { slot, pokemon ->
pokemon?.storeCoordinates?.set(StoreCoordinates(pc, PCPosition(boxNumber, slot)))
}
boxChangeEmitter.emit(Unit)
}
fun trackPokemon(pokemon: Pokemon) {
pokemon.changeObservable
.pipe(
stopAfter {
val coordinates = it.storeCoordinates.get() ?: return@stopAfter true
return@stopAfter coordinates.store !is PCStore || coordinates.store.uuid != pc.uuid || (coordinates.position as PCPosition).box != boxNumber
}
)
.subscribe { boxChangeEmitter.emit(Unit) }
}
fun sendTo(player: ServerPlayer) {
@@ -284,10 +284,6 @@ open class PCStore(
}
}
override fun onPokemonChanged(pokemon: Pokemon) {
pcChangeObservable.emit(Unit)
}
fun clearPC() {
boxes.forEach { box ->
box.getNonEmptySlots().forEach{
@@ -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
@@ -21,126 +21,126 @@ object ElementalTypes {
val NORMAL = register(
name = "Normal",
displayName = Component.translatable("cobblemon.type.normal"),
hue = 0xE8E8DA,
hue = 0xDDDDCF,
textureXMultiplier = 0
)
val FIRE = register(
name = "Fire",
displayName = Component.translatable("cobblemon.type.fire"),
hue = 0xFF6E21,
hue = 0xE55C32,
textureXMultiplier = 1
)
val WATER = register(
name = "Water",
displayName = Component.translatable("cobblemon.type.water"),
hue = 0x3FA5FF,
hue = 0x4A9BE8,
textureXMultiplier = 2
)
val GRASS = register(
name = "Grass",
displayName = Component.translatable("cobblemon.type.grass"),
hue = 0x62D14F,
hue = 0x4DBC3C,
textureXMultiplier = 3
)
val ELECTRIC = register(
name = "Electric",
displayName = Component.translatable("cobblemon.type.electric"),
hue = 0xFFD314,
hue = 0xEFD128,
textureXMultiplier = 4
)
val ICE = register(
name = "Ice",
displayName = Component.translatable("cobblemon.type.ice"),
hue = 0x54F2F2,
hue = 0x6BC3EF,
textureXMultiplier = 5
)
val FIGHTING = register(
name = "Fighting",
displayName = Component.translatable("cobblemon.type.fighting"),
hue = 0xEF565D,
hue = 0xC44C5C,
textureXMultiplier = 6
)
val POISON = register(
name = "Poison",
displayName = Component.translatable("cobblemon.type.poison"),
hue = 0xD651FF,
hue = 0xA24BD8,
textureXMultiplier = 7
)
val GROUND = register(
name = "Ground",
displayName = Component.translatable("cobblemon.type.ground"),
hue = 0xF4A453,
hue = 0xD89950,
textureXMultiplier = 8
)
val FLYING = register(
name = "Flying",
displayName = Component.translatable("cobblemon.type.flying"),
hue = 0xB8B2FF,
hue = 0xBCC1FF,
textureXMultiplier = 9
)
val PSYCHIC = register(
name = "Psychic",
displayName = Component.translatable("cobblemon.type.psychic"),
hue = 0xFF5E9E,
hue = 0xD86AD6,
textureXMultiplier = 10
)
val BUG = register(
name = "Bug",
displayName = Component.translatable("cobblemon.type.bug"),
hue = 0xD3D319,
hue = 0xA2C831,
textureXMultiplier = 11
)
val ROCK = register(
name = "Rock",
displayName = Component.translatable("cobblemon.type.rock"),
hue = 0xB7A16E,
hue = 0xAA9666,
textureXMultiplier = 12
)
val GHOST = register(
name = "Ghost",
displayName = Component.translatable("cobblemon.type.ghost"),
hue = 0x9C80F7,
hue = 0x9572E5,
textureXMultiplier = 13
)
val DRAGON = register(
name = "Dragon",
displayName = Component.translatable("cobblemon.type.dragon"),
hue = 0x7580FF,
hue = 0x535DE8,
textureXMultiplier = 14
)
val DARK = register(
name = "Dark",
displayName = Component.translatable("cobblemon.type.dark"),
hue = 0x587DA0,
hue = 0x5C6CB2,
textureXMultiplier = 15
)
val STEEL = register(
name = "Steel",
displayName = Component.translatable("cobblemon.type.steel"),
hue = 0xABD1F4,
hue = 0xC3CCE0,
textureXMultiplier = 16
)
val FAIRY = register(
name = "Fairy",
displayName = Component.translatable("cobblemon.type.fairy"),
hue = 0xFF7FE5,
hue = 0xEA727E,
textureXMultiplier = 17
)
@@ -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(

Some files were not shown because too many files have changed in this diff Show More