Compare commits

..
Author SHA1 Message Date
Landon Wills 09a0cd6c30 Start restructure of camera transformations 2025-04-27 07:46:20 -04:00
832 changed files with 3987 additions and 10528 deletions
-15
View File
@@ -1,15 +0,0 @@
### Additions
- Rebuilt Pokémon AI to use Minecraft's Brain system, allowing for more complex behaviours and interactions.
- As an early pass of this, pastured Pokémon will now sleep when appropriate, and may come up to you when they see you.
- Probably also added a whole bunch of bugs.
- Growlithe and Arcanine intimidate Skeletons.
- Meowth intimidate Creepers and Phantoms.
- Added a Behaviour Editor to the NPC editing screen.
- Added /behaviouredit command for opening the Behaviour Editor on Pokémon and NPCs.
- Editing variables for Pokémon is not yet supported.
### MoLang & Datapacks
- Added a `behaviours` datapack folder which motivates the Behaviour Editor screen on NPCs and Pokémon.
- Added many default behaviours.
- 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.
-4
View File
@@ -134,8 +134,6 @@
- Improved parity with vanilla mobs' drop behavior; loot and XP drop on death instead of after the entire death sequence finishes.
- Quirk animations no longer play in the battle GUI since they were pretty distracting.
- A number of Pokemon that float above the ground visually (Gastly, Klingklang, etc.) are no longer considered to be touching the ground
- Renamed `pokemonId` and `type` to `species` in relevant Advancement triggers for conformity, this is a breaking change.
- Added a separate `species` argument to the `pick_starter`, `pasture_use` and `resurrect_pokemon` Advancement triggers.
### Fixes
- Fixed Particles sometimes facing the wrong direction (looking at you, Swords Dance)
@@ -167,8 +165,6 @@
- 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
- Fixed Moon Stones not interacting properly with dripstone blocks.
- Fixed some effects like particles from fishing rods appearing for players in the same coordinates in another world.
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
-9
View File
@@ -1,9 +0,0 @@
- Nodemaker bug, it thinks jumps are possible from water up an entire block - they aint
https://gyazo.com/759b8285e7c07046ff2e54adf0d7a7ac
- Why is magikarp trying to path through ice
https://gyazo.com/ae230758c529f885eeb0dd91e60530d2
- We should use mixins to put the path node filtering from OmniPathNodeMaker in the base classes otherwise
NPC wandering will be jank too.
+1 -1
View File
@@ -5,7 +5,7 @@ plugins {
version = "${project.property("mod_version")}+${project.property("mc_version")}"
val isSnapshot = project.property("snapshot")?.equals("true") == true
val isSnapshot = project.property("snapshot")?.equals("true") ?: false
if (isSnapshot) {
val fixedBranchName = versioning.info.branch.substringAfter("/")
version = "$version-${fixedBranchName}-${versioning.info.build}"
@@ -1,61 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.pokemon.ai.EntityBehaviour;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.goal.AvoidEntityGoal;
import net.minecraft.world.entity.monster.Creeper;
import net.minecraft.world.level.Level;
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;
@Mixin(Creeper.class)
public class EntityCreeperMixin extends Mob {
private EntityCreeperMixin(net.minecraft.world.entity.EntityType<? extends Creeper> entityType, Level world) {
super(entityType, world);
}
@Inject(method = "registerGoals", at = @At(value = "TAIL"), cancellable = false)
private void cobblemon$initGoals(CallbackInfo callbackInfo) {
final Creeper creeper = (Creeper) (Object) this;
// Pokemon Entities
this.goalSelector.addGoal(
3,
new AvoidEntityGoal<>(
creeper,
PokemonEntity.class,
6.0f,
1.0,
1.2,
entity -> ((PokemonEntity)entity).getBehaviour().getEntityInteract().getAvoidedByCreeper() && ((PokemonEntity)entity).getBeamMode() != 1
)
);
// Players with shoulder mounted Pokemon
this.goalSelector.addGoal(
3,
new AvoidEntityGoal<>(
creeper,
ServerPlayer.class,
6.0f,
1.0,
1.2,
entity -> EntityBehaviour.Companion.hasCreeperFearedShoulderMount((ServerPlayer)entity)
)
);
}
}
@@ -1,39 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import net.minecraft.world.entity.monster.Phantom;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(targets = "net/minecraft/world/entity/monster/Phantom$PhantomSweepAttackGoal")
public class EntityPhantomMixin {
// TODO: Figure out how to access the outer class that wraps SwoopMovementGoal
// Need to find the cat mons that are near the phantom
// @Inject(method = "shouldContinue", at = @At(value = "RETURN"), cancellable = true)
// public void cobblemon$shouldContinue(CallbackInfoReturnable<Boolean> ci) {
//// var goalSelectorField = PhantomEntity.class.getClasses();// PhantomEntity.class.getFields();// .class.getDeclaredField("goalSelector");
//
//// goalSelectorField.setAccessible(true);
//// GoalSelector goalSelector = (GoalSelector) goalSelectorField.get(mobEntity);
//// final SwoopMovementGoal phantom = (SwoopMovementGoal) (Object) this;
//// if(ci.getReturnValue() == false) {
//// var result = this$0.getWorld().getEntitiesByClass(
//// PokemonEntity.class,
//// this$0.getBoundingBox().expand(16.0),
//// entity -> {
//// return ((PokemonEntity) entity).getBehaviour().getEntityInteract().getAvoidedByPhantom();
//// }
//// ).isEmpty();
////
//// ci.setReturnValue(result);
//// }
// }
}
@@ -1,62 +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 com.cobblemon.mod.common.pokemon.ai.EntityBehaviour;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.goal.AvoidEntityGoal;
import net.minecraft.world.entity.monster.AbstractSkeleton;
import net.minecraft.world.level.Level;
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;
@Mixin(AbstractSkeleton.class)
public class EntitySkeletonMixin extends Mob {
private EntitySkeletonMixin(EntityType<? extends Mob> entityType, Level world) {
super(entityType, world);
}
@Inject(method = "registerGoals", at = @At(value = "TAIL"))
private void cobblemon$initGoals(CallbackInfo callbackInfo) {
final AbstractSkeleton skeleton = (AbstractSkeleton) (Object) this;
// Pokemon Entities
this.goalSelector.addGoal(
3,
new AvoidEntityGoal<>(
skeleton,
PokemonEntity.class,
6.0f,
1.0,
1.2,
entity -> ((PokemonEntity)entity).getBehaviour().getEntityInteract().getAvoidedBySkeleton() && ((PokemonEntity)entity).getBeamMode() != 1
)
);
// Players with shoulder mounted Pokemon
this.goalSelector.addGoal(
3,
new AvoidEntityGoal<>(
skeleton,
ServerPlayer.class,
6.0f,
1.0,
1.2,
entity -> EntityBehaviour.Companion.hasSkeletonFearedShoulderMount((ServerPlayer)entity)
)
);
}
}
@@ -25,7 +25,7 @@ public abstract class EntityVillagerMixin {
@Inject(method = "wantsToPickUp", at = @At(value = "RETURN"), cancellable = true)
private void cobblemon$canGather(ItemStack stack, CallbackInfoReturnable<Boolean> ci) {
final Villager villager = (Villager) (Object) this;
if (ci.getReturnValue() == false && Objects.equals(villager.getVillagerData().getProfession(), VillagerProfession.FARMER) && villager.getInventory().canAddItem(stack)) {
if(ci.getReturnValue() == false && Objects.equals(villager.getVillagerData().getProfession(), VillagerProfession.FARMER) && villager.getInventory().canAddItem(stack)) {
if(VillagerGatherableItems.INSTANCE.getVillagerGatherableItems().contains(stack.getItem()))
ci.setReturnValue(true);
}
@@ -22,9 +22,9 @@ public class PiglinBarterMixin {
@Inject(method = "isBarterCurrency", at = @At(value = "RETURN"), cancellable = true)
private static void cobblemon$acceptsForBarter(ItemStack stack, CallbackInfoReturnable<Boolean> ci) {
if (!ci.getReturnValue()) {
if(!ci.getReturnValue()) {
//TODO: Make a tag?
if (stack.is(CobblemonItems.RELIC_COIN_POUCH))
if(stack.is(CobblemonItems.RELIC_COIN_POUCH))
ci.setReturnValue(true);
}
}
@@ -21,9 +21,9 @@ import org.spongepowered.asm.mixin.injection.Slice;
public abstract class PointedDripstoneBlockMixin {
@Redirect(
method = "canGrow(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;)Z",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;is(Lnet/minecraft/world/level/block/Block;)Z", ordinal = 0),
slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/world/level/block/Blocks;DRIPSTONE_BLOCK:Lnet/minecraft/world/level/block/Block;"))
method = "canTipGrow(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/core/BlockPos;)Z",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;is(Lnet/minecraft/world/level/Block;)Z", ordinal = 0),
slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/world/level/block/Blocks;DRIPSTONE_BLOCK:Lnet/minecraft/world/level/Block;"))
)
private static boolean cobblemon$validateMoonStoneDripstone(BlockState instance, Block block) {
return instance.is(CobblemonBlockTags.DRIPSTONE_GROWABLE);
@@ -8,10 +8,12 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.sound.BlockEntitySoundTracker;
import com.cobblemon.mod.common.client.sound.EntitySoundTracker;
import com.cobblemon.mod.common.client.sound.BattleMusicController;
import com.cobblemon.mod.common.client.sound.SoundTracker;
import com.cobblemon.mod.common.duck.SoundEngineDuck;
import com.cobblemon.mod.common.duck.SoundManagerDuck;
import com.cobblemon.mod.common.duck.SoundEngineDuck;
import net.minecraft.client.resources.sounds.BiomeAmbientSoundsHandler;
import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.client.resources.sounds.UnderwaterAmbientSoundInstances;
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.riding.Rideable;
import com.cobblemon.mod.common.client.MountedPokemonAnimationRenderController;
import com.cobblemon.mod.common.client.render.camera.MountedCameraRenderer;
@@ -21,6 +20,7 @@ import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix3f;
import org.joml.Quaternionf;
@@ -35,30 +35,49 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Camera.class)
public abstract class CameraMixin {
@Shadow private Entity entity;
@Shadow @Final private Quaternionf rotation;
@Shadow @Final private Vector3f forwards;
@Shadow @Final private Vector3f up;
@Shadow @Final private Vector3f left;
@Shadow private float xRot;
@Shadow private float yRot;
@Shadow
private Entity entity;
@Shadow
@Final
private Quaternionf rotation;
@Shadow
@Final
private Vector3f forwards;
@Shadow
@Final
private Vector3f up;
@Shadow
@Final
private Vector3f left;
@Shadow
private float xRot;
@Shadow
private float yRot;
@Shadow private float eyeHeight;
@Shadow private float eyeHeightOld;
@Shadow
private float eyeHeight;
@Shadow
private float eyeHeightOld;
@Shadow protected abstract void setPosition(Vec3 pos);
@Shadow
protected abstract void setPosition(Vec3 pos);
@Unique private float returnTimer = 0;
@Unique private float rollAngleStart = 0;
@Unique Minecraft minecraft = Minecraft.getInstance();
@Unique
private float returnTimer = 0;
@Unique
private float rollAngleStart = 0;
@Unique
Minecraft minecraft = Minecraft.getInstance();
@Unique
boolean disableRollableCameraDebug = false;
@Inject(method = "setRotation", at = @At("HEAD"), cancellable = true)
public void cobblemon$setRotation(float f, float g, CallbackInfo ci) {
if (!(this.entity instanceof OrientationControllable controllable) || Cobblemon.config.getDisableRoll()) return;
if (!(this.entity instanceof OrientationControllable controllable) || disableRollableCameraDebug) return;
var controller = controllable.getOrientationController();
if (!controller.isActive() && controller.getOrientation() != null) {
if(this.returnTimer < 1) {
if (this.returnTimer < 1) {
//Rotation is taken from entity since we no longer handle mouse ourselves
//Stops a period of time when you can't input anything.
controller.getOrientation().set(
@@ -66,16 +85,17 @@ public abstract class CameraMixin {
.rotateY((float) Math.toRadians(180 - this.entity.getYRot()))
.rotateX((float) Math.toRadians(-this.entity.getXRot()))
);
if(rollAngleStart == 0){
if (rollAngleStart == 0) {
this.returnTimer = 1;
controller.reset();
return;
}
controller.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart*(1-returnTimer)));
controller.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart * (1 - returnTimer)));
cobblemon$applyRotation();
this.returnTimer += .05F;
ci.cancel();
} else {
}
else {
this.returnTimer = 1;
controller.reset();
}
@@ -89,26 +109,16 @@ public abstract class CameraMixin {
ci.cancel();
}
@Inject(method = "setup", at = @At("HEAD"))
private void cobblemon$tickAnimationPreRenders(BlockGetter level, Entity entity, boolean detached, boolean thirdPersonReverse, float partialTick, CallbackInfo ci) {
MountedPokemonAnimationRenderController.INSTANCE.reset();
}
@WrapOperation(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setPosition(DDD)V"))
public void cobblemon$positionCamera(Camera instance, double x, double y, double z, Operation<Void> original, @Local(ordinal = 1, argsOnly = true) boolean thirdPersonReverse) {
Entity entity = instance.getEntity();
Entity vehicle = entity.getVehicle();
MountedPokemonAnimationRenderController.INSTANCE.reset();
if(vehicle instanceof Rideable){
Vec3 position = MountedCameraRenderer.INSTANCE.getCameraPosition(
instance,
entity,
vehicle,
x,
y,
z,
thirdPersonReverse,
eyeHeightOld,
eyeHeight
);
if(position != null){
if (cobblemon$isRidingPokemon(entity)) {
Vec3 position = MountedCameraRenderer.INSTANCE.getCameraPosition(instance, entity, entity.getVehicle(), thirdPersonReverse, eyeHeightOld, eyeHeight);
if (position != null) {
setPosition(position);
return;
}
@@ -118,19 +128,25 @@ public abstract class CameraMixin {
}
@Unique
private void cobblemon$applyRotation(){
if (!(this.entity instanceof OrientationControllable controllable) || controllable.getOrientationController().getOrientation() == null) return;
private boolean cobblemon$isRidingPokemon(Entity entity) {
return entity.getVehicle() instanceof PokemonEntity;
}
@Unique
private void cobblemon$applyRotation() {
if (!(this.entity instanceof OrientationControllable controllable) || controllable.getOrientationController().getOrientation() == null)
return;
var controller = controllable.getOrientationController();
var newRotation = controller.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
if (this.minecraft.options.getCameraType().isMirrored()) {
newRotation.rotateY((float)Math.toRadians(180));
newRotation.rotateY((float) Math.toRadians(180));
}
this.rotation.set(newRotation);
this.xRot = controller.getPitch();
this.yRot = controller.getYaw();
OrientationController.Companion.getFORWARDS().rotate(this.rotation, this.forwards);
OrientationController.Companion.getUP().rotate(this.rotation, this.up);
OrientationController.Companion.getLEFT().rotate(this.rotation, this.left);
this.forwards.set(controller.getForwardVector());
this.up.set(controller.getUpVector());
this.left.set(controller.getLeftVector());
}
@WrapOperation(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;move(FFF)V", ordinal = 0))
@@ -138,7 +154,7 @@ public abstract class CameraMixin {
Entity entity = instance.getEntity();
Entity vehicle = entity.getVehicle();
if (!(vehicle instanceof PokemonEntity) || Cobblemon.config.getThirdPersonViewBobbing()) {
if (!(vehicle instanceof PokemonEntity) || Cobblemon.config.getViewBobTranslation()) {
original.call(instance, zoom, dy, dx);
}
}
@@ -1,46 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.render.TextClipping;
import net.minecraft.client.gui.Font;
import net.minecraft.network.chat.Style;
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;
/**
* This mixin facilitates the gradual reveal of text with NPC dialogue. If there is a better way
* to do this, I'd love to hear it.
*
* The idea is that somewhere static in the mod we have a control for how many characters can be drawn
* and how many have been drawn so far, and this mixin will add to the latter and stop drawing characters
* once it reaches the former.
*
* This relies STRONGLY on the code that sets the character cap to reset it to -1 after the text is rendered,
* otherwise all text in Minecraft will stop drawing.
*
* Sites that take advantage of this mixin will do the following:
* 1: Set the character cap, which resets the drawn counter to zero.
* 2: Call whatever Minecraft function draws your text.
* 3: Reset the character cap to -1, which will allow regular text to draw again.
*
* @author Hiroku
* @since April 18th, 2025
*/
@Mixin(Font.StringRenderOutput.class)
public class StringRenderOutputMixin {
@Inject(method = "accept", at = @At(value = "HEAD"), cancellable = true)
public void onAccept(int i, Style style, int j, CallbackInfoReturnable<Boolean> cir) {
if (!TextClipping.canDrawAnotherCharacter()) {
cir.setReturnValue(true);
}
}
}
@@ -15,9 +15,6 @@ object CobblemonActivities {
val BATTLING = register(Activity("battling"))
val ACTION_EFFECT = register(Activity("action_effect"))
val NPC_CHATTING = register(Activity("npc_chatting"))
val POKEMON_SLEEPING_ACTIVITY = register(Activity("pokemon_sleeping"))
val POKEMON_GROW_CROP = register(Activity("pokemon_grow_crop"))
val POKEMON_SLEEP_ON_TRAINER_BED = register(Activity("pokemon_sleep_on_trainer_bed"))
fun register(activity: Activity): Activity {
activities.add(activity)
@@ -1,106 +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
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.ai.CobblemonBehaviour
import com.cobblemon.mod.common.api.ai.config.BehaviourConfig
import com.cobblemon.mod.common.api.ai.config.task.TaskConfig
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.data.JsonDataRegistry.Companion.JSON_EXTENSION
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.BehaviourSyncPacket
import com.cobblemon.mod.common.util.adapters.ActivityAdapter
import com.cobblemon.mod.common.util.adapters.BehaviourConfigAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionOrEntityVariableAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.TaskConfigAdapter
import com.cobblemon.mod.common.util.adapters.TranslatedTextAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.endsWith
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import com.mojang.datafixers.util.Either
import java.io.File
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
import net.minecraft.world.entity.schedule.Activity
object CobblemonBehaviours : JsonDataRegistry<CobblemonBehaviour> {
override val gson: Gson = GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(Activity::class.java, ActivityAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(BehaviourConfig::class.java, BehaviourConfigAdapter)
.registerTypeAdapter(TaskConfig::class.java, TaskConfigAdapter)
.registerTypeAdapter(
TypeToken.getParameterized(Either::class.java, Expression::class.java, MoLangConfigVariable::class.java).type,
ExpressionOrEntityVariableAdapter
)
.registerTypeAdapter(Component::class.java, TranslatedTextAdapter)
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.create()
override val typeToken = TypeToken.get(CobblemonBehaviour::class.java)
override val resourcePath = "behaviours"
override val id: ResourceLocation = cobblemonResource("behaviours")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<CobblemonBehaviours>()
/**
* Any behaviours that are put into /pokemon/auto will be automatically
* applied to all Pokémon species/forms that don't define a baseAI list of
* behaviour configurations. This is an addon-friendly way of defining the general
* rules of Pokémon AI without it being unavoidable for specific
* species.
*/
val autoPokemonBehaviours = mutableListOf<CobblemonBehaviour>()
val behaviours = mutableMapOf<ResourceLocation, CobblemonBehaviour>()
override fun sync(player: ServerPlayer) {
player.sendPacket(BehaviourSyncPacket(behaviours.filter { it.value.visible }))
}
override fun reload(manager: ResourceManager) {
autoPokemonBehaviours.clear()
val data = mutableMapOf<ResourceLocation, CobblemonBehaviour>()
manager.listResources(resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
resource.open().use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
val behaviour = parse(reader, resolvedIdentifier)
if ("pokemon/auto/" in identifier.path) {
autoPokemonBehaviours.add(behaviour)
}
data[resolvedIdentifier] = behaviour
}
}
}
reload(data)
Cobblemon.LOGGER.info("Loaded ${behaviours.size} entity behaviours")
observable.emit(this)
}
override fun reload(data: Map<ResourceLocation, CobblemonBehaviour>) {
behaviours.clear()
behaviours.putAll(data)
}
}
@@ -0,0 +1,58 @@
/*
* 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
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.ai.config.BrainConfig
import com.cobblemon.mod.common.api.ai.config.task.TaskConfig
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.util.adapters.ActivityAdapter
import com.cobblemon.mod.common.util.adapters.BrainConfigAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.adapters.TaskConfigAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.world.entity.schedule.Activity
object CobblemonBrainConfigs : JsonDataRegistry<List<BrainConfig>> {
override val gson: Gson = GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(Activity::class.java, ActivityAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(BrainConfig::class.java, BrainConfigAdapter)
.registerTypeAdapter(TaskConfig::class.java, TaskConfigAdapter)
.create()
override val typeToken = TypeToken.getParameterized(List::class.java, BrainConfig::class.java) as TypeToken<List<BrainConfig>>
override val resourcePath = "brain_presets"
override val id: ResourceLocation = cobblemonResource("brain_presets")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<CobblemonBrainConfigs>()
val presets = mutableMapOf<ResourceLocation, List<BrainConfig>>()
override fun sync(player: ServerPlayer) {
// TODO implement probs ay
}
override fun reload(data: Map<ResourceLocation, List<BrainConfig>>) {
presets.clear()
presets.putAll(data)
}
}
@@ -63,7 +63,6 @@ object CobblemonCommands {
SpectateBattleCommand.register(dispatcher)
CobblemonConfigCommand.register(dispatcher)
UnlockPCBoxWallpaperCommand.register(dispatcher)
BehaviourEditCommand.register(dispatcher)
ChangePCBoxesCommand.register(dispatcher)
MarkGiveCommand.register(dispatcher)
MarkTakeCommand.register(dispatcher)
@@ -16,32 +16,14 @@ import com.mojang.serialization.codecs.PrimitiveCodec
import net.minecraft.core.UUIDUtil
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import java.util.Optional
import java.util.UUID
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
object CobblemonMemories {
val memories = mutableMapOf<String, MemoryModuleType<*>>()
/*
* When a memory doesn't have a codec provided, it's because it doesn't
* get serialized during relogs. This makes sense for things like battles which
* do not survive a relog therefore any memory of it should be temporary.
*/
val BATTLING_POKEMON = register("battling_pokemon", ListCodec(UUIDUtil.CODEC, 0, 31))
val NPC_BATTLING = register("npc_battling", PrimitiveCodec.BOOL)
val DIALOGUES = register<List<ActiveDialogue>>("npc_dialogues")
val ACTIVE_ACTION_EFFECT = register<ActionEffectContext>("active_action_effect")
val POKEMON_FLYING = register("pokemon_flying", PrimitiveCodec.BOOL)
val POKEMON_DROWSY = register("pokemon_drowsy", PrimitiveCodec.BOOL)
val POKEMON_SLEEPING = register("pokemon_sleeping", PrimitiveCodec.BOOL)
val POKEMON_BATTLE = register<UUID>("pokemon_battle")
val PATH_COOLDOWN = register<Boolean>("path_cooldown")
val TARGETED_BATTLE_POKEMON = register<UUID>("targeted_battle_pokemon")
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")
fun <U> register(id: String, codec: Codec<U>): MemoryModuleType<U> {
val memoryModule = MemoryModuleType(Optional.of(codec))
@@ -54,4 +36,4 @@ object CobblemonMemories {
memories[id] = memoryModule
return memoryModule
}
}
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.client.net.OpenBehaviourEditorHandler
import com.cobblemon.mod.common.client.net.CalculateSeatPositionsHandler
import com.cobblemon.mod.common.client.net.PlayerInteractOptionsHandler
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
@@ -61,7 +60,6 @@ import com.cobblemon.mod.common.client.net.trade.TradeProcessStartedHandler
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
import com.cobblemon.mod.common.net.PacketRegisterInfo
import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
import com.cobblemon.mod.common.net.messages.client.CalculateSeatPositionsPacket
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
@@ -140,7 +138,6 @@ import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamRequestPack
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamResponsePacket
import com.cobblemon.mod.common.net.messages.server.battle.RemoveSpectatorPacket
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
import com.cobblemon.mod.common.net.messages.server.behaviour.SetEntityBehaviourPacket
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
@@ -187,7 +184,6 @@ import com.cobblemon.mod.common.net.serverhandling.battle.SpectateBattleHandler
import com.cobblemon.mod.common.net.serverhandling.battle.TeamLeaveHandler
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestHandler
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestResponseHandler
import com.cobblemon.mod.common.net.serverhandling.behaviour.SetEntityBehaviourHandler
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectedHandler
@@ -386,7 +382,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(SeasoningRegistrySyncPacket.ID, SeasoningRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(CosmeticItemAssignmentSyncPacket.ID, CosmeticItemAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
// Effects
@@ -441,9 +436,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(CloseNPCEditorPacket.ID, CloseNPCEditorPacket::decode, CloseNPCEditorHandler))
list.add(PacketRegisterInfo(OpenNPCEditorPacket.ID, OpenNPCEditorPacket::decode, OpenNPCEditorHandler))
// Behaviours
list.add(PacketRegisterInfo(OpenBehaviourEditorPacket.ID, OpenBehaviourEditorPacket::decode, OpenBehaviourEditorHandler))
// Pokédex scanning
list.add(PacketRegisterInfo(ServerConfirmedRegisterPacket.ID, ServerConfirmedRegisterPacket::decode, ServerConfirmedRegisterHandler))
@@ -559,9 +551,6 @@ object CobblemonNetwork {
// Cooking
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
// Behaviour Packets
list.add(PacketRegisterInfo(SetEntityBehaviourPacket.ID, SetEntityBehaviourPacket::decode, SetEntityBehaviourHandler))
return list
}
@@ -8,11 +8,6 @@
package com.cobblemon.mod.common
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.pokemon.ai.PokemonDisturbancesSensor
import com.cobblemon.mod.common.entity.sensor.BattlingPokemonSensor
import com.cobblemon.mod.common.entity.sensor.NPCBattlingSensor
import java.util.function.Supplier
@@ -25,20 +20,10 @@ object CobblemonSensors {
val NPC_BATTLING = register("npc_battling", ::NPCBattlingSensor)
val BATTLING_POKEMON = register("battling_pokemon", ::BattlingPokemonSensor)
// val NPC_BATTLING = register("npc_battling", ::NPCBattlingSensor)
val POKEMON_DROWSY = register("pokemon_drowsy", ::DrowsySensor)
val POKEMON_ADULT = register("pokemon_adult_sensor", ::PokemonAdultSensor)
val POKEMON_DISTURBANCE = register("pokemon_disturbance_sensor", ::PokemonDisturbancesSensor)
val POKEMON_DEFEND_OWNER = register("pokemon_owner_under_attack", ::DefendOwnerSensor)
val NEARBY_GROWABLE_CROPS = register("nearby_growable_crops", ::PokemonGrowableCropSensor)
fun <E : Entity, U : Sensor<E>> register(id: String, supplier: Supplier<U>): SensorType<U> {
val sensor = SensorType(supplier)
sensors[id] = sensor
return sensor
}
}
}
@@ -43,18 +43,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val PC_WALLPAPER_UNLOCK = this.create("pc.wallpaper.unlock")
@JvmField
val NPC_GIBBER_GENERIC = this.create("entity.npc.gibber.generic")
@JvmField
val NPC_GIBBER_SPEECH_TYPE_1_1 = this.create("entity.npc.gibber.speech_type_1_1")
@JvmField
val NPC_GIBBER_SPEECH_TYPE_1_2 = this.create("entity.npc.gibber.speech_type_1_2")
@JvmField
val NPC_GIBBER_SPEECH_TYPE_1_3 = this.create("entity.npc.gibber.speech_type_1_3")
@JvmField
val VILLAGER_WORK_NURSE = this.create("entity.villager.work_nurse")
@JvmField
val HEALING_MACHINE_ACTIVE = this.create("block.healing_machine.active")
@@ -470,4 +460,4 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
)
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.createVariableRangeEvent(cobblemonResource(name)))
}
}
@@ -18,7 +18,7 @@ import java.util.Optional
class AspectCriterion(
playerCtx: Optional<ContextAwarePredicate>,
val species: ResourceLocation,
val pokemon: ResourceLocation,
val aspects: List<String>
): SimpleCriterionCondition<MutableMap<ResourceLocation, MutableSet<String>>>(playerCtx) {
@@ -26,13 +26,42 @@ class AspectCriterion(
val CODEC: Codec<AspectCriterion> = RecordCodecBuilder.create { it.group(
//All three of these codecs used to use Codecs.createStrictOptionalFieldCodec, that no longer exists
ContextAwarePredicate.CODEC.optionalFieldOf("player").forGetter(AspectCriterion::playerCtx),
ResourceLocation.CODEC.optionalFieldOf("species", cobblemonResource("pikachu")).forGetter(AspectCriterion::species),
ResourceLocation.CODEC.optionalFieldOf("pokemon", cobblemonResource("pikachu")).forGetter(AspectCriterion::pokemon),
Codec.STRING.listOf().optionalFieldOf("aspects", listOf()).forGetter(AspectCriterion::aspects)
) .apply(it, ::AspectCriterion) }
}
override fun matches(player: ServerPlayer, context: MutableMap<ResourceLocation, MutableSet<String>>): Boolean {
val caughtAspects = context.getOrDefault(species, mutableSetOf())
val caughtAspects = context.getOrDefault(pokemon, mutableSetOf())
return this.aspects.all { it in caughtAspects }
}
}
//open class AspectCriterionTrigger(identifier: Identifier, criterionClass: Class<AspectCriterionCondition>) : SimpleCriterionTrigger<MutableMap<Identifier, MutableSet<String>>, AspectCriterionCondition>(identifier, criterionClass) {
//
//}
//
//class AspectCriterionCondition(id: Identifier, predicate: LootContextPredicate) : SimpleCriterionCondition<MutableMap<Identifier, MutableSet<String>>>(id, predicate) {
// var pokemon = Identifier.of("cobblemon:pikachu")
// var aspects = mutableListOf<String>()
// override fun toJson(json: JsonObject) {
// json.add("aspects", JsonArray(aspects.size).also {
// aspects.forEach { aspect -> it.add(aspect) }
// })
// json.addProperty("pokemon", pokemon.toString())
// }
//
// override fun fromJson(json: JsonObject) {
// aspects.clear()
// json.getAsJsonArray("aspects").forEach { element ->
// aspects.add(element.asString)
// }
// pokemon = json.get("pokemon").asString.asIdentifierDefaultingNamespace()
// }
//
// override fun matches(player: ServerPlayer, context: MutableMap<Identifier, MutableSet<String>>): Boolean {
// val caughtAspects = context.getOrDefault(pokemon, mutableSetOf())
// return this.aspects.all { it in caughtAspects }
// }
//
//}
@@ -21,23 +21,40 @@ import java.util.Optional
* @author Hiroku
* @since November 4th, 2022
*/
open class CountablePokemonTypeContext(times: Int, var species: String) : CountableContext(times)
open class CountablePokemonTypeContext(times: Int, var type: String) : CountableContext(times)
class CaughtPokemonCriterion(
playerCtx: Optional<ContextAwarePredicate>,
val species: String,
val type: String,
count: Int
): CountableCriterion<CountablePokemonTypeContext>(playerCtx, count) {
companion object {
val CODEC: Codec<CaughtPokemonCriterion> = RecordCodecBuilder.create { it.group(
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(CaughtPokemonCriterion::playerCtx),
Codec.STRING.optionalFieldOf("species", "any").forGetter(CaughtPokemonCriterion::species),
Codec.STRING.optionalFieldOf("type", "any").forGetter(CaughtPokemonCriterion::type),
Codec.INT.optionalFieldOf("count", 0).forGetter(CaughtPokemonCriterion::count)
).apply(it, ::CaughtPokemonCriterion) }
}
override fun matches(player: ServerPlayer, context: CountablePokemonTypeContext): Boolean {
return super.matches(player, context) && (context.species == species || species == "any")
return super.matches(player, context) && (context.type == type || type == "any")
}
}
//class CaughtPokemonCriterionCondition(id: Identifier, predicate: LootContextPredicate) : CountableCriterionCondition<CountablePokemonTypeContext>(id, predicate) {
// var type = "any"
// override fun toJson(json: JsonObject) {
// super.toJson(json)
// json.addProperty("type", type)
// }
//
// override fun fromJson(json: JsonObject) {
// super.fromJson(json)
// type = json.get("type")?.asString ?: "any"
// }
//
// override fun matches(player: ServerPlayer, context: CountablePokemonTypeContext): Boolean {
// return super.matches(player, context) && (context.type == type || type == "any")
// }
//}
@@ -19,19 +19,17 @@ import java.util.Optional
class PokemonCriterion(
playerCtx: Optional<ContextAwarePredicate>,
val species: String,
val properties: PokemonProperties
): SimpleCriterionCondition<Pokemon>(playerCtx) {
companion object {
val CODEC: Codec<PokemonCriterion> = RecordCodecBuilder.create { it.group(
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(PokemonCriterion::playerCtx),
Codec.STRING.optionalFieldOf("species", "any").forGetter(PokemonCriterion::species),
PokemonProperties.CODEC.optionalFieldOf("properties", PokemonProperties()).forGetter(PokemonCriterion::properties)
).apply(it, ::PokemonCriterion) }
}
override fun matches(player: ServerPlayer, context: Pokemon): Boolean {
return (species.equals(context.species.resourceIdentifier.path) || species.equals(context.species.resourceIdentifier)) && properties.matches(context)
return properties.matches(context)
}
}
}
@@ -17,22 +17,22 @@ import net.minecraft.server.level.ServerPlayer
import net.minecraft.resources.ResourceLocation
import java.util.Optional
class PokemonInteractContext(val species: ResourceLocation, val item: ResourceLocation)
class PokemonInteractContext(val type: ResourceLocation, val item: ResourceLocation)
class PokemonInteractCriterion(
playerCtx: Optional<ContextAwarePredicate>,
val species: String,
val type: String,
val item: String
): SimpleCriterionCondition<PokemonInteractContext>(playerCtx) {
companion object {
val CODEC: Codec<PokemonInteractCriterion> = RecordCodecBuilder.create { it.group(
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(PokemonInteractCriterion::playerCtx),
Codec.STRING.optionalFieldOf("species", "any").forGetter(PokemonInteractCriterion::species),
Codec.STRING.optionalFieldOf("type", "any").forGetter(PokemonInteractCriterion::type),
Codec.STRING.optionalFieldOf("item", "any").forGetter(PokemonInteractCriterion::item)
).apply(it, ::PokemonInteractCriterion) }
}
override fun matches(player: ServerPlayer, context: PokemonInteractContext): Boolean {
return (context.species == this.species.asIdentifierDefaultingNamespace() || this.species == "any") && (context.item == this.item.asIdentifierDefaultingNamespace() || this.item == "any")
return (context.type == this.type.asIdentifierDefaultingNamespace() || this.type == "any") && (context.item == this.item.asIdentifierDefaultingNamespace() || this.item == "any")
}
}
}
@@ -17,23 +17,23 @@ import net.minecraft.advancements.critereon.EntityPredicate
import net.minecraft.server.level.ServerPlayer
import net.minecraft.resources.ResourceLocation
class ReelInPokemonContext(val species : ResourceLocation, val baitId: ResourceLocation)
class ReelInPokemonContext(val pokemonId : ResourceLocation, val baitId: ResourceLocation)
class ReelInPokemonCriterionCondition(
playerCtx: Optional<ContextAwarePredicate>,
val species: String,
val pokemonId: String,
val baitId: String
) : SimpleCriterionCondition<ReelInPokemonContext>(playerCtx) {
companion object {
val CODEC: Codec<ReelInPokemonCriterionCondition> = RecordCodecBuilder.create { it.group(
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter { it.playerCtx },
Codec.STRING.optionalFieldOf("species", "any").forGetter { it.species },
Codec.STRING.optionalFieldOf("pokemonId", "any").forGetter { it.pokemonId },
Codec.STRING.optionalFieldOf("baitId", "empty_bait").forGetter { it.baitId }
).apply(it, { playerCtx, pokemonId, baitId -> ReelInPokemonCriterionCondition(playerCtx, pokemonId, baitId.ifEmpty { "empty_bait" }) }) }
}
override fun matches(player: ServerPlayer, context: ReelInPokemonContext): Boolean {
return (context.species == this.species.asIdentifierDefaultingNamespace() || this.species == "any") && (context.baitId == this.baitId.asIdentifierDefaultingNamespace() || this.baitId == "empty_bait")
return (context.pokemonId == this.pokemonId.asIdentifierDefaultingNamespace() || this.pokemonId == "any") && (context.baitId == this.baitId.asIdentifierDefaultingNamespace() || this.baitId == "empty_bait")
}
}
@@ -8,15 +8,12 @@
package com.cobblemon.mod.common.api.ai
import com.cobblemon.mod.common.api.ai.config.BehaviourConfig
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
import net.minecraft.resources.ResourceLocation
import com.cobblemon.mod.common.api.ai.config.BrainConfig
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.schedule.Activity
import net.minecraft.world.entity.schedule.Schedule
class BehaviourConfigurationContext {
var appliedBehaviours: Set<ResourceLocation> = setOf()
class BrainConfigurationContext {
var defaultActivity = Activity.IDLE
var coreActivities = setOf(Activity.CORE)
@@ -27,16 +24,11 @@ class BehaviourConfigurationContext {
return activities.firstOrNull { it.activity == activity } ?: ActivityConfigurationContext(activity).also(activities::add)
}
fun apply(entity: LivingEntity, behaviourConfigs: List<BehaviourConfig>) {
fun apply(entity: LivingEntity, brainConfigs: List<BrainConfig>) {
val brain = entity.brain
if (entity is MoLangScriptingEntity) {
entity.registerVariables(behaviourConfigs.flatMap { it.getVariables(entity) })
entity.initializeScripting()
}
// Setup the brain config
behaviourConfigs.forEach { it.configure(entity, this) }
brainConfigs.forEach { it.configure(entity, this) }
// Apply the brain config
activities.forEach { it.apply(entity) }
@@ -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.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.util.asTranslated
import com.cobblemon.mod.common.util.resolve
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.LivingEntity
class CobblemonBehaviour(
val name: Component = "".asTranslated(),
val description: Component = "".asTranslated(),
/** Some behaviours aren't really worth showing on the client, namely ones that are simply conditional bundles of other behaviours. */
val visible: Boolean = true,
val entityType: ResourceLocation? = null,
val configurations: List<BehaviourConfig> = mutableListOf(),
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) {
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 undo(entity: LivingEntity) {
val runtime = MoLangRuntime().setup()
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (undoScript != null) {
CobblemonScripts.run(undoScript, runtime)
}
if (undo != null) {
runtime.resolve(undo)
}
}
}
@@ -1,20 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.mojang.datafixers.util.Either
import kotlin.jvm.optionals.getOrNull
typealias ExpressionOrEntityVariable = Either<Expression, MoLangConfigVariable>
fun List<ExpressionOrEntityVariable>.asVariables() = mapNotNull { it.right().getOrNull() }
@@ -8,34 +8,32 @@
package com.cobblemon.mod.common.api.ai.config
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.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.ai.config.task.TaskConfig
import com.cobblemon.mod.common.util.asExpression
import com.mojang.datafixers.util.Either
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.schedule.Activity
class AddTasksToActivity : BehaviourConfig {
val activity: Activity? = null
// Can be useful to add to multiple activities at once
val activities = mutableListOf<Activity>()
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
class AddTasksToActivity : BrainConfig {
val activity = Activity.IDLE
val condition = "true".asExpressionLike()
val tasksByPriority = mutableMapOf<Int, List<TaskConfig>>()
override fun getVariables(entity: LivingEntity) = tasksByPriority.values.flatten().flatMap { it.getVariables(entity) } + listOf(condition).asVariables()
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (!checkCondition(entity, condition)) return
val activities = if (activity != null) (activities + activity) else activities
override fun configure(entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext) {
val runtime = MoLangRuntime().setup()
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return
val activity = brainConfigurationContext.getOrCreateActivity(activity)
tasksByPriority.forEach { (priority, taskConfigs) ->
val tasks = taskConfigs.flatMap { it.createTasks(entity, behaviourConfigurationContext) }
for (activity in activities) {
val activityContext = behaviourConfigurationContext.getOrCreateActivity(activity)
activityContext.addTasks(priority, *tasks.toTypedArray())
}
val tasks = taskConfigs.flatMap { it.createTasks(entity, brainConfigurationContext) }
activity.addTasks(priority, *tasks.toTypedArray())
}
}
}
@@ -1,20 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai.config
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import net.minecraft.world.entity.LivingEntity
class AddVariablesConfig(val variables: List<MoLangConfigVariable> = emptyList()) : BehaviourConfig {
override fun getVariables(entity: LivingEntity) = variables
// Configuration happens naturally through the override on variables
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {}
}
@@ -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
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonBehaviours
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.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.util.asExpression
import com.google.gson.annotations.SerializedName
import com.mojang.datafixers.util.Either
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.LivingEntity
class ApplyBehaviours : BehaviourConfig {
var condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
@SerializedName("behaviours", alternate = ["behaviors"])
val behaviours = mutableListOf<ResourceLocation>()
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return if (checkCondition(entity, condition)) {
behaviours.flatMap {
CobblemonBehaviours.behaviours[it]?.configurations?.flatMap { it.getVariables(entity) } ?: emptyList()
} + listOf(condition).asVariables()
} else {
listOf(condition).asVariables()
}
}
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (!checkCondition(entity, condition)) return
val configurations = behaviours.map { CobblemonBehaviours.behaviours[it]?.takeIf { it.canBeApplied(entity) } ?: return Cobblemon.LOGGER.warn("Behaviour $it not found") }
// Why not just add the presets to the context directly?
// Nested preset application is a thing, and I only want to track the top level presets.
// i.e. if a preset applies another preset, I don't want to track the inner preset, since it's redundant if we track the top one.
val originalContextBehaviours = behaviourConfigurationContext.appliedBehaviours.toMutableSet()
configurations.forEach {
it.configure(entity, behaviourConfigurationContext)
}
originalContextBehaviours.addAll(behaviours)
behaviourConfigurationContext.appliedBehaviours = originalContextBehaviours
}
}
@@ -0,0 +1,35 @@
/*
* 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
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonBrainConfigs
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.LivingEntity
class ApplyPresets : BrainConfig {
val condition = "true".asExpressionLike()
val presets = mutableListOf<ResourceLocation>()
override fun configure(entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext) {
val runtime = MoLangRuntime().setup()
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return
val presetConfigs = presets.flatMap { CobblemonBrainConfigs.presets[it] ?: return Cobblemon.LOGGER.warn("Preset $it not found") }
presetConfigs.forEach { it.configure(entity, brainConfigurationContext) }
}
}
@@ -1,58 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai.config
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.LivingEntity
/**
* Something that contributes to the construction of an entity's brain. Implementations are expected to make
* their changes using the provided [BehaviourConfigurationContext] which helps stage the details of the entity's
* brain before locking into the highly immutable structure that Mojang uses.
*
* A brain config has access to the [LivingEntity] it's for but note that this is, in most cases, an extremely
* young version of the entity that is not fully initialized and might die if you try getting exotic with it.
*
* @see BehaviourConfigurationContext
* @author Hiroku
* @since October 13th, 2024
*/
interface BehaviourConfig {
companion object {
val types = mutableMapOf<ResourceLocation, Class<out BehaviourConfig>>(
cobblemonResource("script") to ScriptBehaviourConfig::class.java,
cobblemonResource("custom_script") to CustomScriptBehaviourConfig::class.java,
cobblemonResource("add_tasks_to_activity") to AddTasksToActivity::class.java,
cobblemonResource("apply_behaviours") to ApplyBehaviours::class.java,
cobblemonResource("set_default_activity") to SetDefaultActivity::class.java,
cobblemonResource("set_core_activities") to SetCoreActivities::class.java,
cobblemonResource("add_variables") to AddVariablesConfig::class.java,
cobblemonResource("set_variables") to SetVariablesConfig::class.java,
)
}
fun checkCondition(entity: LivingEntity, expressionOrEntityVariable: ExpressionOrEntityVariable): Boolean {
val runtime = MoLangRuntime().setup()
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
return runtime.resolveBoolean(expressionOrEntityVariable.map({ it }, { "q.entity.config.${it.variableName}".asExpression() }))
}
fun getVariables(entity: LivingEntity): List<MoLangConfigVariable>
fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext)
}
@@ -0,0 +1,41 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai.config
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import net.minecraft.world.entity.LivingEntity
/**
* Something that contributes to the construction of an entity's brain. Implementations are expected to make
* their changes using the provided [BrainConfigurationContext] which helps stage the details of the entity's
* brain before locking into the highly immutable structure that Mojang uses.
*
* A brain config has access to the [LivingEntity] it's for but note that this is, in most cases, an extremely
* young version of the entity that is not fully initialized and might die if you try getting exotic with it.
*
* @see BrainConfigurationContext
* @author Hiroku
* @since October 13th, 2024
*/
interface BrainConfig {
companion object {
val types = mutableMapOf<String, Class<out BrainConfig>>(
"script" to ScriptBrainConfig::class.java,
"add_tasks_to_activity" to AddTasksToActivity::class.java,
"apply_presets" to ApplyPresets::class.java,
"set_default_activity" to SetDefaultActivity::class.java,
"set_core_activities" to SetCoreActivities::class.java
)
}
// fun encode(buffer: RegistryFriendlyByteBuf)
// fun decode(buffer: RegistryFriendlyByteBuf)
fun configure(entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext)
}
@@ -1,40 +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
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
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.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolve
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
class CustomScriptBehaviourConfig : BehaviourConfig {
var condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
val variables: List<MoLangConfigVariable> = emptyList()
val script: ExpressionLike = "0".asExpressionLike()
companion object {
val runtime = MoLangRuntime().setup()
}
override fun getVariables(entity: LivingEntity) = variables
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (!checkCondition(entity, condition)) return
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
runtime.resolve(script)
}
}
@@ -0,0 +1,16 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai.config
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import net.minecraft.world.entity.LivingEntity
object DummyBrainConfig : BrainConfig {
override fun configure(entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext) {}
}
@@ -1,13 +1,12 @@
# Behaviour Configuration
# Brain Configuration
A "Brain" is a Java Edition AI concept for more modern entities. It has tasks that are listed inside 'activities',
A "Brain" is a Java Edition AI concept for more modern entities. It has tasks that are listed inside of 'activities',
and in some cases a schedule which determines which activities will occur during different times of the day. There is
also a set of core activities, and a default activity. In Minecraft, the most complex usage of the Brain AI system
is on villagers which have schedules that control their behaviour during the day and night as well as child villager play behaviours.
Cobblemon's Pokémon and NPCs use the Brain AI system. As part of that, Cobblemon added the concept of a Behaviour Configuration
that allows Java Edition brains to be configured in a more data-driven way, as well as to support the configuration of
other logical properties of entities that are configured at the same initialization step.
Cobblemon's NPCs use the Brain AI system. As part of that, Cobblemon added the concept of a Brain Configuration
that allows Java Edition brains to be configured in a more data-driven way.
By default, without any brain configuration, the default activity will be `minecraft:idle` and the core activities will be `[minecraft:core]`.
The activities would not have any tasks though.
@@ -49,18 +48,18 @@ The activities that were added by Cobblemon are:
- `cobblemon:action_effect`: For when the entity is performing an action effect. Usually registered as an almost empty activity that simply prevents the entity from doing other things.
- `cobblemon:npc_chatting`: Intended for NPC entities using dialogue so that they look at their chat partner and do nothing else.
## Behaviour Configuration Format
A Behaviour is configured using a series of "Behaviour Configurations". Each configuration is a JSON object that contains
a "type" field which determines the action to perform when configuring an entity. Many different types are available.
## Brain Configuration Format
A brain is configured using a series of "Brain Configurations". Each configuration is a JSON object that contains
a "type" field which determines the action to perform when configuring a brain. Many different types are available.
### script
The `script` type executes a MoLang script which will apply some changes to the entity or brain.
The `script` type executes a MoLang script which will apply some changes to the brain.
- `script`: The MoLang script to execute. This will be a resource location for the `molang` datapack folder. Something like `cobblemon:some_script`.
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if the script should be executed. Defaults to `"true"`.
### add_tasks_to_activity
The 'add_tasks_to_activity' type adds a list of tasks to an activity.
- `activity`: The activity to add the tasks to. This must be a pre-existing activity - a behaviour configuration cannot
- `activity`: The activity to add the tasks to. This must be a pre-existing activity - a brain configuration cannot
create new activities.
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if the tasks should be added. If this
is not present, the tasks will always be added.
@@ -97,107 +96,24 @@ The `set_core_activities` type sets the core activities of the brain. It has onl
### set_default_activity
The `set_default_activity` type sets the default activity of the brain. It has only one property, `activity`, which is the activity to set as the default.
### apply_behaviours
The `apply_behaviours` type applies some number of other Behaviours to the entity. These behaviours are JSONs under the `behaviours` datapack folder.
This is a way to apply a set of configurations to the entity.
- `behaviours`: A list of behaviour resource locations to apply. This is the name of the file in the `behaviours` folder without the `.json` extension.
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if the behaviour should be applied. Defaults to `"true"`.
- `visible`: Whether the behaviour should be visible in the UI. Defaults to `true`. If it is not visible, name and description can be left out.
- `name`: The name of the behaviour. This is used in the UI to label the behaviour. This can be a translation key.
- `description`: A description of the behaviour. This is used in the UI to describe what the behaviour does. This can be a translation key.
- `entityType`: The entity type that the behaviour applies to. Defaults to null. This is used to determine if the behaviour should be applied to the entity type..
### apply_preset
The `apply_preset` type applies a preset to the brain. Brain presets are JSON arrays under the `brain_presets` datapack folder.
This is a way to apply a set of configurations to the brain.
- `preset`: The name of the preset to apply. This is the name of the file in the `brain_presets` folder without the `.json` extension.
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if the preset should be applied. Defaults to `"true"`.
Example behaviour (`uses_healing_machine.json`):
Example brain preset (`uses_healing_machine.json`):
```json
{
"name": "cobblemon.behaviour.uses_healing_machine.name",
"description": "cobblemon.behaviour.uses_healing_machine.desc",
"entityType": "cobblemon:npc",
"visible": true,
"configurations": [
{
"type": "add_tasks_to_activity",
"activity": "idle",
"tasksByPriority": {
"1": [
"heal_using_healing_machine",
"go_to_healing_machine"
]
}
[
{
"type": "add_tasks_to_activity",
"activity": "idle",
"tasksByPriority": {
"1": [
"heal_using_healing_machine",
"go_to_healing_machine"
]
}
]
}
```
### add_variables
The `add_variables` type adds some number of MoLang variables to the entity that can be configured in-game or left with defaults.
- `variables`: A list of objects, each representing a variable that can be configured.
Each variable has the following properties:
- `variableName`: The name of the variable. This is the name that will be accessible from MoLang scripts.
- `defaultValue`: The default value of the variable. This is a MoLang expression that will be used if the variable is not set.
- `displayName`: The display name of the variable. This is used in the UI to label the variable. This can be a translation key.
- `description`: A description of the variable. This is used in the UI to describe what the variable does. This can be a translation key.
- `type`: The type of the variable. This can be `TEXT`, `NUMBER`, or `BOOLEAN`.
Example:
```json
{
"type": "add_variables",
"variables": [
{
"variableName": "can_heal_using_machine",
"defaultValue": "false",
"displayName": "Can Use Healing Machine",
"description": "Whether or not the entity can use a healing machine.",
"type": "BOOLEAN"
}
]
}
```
### set_variables
The `set_variables` type sets some values for pre-established variables. This is useful in cases where a preset has defined a variable
but it's locked in by a specific entity using that preset. This strongly forces the variable to a specific value. In future this
probably should deregister the variable so that the client cannot edit them once they've been forced, as currently that will
be possible yet the value will get repeatedly overwritten.
The values inside the variables map can be MoLang expressions (with `q.entity` as the entity) or simple JSON primitives.
Example:
```json
{
"type": "set_variables",
"variables": {
"aggressive": true,
"attack_range": "10"
}
}
]
```
### script
The `script` type executes a registered MoLang script which will apply some changes to the brain or entity. This can be
useful if the goal is to apply some kind of configuration to the entity that would otherwise be performed by an NPC preset
or NPC class. `q.entity` will refer to the entity. This is powerful when combined with [add_variables](#add_variables)
configs in the same behaviour preset.
Example:
```json
{
"type": "script",
"script": "cobblemon:configure_npc_party"
}
```
### custom_script
The `custom_script` type executes a custom MoLang script from this config which will apply some changes to the brain or
entity. This is useful for when you want to apply some kind of configuration to the entity that would otherwise be
by an NPC preset or NPC class. `q.entity` will refer to the entity.
Example:
```json
{
"type": "custom_script",
"script": "q.entity.set_pathfinding_malus('WATER', -1);"
}
```
@@ -13,13 +13,12 @@ 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.api.ai.ActivityConfigurationContext
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
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.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.activityRegistry
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
@@ -31,16 +30,13 @@ import com.mojang.datafixers.util.Pair
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class ScriptBehaviourConfig : BehaviourConfig {
class ScriptBrainConfig : BrainConfig {
val condition = "true".asExpressionLike()
val script = cobblemonResource("dummy")
val variables = mutableListOf<MoLangConfigVariable>()
override fun getVariables(entity: LivingEntity) = variables
override fun configure(entity: LivingEntity, context: BehaviourConfigurationContext) {
override fun configure(entity: LivingEntity, context: BrainConfigurationContext) {
val runtime = MoLangRuntime().setup()
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return
val struct = createBrainStruct(entity, context)
@@ -52,30 +48,30 @@ class ScriptBehaviourConfig : BehaviourConfig {
runtime.resolve(script, mapOf("brain" to struct))
}
fun createBrainStruct(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): QueryStruct {
fun createBrainStruct(entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext): QueryStruct {
return QueryStruct(hashMapOf()).addStandardFunctions()
.addFunction("entity") { entity.asMostSpecificMoLangValue() }
.addFunction("entity") { (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()) }
.addFunction("create_activity") { params ->
val name = params.getString(0).asIdentifierDefaultingNamespace()
val activity = entity.level().activityRegistry.get(name) ?: return@addFunction run {
Cobblemon.LOGGER.error("Tried loading activity $name as part of an entity brain but that activity does not exist")
DoubleValue.ZERO
}
val existingActivityBuilder = behaviourConfigurationContext.activities.find { it.activity == activity }
val existingActivityBuilder = brainConfigurationContext.activities.find { it.activity == activity }
if (existingActivityBuilder != null) {
return@addFunction createActivityStruct(existingActivityBuilder)
} else {
val activityConfigurationContext = ActivityConfigurationContext(activity)
behaviourConfigurationContext.activities.add(activityConfigurationContext)
brainConfigurationContext.activities.add(activityConfigurationContext)
return@addFunction createActivityStruct(activityConfigurationContext)
}
}
.addFunction("set_core_activities") { params ->
behaviourConfigurationContext.coreActivities = params.params.map { (it as ObjectValue<ActivityConfigurationContext>).obj.activity }.toSet()
brainConfigurationContext.coreActivities = params.params.map { (it as ObjectValue<ActivityConfigurationContext>).obj.activity }.toSet()
return@addFunction DoubleValue.ONE
}
.addFunction("set_default_activity") { params ->
behaviourConfigurationContext.defaultActivity = params.get<ObjectValue<ActivityConfigurationContext>>(0).obj.activity
brainConfigurationContext.defaultActivity = params.get<ObjectValue<ActivityConfigurationContext>>(0).obj.activity
return@addFunction DoubleValue.ONE
}
}
@@ -8,15 +8,13 @@
package com.cobblemon.mod.common.api.ai.config
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.schedule.Activity
class SetCoreActivities : BehaviourConfig {
class SetCoreActivities : BrainConfig {
val activities = mutableListOf<Activity>()
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
behaviourConfigurationContext.coreActivities = activities.toSet()
override fun configure(entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext) {
brainConfigurationContext.coreActivities = activities.toSet()
}
}
@@ -8,15 +8,13 @@
package com.cobblemon.mod.common.api.ai.config
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.schedule.Activity
class SetDefaultActivity : BehaviourConfig {
class SetDefaultActivity : BrainConfig {
val activity: Activity = Activity.IDLE
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
behaviourConfigurationContext.defaultActivity = activity
override fun configure(entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext) {
brainConfigurationContext.defaultActivity = activity
}
}
@@ -1,36 +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
import com.bedrockk.molang.runtime.MoLangRuntime
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.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
import com.cobblemon.mod.common.util.resolve
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
class SetVariablesConfig : BehaviourConfig {
var variableValues = mutableMapOf<String, ExpressionLike>()
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
val runtime = MoLangRuntime().setup()
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (entity is MoLangScriptingEntity) {
variableValues.forEach { (variableName, valueExpression) ->
entity.config.setDirectly(variableName, runtime.resolve(valueExpression))
}
}
}
}
@@ -1,76 +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.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.ai.config.task.WanderTaskConfig.Companion.WANDER
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
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.memory.WalkTarget
import net.minecraft.world.entity.ai.util.HoverRandomPos
class AirWanderTaskConfig : SingleTaskConfig {
val condition = booleanVariable(WANDER, "air_wanders", true).asExpressible()
val wanderChance = numberVariable(WANDER, "air_wander_chance", 1/(20 * 1F)).asExpressible()
val horizontalRange = numberVariable(WANDER, "horizontal_wander_range", 20).asExpressible()
val verticalRange = numberVariable(WANDER, "vertical_wander_range", 5).asExpressible()
val speedMultiplier = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
val minUpwardsMovement: ExpressionOrEntityVariable = Either.left("3.0".asExpression())
val minDownwardsMovement: ExpressionOrEntityVariable = Either.left("3.0".asExpression())
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return listOf(condition, wanderChance, horizontalRange, verticalRange, speedMultiplier, minUpwardsMovement, minDownwardsMovement).asVariables()
}
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
val wanderChanceExpression = wanderChance.asSimplifiedExpression(entity)
return BehaviorBuilder.create {
it.group(
it.absent(MemoryModuleType.WALK_TARGET),
it.registered(MemoryModuleType.LOOK_TARGET)
).apply(it) { walkTarget, lookTarget ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || entity.isInWater) {
return@Trigger false
}
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val wanderChance = runtime.resolveFloat(wanderChanceExpression)
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance) return@Trigger false
val rotVec = entity.getViewVector(0F)
val target = HoverRandomPos.getPos(entity, horizontalRange.resolveInt(), verticalRange.resolveInt(), rotVec.x, rotVec.y, 1.5707964f, minUpwardsMovement.resolveInt(), minDownwardsMovement.resolveInt())
?: return@Trigger false
walkTarget.set(WalkTarget(target, speedMultiplier.resolveFloat(), 3))
lookTarget.set(BlockPosTracker(target))
return@Trigger true
}
}
}
}
}
@@ -8,34 +8,25 @@
package com.cobblemon.mod.common.api.ai.config.task
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.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.util.asExpression
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class AllOfTaskConfig : TaskConfig {
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
val condition = "true".asExpressionLike()
val tasks: List<TaskConfig> = emptyList()
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return if (checkCondition(entity, condition))
tasks.flatMap { it.getVariables(entity) } + listOf(condition).asVariables()
else
listOf(condition).asVariables()
}
override fun createTasks(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): List<BehaviorControl<in LivingEntity>> {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return emptyList()
return tasks.flatMap { it.createTasks(entity, behaviourConfigurationContext) }
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return emptyList()
return tasks.flatMap { it.createTasks(entity, brainConfigurationContext) }
}
}
@@ -8,24 +8,24 @@
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.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.ai.AttackAngryAtTask
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class AttackAngryAtTaskConfig : SingleTaskConfig {
val condition = booleanVariable(SharedEntityVariables.ATTACKING_CATEGORY, "should_attack_angry_at", true).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(condition).asVariables()
val condition = "true".asExpressionLike()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
return AttackAngryAtTask.create()
}
}
@@ -8,26 +8,24 @@
package com.cobblemon.mod.common.api.ai.config.task
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.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.VillagerCalmDown
class CalmDownTaskConfig : SingleTaskConfig {
var condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
override fun getVariables(entity: LivingEntity) = listOf(condition).asVariables()
var condition = "true".asExpression()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
return VillagerCalmDown.create()
}
}
@@ -8,29 +8,30 @@
package com.cobblemon.mod.common.api.ai.config.task
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.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.DoNothing
class DoNothingTaskConfig : SingleTaskConfig {
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
val minDurationTicks: ExpressionOrEntityVariable = Either.left("40".asExpression())
val maxDurationTicks: ExpressionOrEntityVariable = Either.left("80".asExpression())
override fun getVariables(entity: LivingEntity) = listOf(condition, minDurationTicks, maxDurationTicks).asVariables()
val condition: ExpressionLike = "true".asExpressionLike()
val minDurationTicks = "40".asExpression()
val maxDurationTicks = "80".asExpression()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
return DoNothing(minDurationTicks.resolveInt(), maxDurationTicks.resolveInt())
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
return DoNothing(runtime.resolveInt(minDurationTicks), runtime.resolveInt(maxDurationTicks))
}
}
@@ -1,58 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.EatGrassTask
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class EatGrassTaskConfig : SingleTaskConfig {
companion object {
const val EATING_GRASS = "eating_grass"
const val COOLDOWN = "eating_grass_cooldown_ticks"
const val EAT_GRASS_CHANCE = "eat_grass_chance"
}
val cooldown = numberVariable(category = EATING_GRASS, name = COOLDOWN, default = 20 * 20).asExpressible()
val eatGrassChance = numberVariable(category = EATING_GRASS, name = EAT_GRASS_CHANCE, default = 1 / (20 * 20F)).asExpressible()
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return listOf(
cooldown,
eatGrassChance
).asVariables()
}
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (entity !is PokemonEntity) {
return null
}
val chance = eatGrassChance.resolveFloat()
if (chance == 0F) {
return null
}
return WrapperLivingEntityTask(
EatGrassTask(
chance,
cooldown.resolveInt().toLong()
),
PokemonEntity::class.java
)
}
}
@@ -8,13 +8,14 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
@@ -23,21 +24,15 @@ import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class ExitBattleWhenHurtTaskConfig : SingleTaskConfig {
var condition = booleanVariable(SharedEntityVariables.BATTLING_CATEGORY, "exit_battle_when_hurt", true).asExpressible()
var includePassiveDamage = booleanVariable(SharedEntityVariables.BATTLING_CATEGORY, "exit_battle_from_passive_damage", true).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
includePassiveDamage
).asVariables()
var condition = "true".asExpression()
var includePassiveDamage = true
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val includePassiveDamage = includePassiveDamage.resolveBoolean()
if (!condition.resolveBoolean()) return null
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
if (entity is NPCEntity) {
fun cancelNPCBattles(npcEntity: NPCEntity): Boolean {
@@ -57,7 +52,7 @@ class ExitBattleWhenHurtTaskConfig : SingleTaskConfig {
.apply(it) { _, _ -> Trigger { world, entity, _ -> return@Trigger cancelNPCBattles(entity as NPCEntity) } }
}
}
} else if (entity is PokemonEntity) {
} else { return null }/* else if (entity is PokemonEntity) { TODO uncomment when pokemon have brain :)
fun cancelPokemonBattle(pokemonEntity: PokemonEntity): Boolean {
val battle = BattleRegistry.getBattle(pokemonEntity.battleId ?: return false)
battle?.end()
@@ -66,17 +61,15 @@ class ExitBattleWhenHurtTaskConfig : SingleTaskConfig {
return if (includePassiveDamage) {
BehaviorBuilder.create {
it.group(it.present(MemoryModuleType.HURT_BY), it.present(CobblemonMemories.POKEMON_BATTLE))
it.group(it.present(MemoryModuleType.HURT_BY), it.present(CobblemonMemories.POKEMON_BATTLING))
.apply(it) { _, _ -> Trigger { world, entity, _ -> return@Trigger cancelPokemonBattle(entity as PokemonEntity) } }
}
} else {
BehaviorBuilder.create {
it.group(it.present(MemoryModuleType.HURT_BY_ENTITY), it.present(CobblemonMemories.POKEMON_BATTLE))
it.group(it.present(MemoryModuleType.HURT_BY_ENTITY), it.present(CobblemonMemories.POKEMON_BATTLING))
.apply(it) { _, _ -> Trigger { world, entity, _ -> return@Trigger cancelPokemonBattle(entity as PokemonEntity) } }
}
}
} else {
return null
}
}*/
}
}
@@ -1,86 +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.asVariables
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.util.closestPosition
import com.cobblemon.mod.common.util.getIsSubmerged
import net.minecraft.core.BlockPos
import net.minecraft.util.Mth
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.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.level.LevelReader
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.pathfinder.PathComputationType
/**
* Moves to the nearest surface it can find so it can breathe.
*/
class FindAirTaskConfig : SingleTaskConfig {
val walkSpeed = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return listOf(walkSpeed).asVariables()
}
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (entity !is PathfinderMob) {
return null
}
return BehaviorBuilder.create {
it.group(
it.absent(MemoryModuleType.WALK_TARGET),
it.absent(CobblemonMemories.PATH_COOLDOWN)
).apply(it) { walkTarget, pathCooldown ->
Trigger { world, entity, time ->
if (!entity.getIsSubmerged()) {
return@Trigger false
}
val iterable = BlockPos.betweenClosed(
Mth.floor(entity.x - 4.0),
entity.blockY - 1,
Mth.floor(entity.z - 4.0),
Mth.floor(entity.x + 4.0),
Mth.floor(entity.y + 8.0),
Mth.floor(entity.z + 4.0)
)
pathCooldown.setWithExpiry(true, 20L)
val blockPos = entity.closestPosition(iterable) { isAirPos(entity.level(), it) }
?: return@Trigger false
walkTarget.set(WalkTarget(blockPos.above(), 0.35F, 0))
return@Trigger true
}
}
}
}
private fun isAirPos(world: LevelReader, pos: BlockPos): Boolean {
val blockState = world.getBlockState(pos)
val aboveState = world.getBlockState(pos.above())
val notFluid = world.getFluidState(pos.above()).isEmpty || blockState.`is`(Blocks.BUBBLE_COLUMN)
val canPathfindThroughAbove = aboveState.isPathfindable(PathComputationType.LAND)
val solidBelow = !blockState.isPathfindable(PathComputationType.LAND)
return notFluid && canPathfindThroughAbove && solidBelow
}
}
@@ -1,36 +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.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
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.FindRestingPlaceTask
import com.cobblemon.mod.common.util.asExpression
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class FindRestingPlaceTaskConfig : SingleTaskConfig {
val horizontalSearchDistance: ExpressionOrEntityVariable = Either.left("16".asExpression())
val verticalSearchDistance: ExpressionOrEntityVariable = Either.left("5".asExpression())
override fun getVariables(entity: LivingEntity) = listOf(horizontalSearchDistance, verticalSearchDistance).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(
FindRestingPlaceTask.create(horizontalSearchDistance.resolveInt(), verticalSearchDistance.resolveInt()),
PokemonEntity::class.java
)
}
}
@@ -8,12 +8,14 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_DESIRED_DISTANCE
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_SPEED_MULTIPLIER
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
@@ -22,24 +24,18 @@ import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class FleeAttackerTaskConfig : SingleTaskConfig {
var condition = booleanVariable(SharedEntityVariables.FEAR_CATEGORY, "flee_attacker", true).asExpressible()
var speedMultiplier = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_SPEED_MULTIPLIER, 0.5).asExpressible()
var desiredDistance = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_DESIRED_DISTANCE, 9).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
speedMultiplier,
desiredDistance
).asVariables()
var condition = "true".asExpression()
var speedMultiplier = "0.5".asExpression()
var desiredDistance = "9".asExpression()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PathfinderMob) return null
val speedMultiplier = speedMultiplier.resolveFloat()
val desiredDistance = desiredDistance.resolveInt()
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition) || entity !is PathfinderMob) return null
val speedMultiplier = runtime.resolveFloat(speedMultiplier)
val desiredDistance = runtime.resolveInt(desiredDistance)
return WrapperLivingEntityTask(
SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, speedMultiplier, desiredDistance, false),
PathfinderMob::class.java
@@ -8,12 +8,14 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_DESIRED_DISTANCE
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_SPEED_MULTIPLIER
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
@@ -22,20 +24,18 @@ import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class FleeNearestHostileTaskConfig : SingleTaskConfig {
var condition = booleanVariable(SharedEntityVariables.FEAR_CATEGORY, "flee_nearest_hostile", true).asExpressible()
var speedMultiplier = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_SPEED_MULTIPLIER, 0.5).asExpressible()
var desiredDistance = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_DESIRED_DISTANCE, 9).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(condition, speedMultiplier, desiredDistance).asVariables()
var condition = "true".asExpression()
var speedMultiplier = "0.5".asExpression()
var desiredDistance = "9".asExpression()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PathfinderMob) return null
val speedMultiplier = speedMultiplier.resolveFloat()
val desiredDistance = desiredDistance.resolveInt()
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition) || entity !is PathfinderMob) return null
val speedMultiplier = runtime.resolveFloat(speedMultiplier)
val desiredDistance = runtime.resolveInt(desiredDistance)
return WrapperLivingEntityTask(
SetWalkTargetAwayFrom.entity(MemoryModuleType.NEAREST_HOSTILE, speedMultiplier, desiredDistance, false),
PathfinderMob::class.java
@@ -8,30 +8,32 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.ai.FollowWalkTargetTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class FollowWalkTargetTaskConfig : SingleTaskConfig {
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
val minRunTicks: ExpressionOrEntityVariable = Either.left("150".asExpression())
val maxRunTicks: ExpressionOrEntityVariable = Either.left("250".asExpression())
val condition: ExpressionLike = "true".asExpressionLike()
val minRunTicks: Expression = "150".asExpression()
val maxRunTicks: Expression = "250".asExpression()
override fun getVariables(entity: LivingEntity) = listOf(minRunTicks, maxRunTicks).asVariables()
override fun createTask(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): BehaviorControl<LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
override fun createTask(entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext): BehaviorControl<LivingEntity>? {
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
return WrapperLivingEntityTask(
FollowWalkTargetTask(minRunTicks.resolveInt(), maxRunTicks.resolveInt()),
FollowWalkTargetTask(runtime.resolveInt(minRunTicks), runtime.resolveInt(maxRunTicks)),
PathfinderMob::class.java
)
}
@@ -8,30 +8,26 @@
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.api.ai.config.task.SharedEntityVariables.ATTACKING_CATEGORY
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.ai.GetAngryAtAttackerTask
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class GetAngryAtAttackerTaskConfig : SingleTaskConfig {
companion object {
const val GET_ANGRY_AT_ATTACKER = "get_angry_at_attacker"
}
val condition = booleanVariable(ATTACKING_CATEGORY, GET_ANGRY_AT_ATTACKER, true).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(condition).asVariables()
val condition: ExpressionLike = "true".asExpressionLike()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
return GetAngryAtAttackerTask.create()
}
}
@@ -8,43 +8,38 @@
package com.cobblemon.mod.common.api.ai.config.task
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.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.npc.ai.GoToHealingMachineTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class GoToHealingMachineTaskConfig : SingleTaskConfig {
val condition = booleanVariable(SELF_HEALING, USE_HEALING_MACHINES, true).asExpressible()
val horizontalSearchRange: ExpressionOrEntityVariable = Either.left("10".asExpression())
val verticalSearchRange: ExpressionOrEntityVariable = Either.left("5".asExpression())
val completionRange: ExpressionOrEntityVariable = Either.left("1".asExpression())
val walkSpeed = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(condition, walkSpeed, horizontalSearchRange, verticalSearchRange, completionRange).asVariables()
companion object {
const val SELF_HEALING = "self_healing"
const val USE_HEALING_MACHINES = "use_healing_machines"
}
class GoToHealingMachineTaskConfig(
val condition: ExpressionLike = "true".asExpressionLike(),
val horizontalSearchRange: Expression = "10".asExpression(),
val verticalSearchRange: Expression = "5".asExpression(),
val speedMultiplier: Expression = "0.35".asExpression(),
val completionRange: Expression = "1".asExpression()
) : SingleTaskConfig {
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
return GoToHealingMachineTask.create(
horizontalSearchRange = horizontalSearchRange.asExpression(),
verticalSearchRange = verticalSearchRange.asExpression(),
speedMultiplier = walkSpeed.asExpression(),
completionRange = completionRange.asExpression()
horizontalSearchRange = horizontalSearchRange,
verticalSearchRange = verticalSearchRange,
speedMultiplier = speedMultiplier,
completionRange = completionRange
)
}
@@ -1,122 +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.asVariables
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.util.closestPosition
import kotlin.math.ceil
import kotlin.math.floor
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.util.Mth
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.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.level.pathfinder.PathComputationType
/**
* Seeks land if it's underwater.
*/
class GoToLandTaskConfig : SingleTaskConfig {
val walkSpeed = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return listOf(walkSpeed).asVariables()
}
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (entity !is PathfinderMob) {
return null
}
return BehaviorBuilder.create {
it.group(
it.absent(MemoryModuleType.WALK_TARGET),
it.absent(CobblemonMemories.PATH_COOLDOWN)
).apply(it) { walkTarget, pathCooldown ->
Trigger { world, entity, time ->
if (!entity.isInLiquid) {
return@Trigger false
}
entity as PathfinderMob
val iterable = BlockPos.betweenClosed(
Mth.floor(entity.x - 8.0),
entity.blockY,
Mth.floor(entity.z - 8.0),
Mth.floor(entity.x + 8.0),
Mth.floor(entity.y + 2.0),
Mth.floor(entity.z + 8.0)
)
pathCooldown.setWithExpiry(true, 40L)
val blockPos = entity.closestPosition(iterable) {
isSafeLandPosAround(entity.level() as ServerLevel, it, entity)
} ?: return@Trigger false
walkTarget.set(WalkTarget(blockPos.above(), 0.35F, 1))
return@Trigger true
}
}
}
}
fun isSafeLandPos(world: ServerLevel, pos: BlockPos, mob: PathfinderMob): Boolean {
val blockState = world.getBlockState(pos)
val isFluid = world.getFluidState(pos.above()).isEmpty.not()
val solidBelow = blockState.isSolid
if (isFluid || !solidBelow) {
return false
} else {
val mutable = BlockPos.MutableBlockPos()
val minY = pos.y + 1
val maxY = minY + ceil(mob.bbHeight).toInt()
for (y in minY..maxY) {
val aboveState = world.getBlockState(mutable.set(pos.x, y, pos.z))
val canPathfindThroughAbove = aboveState.isPathfindable(PathComputationType.LAND)
if (!canPathfindThroughAbove) {
return false
}
}
return true
}
}
fun isSafeLandPosAround(world: ServerLevel, pos: BlockPos, mob: PathfinderMob): Boolean {
val minX = pos.x
val maxX = minX + floor(mob.bbWidth).toInt()
val minZ = pos.z
val maxZ = minZ + floor(mob.bbWidth).toInt()
val mutable = BlockPos.MutableBlockPos()
for (x in minX..maxX) {
for (z in minZ..maxZ) {
if (!isSafeLandPos(world, mutable.set(x, pos.y, z), mob)) {
return false
}
}
}
return true
}
}
@@ -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.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.GoToSleepTask
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class GoToSleepTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(GoToSleepTask.create(), PokemonEntity::class.java)
}
}
@@ -8,32 +8,29 @@
package com.cobblemon.mod.common.api.ai.config.task
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.api.ai.config.task.GoToHealingMachineTaskConfig.Companion.SELF_HEALING
import com.cobblemon.mod.common.api.ai.config.task.GoToHealingMachineTaskConfig.Companion.USE_HEALING_MACHINES
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.npc.ai.HealUsingHealingMachineTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class HealUsingHealingMachineTaskConfig : SingleTaskConfig {
val condition = booleanVariable(SELF_HEALING, USE_HEALING_MACHINES, true).asExpressible()
val horizontalUseRange: ExpressionOrEntityVariable = Either.left("2".asExpression())
val verticalUseRange: ExpressionOrEntityVariable = Either.left("1".asExpression())
override fun getVariables(entity: LivingEntity) = listOf(condition, horizontalUseRange, verticalUseRange).asVariables()
val condition: ExpressionLike = "true".asExpressionLike()
val horizontalUseRange = "3".asExpression()
val verticalUseRange = "2".asExpression()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
return HealUsingHealingMachineTask(horizontalUseRange.asExpression(), verticalUseRange.asExpression())
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
return HealUsingHealingMachineTask(horizontalUseRange, verticalUseRange)
}
}
@@ -8,41 +8,29 @@
package com.cobblemon.mod.common.api.ai.config.task
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.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.npc.ai.LookAtBattlingPokemonTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class LookAtBattlingPokemonTaskConfig : SingleTaskConfig {
companion object {
const val LOOK_AT_BATTLING_POKEMON = "look_at_battling_pokemon"
}
val condition = booleanVariable(SharedEntityVariables.BATTLING_CATEGORY, LOOK_AT_BATTLING_POKEMON, true).asExpressible()
val minDurationTicks: ExpressionOrEntityVariable = Either.left("40".asExpression())
val maxDurationTicks: ExpressionOrEntityVariable = Either.left("80".asExpression())
override fun getVariables(entity: LivingEntity) = listOf(
condition,
minDurationTicks,
maxDurationTicks
).asVariables()
val minDurationTicks = "40".asExpression()
val maxDurationTicks = "80".asExpression()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!resolveBooleanVariable(LOOK_AT_BATTLING_POKEMON)) return null
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
return LookAtBattlingPokemonTask.create(
minDurationTicks = minDurationTicks.resolveInt(),
maxDurationTicks = maxDurationTicks.resolveInt()
minDurationTicks = runtime.resolveInt(minDurationTicks),
maxDurationTicks = runtime.resolveInt(maxDurationTicks)
)
}
}
@@ -8,34 +8,29 @@
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.api.ai.config.task.SharedEntityVariables.SEE_DISTANCE
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.SetEntityLookTarget
class LookAtEntitiesTaskConfig : SingleTaskConfig {
companion object {
const val LOOK_AT_ENTITIES = "look_at_entities"
}
val condition = booleanVariable(SharedEntityVariables.LOOKING_CATEGORY, LOOK_AT_ENTITIES, true).asExpressible()
val maxDistance = numberVariable(SharedEntityVariables.LOOKING_CATEGORY, SEE_DISTANCE, 15).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
maxDistance
).asVariables()
val condition: ExpressionLike = "true".asExpressionLike()
val maxDistance = "15".asExpression()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
return SetEntityLookTarget.create(maxDistance.resolveFloat())
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
return SetEntityLookTarget.create(runtime.resolveFloat(maxDistance))
}
}
@@ -8,15 +8,13 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.entity.npc.ai.LookAtSpeakerTask
import net.minecraft.world.entity.LivingEntity
class LookAtSpeakerTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
) = LookAtSpeakerTask.create()
}
@@ -8,36 +8,33 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.Mob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.LookAtTargetSink
class LookAtTargetTaskConfig : SingleTaskConfig {
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
val minDurationTicks: ExpressionOrEntityVariable = Either.left("80".asExpression())
val maxDurationTicks: ExpressionOrEntityVariable = Either.left("160".asExpression())
override fun getVariables(entity: LivingEntity) = listOf(
condition,
minDurationTicks,
maxDurationTicks
).asVariables()
val condition: ExpressionLike = "true".asExpressionLike()
val minDurationTicks: Expression = "40".asExpression()
val maxDurationTicks: Expression = "80".asExpression()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
return WrapperLivingEntityTask(LookAtTargetSink(minDurationTicks.resolveInt(), maxDurationTicks.resolveInt()), Mob::class.java)
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
return WrapperLivingEntityTask(LookAtTargetSink(runtime.resolveInt(minDurationTicks), runtime.resolveInt(maxDurationTicks)), Mob::class.java)
}
}
@@ -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.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.LookAtTargetedBattlePokemonTask
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class LookAtTargetedBattlePokemonTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity> {
return WrapperLivingEntityTask(LookAtTargetedBattlePokemonTask.create(), PokemonEntity::class.java)
}
}
@@ -8,29 +8,18 @@
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.api.ai.config.task.SharedEntityVariables.LOOKING_CATEGORY
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.entity.ai.LookInDirectionTask
import com.cobblemon.mod.common.util.asExpression
import net.minecraft.world.entity.LivingEntity
class LookInDirectionTaskConfig : SingleTaskConfig {
var condition = booleanVariable(LOOKING_CATEGORY, "locked_rotation", true).asExpressible()
var yaw = numberVariable(LOOKING_CATEGORY, "locked_yaw", 0F).asExpressible()
var pitch = numberVariable(LOOKING_CATEGORY, "locked_pitch", 0F).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
yaw,
pitch
).asVariables()
var yaw: Expression = "0".asExpression()
var pitch: Expression = "0".asExpression()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
) = LookInDirectionTask(
shouldLock = condition.asExpression(),
yaw = yaw.asExpression(),
pitch = pitch.asExpression()
)
brainConfigurationContext: BrainConfigurationContext
) = LookInDirectionTask(yaw, pitch)
}
@@ -8,35 +8,32 @@
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.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.npc.ai.MeleeAttackTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class MeleeAttackTaskConfig : SingleTaskConfig {
val condition = booleanVariable(SharedEntityVariables.ATTACKING_CATEGORY, "attacks_melee", true).asExpressible()
val range = numberVariable(SharedEntityVariables.ATTACKING_CATEGORY, "melee_range", 1.5F).asExpressible()
val cooldownTicks = numberVariable(SharedEntityVariables.ATTACKING_CATEGORY, "melee_cooldown", 30).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
range,
cooldownTicks
).asVariables()
val condition = "true".asExpressionLike()
val range = "1.5".asExpression()
val cooldownTicks = "30".asExpression()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
return MeleeAttackTask.create(
range = range.asExpression(),
cooldownTicks = cooldownTicks.asExpression()
range = range,
cooldownTicks = cooldownTicks
)
}
}
@@ -8,28 +8,20 @@
package com.cobblemon.mod.common.api.ai.config.task
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.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.entity.ai.MoveToAttackTargetTask
import com.cobblemon.mod.common.util.asExpression
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
class MoveToAttackTargetTaskConfig : SingleTaskConfig {
val speedMultiplier = numberVariable(SharedEntityVariables.ATTACKING_CATEGORY, "attacking_movement_speed", 0.5).asExpressible()
val closeEnoughDistance: ExpressionOrEntityVariable = Either.left("1".asExpression())
override fun getVariables(entity: LivingEntity) = listOf(
speedMultiplier,
closeEnoughDistance
).asVariables()
val speedMultiplier = "0.5".asExpression()
val closeEnoughDistance = "1".asExpression()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
) = MoveToAttackTargetTask.create(
speedMultiplier = speedMultiplier.asExpression(),
closeEnoughDistance = closeEnoughDistance.asExpression()
speedMultiplier = speedMultiplier,
closeEnoughDistance = closeEnoughDistance
)
}
@@ -1,52 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
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.MoveToOwnerTask
import com.cobblemon.mod.common.util.asExpression
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class MoveToOwnerTaskConfig : SingleTaskConfig {
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
var completionRange: ExpressionOrEntityVariable = Either.left("4.0".asExpression())
var speedMultiplier: ExpressionOrEntityVariable = Either.left("0.4".asExpression())
var teleportDistance: ExpressionOrEntityVariable = Either.left("24.0".asExpression())
var maxDistance: ExpressionOrEntityVariable = Either.left("14.0".asExpression())
override fun getVariables(entity: LivingEntity) = listOf(
condition,
completionRange,
speedMultiplier,
teleportDistance,
maxDistance
).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(
MoveToOwnerTask.create(
condition = condition.asExpression(),
completionRange = completionRange.asExpression(),
speedMultiplier = speedMultiplier.asExpression(),
teleportDistance = teleportDistance.asExpression(),
maxDistance = maxDistance.asExpression()
),
PokemonEntity::class.java
)
}
}
@@ -8,15 +8,17 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.toDF
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.DoNothing
import net.minecraft.world.entity.ai.behavior.RunOne
/**
@@ -32,17 +34,15 @@ import net.minecraft.world.entity.ai.behavior.RunOne
class OneOfTaskConfig : SingleTaskConfig {
class OneOfTaskOption {
val weight: Int = 1
val task: TaskConfig = SingleTaskConfig.nothing()
val task: TaskConfig = SingleTaskConfig { _, _ -> DoNothing(0, 1) }
}
val condition: ExpressionLike = "true".asExpressionLike()
val options = mutableListOf<OneOfTaskOption>()
override fun getVariables(entity: LivingEntity) = options.flatMap { it.task.getVariables(entity) }
override fun createTask(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
override fun createTask(entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
return RunOne(options.map { it.task.createTasks(entity, behaviourConfigurationContext).first() toDF it.weight })
return RunOne(options.map { it.task.createTasks(entity, brainConfigurationContext).first() toDF it.weight })
}
}
@@ -1,42 +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.npc.configuration.MoLangConfigVariable
import net.minecraft.core.Direction
import net.minecraft.world.entity.LivingEntity
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.phys.Vec3
class PointToSpawnTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): BehaviorControl<in LivingEntity>? {
return BehaviorBuilder.create {
it.group(
it.registered(MemoryModuleType.LOOK_TARGET),
it.absent(MemoryModuleType.WALK_TARGET)
).apply(it) { lookTargetMemory, _ ->
Trigger { world, entity, _ ->
val lookTarget = entity.brain.getMemory(MemoryModuleType.LOOK_TARGET).orElse(null)
if (lookTarget == null) {
lookTargetMemory.set(BlockPosTracker(Vec3.atCenterOf(entity.level().sharedSpawnPos).with(Direction.Axis.Y, entity.eyeY)))
return@Trigger true
} else {
return@Trigger false
}
}
}
}
}
}
@@ -183,17 +183,4 @@ the script to avoid lagging the game.
### look_in_direction
The 'look_in_direction' type creates a task that will make the entity look in a specific direction at all times where they aren't being told to look elsewhere.
- `yaw`: A MoLang expression that determines the yaw angle (degrees) of the entity. Defaults to `0`.
- `pitch`: A MoLang expression that determines the pitch angle (degrees) of the entity. Defaults to `0`.
### move_to_owner
The 'move_to_owner' type creates a task that will make the entity move to its owner. This will only work for entities that
can be owned, like Pokémon.
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
- `completionRange`: A MoLang expression that determines the distance from the owner that the entity needs to be for the path to be considered complete. Defaults to 4.
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for moving to the owner. Defaults to 0.4 which is slightly above the normal walk speed.
- `teleportDistance`: A MoLang expression that determines the distance from the owner the entity must be before it resorts to teleporting. A value of -1 will cause it to never teleport. Defaults to 24.
- `maxDistance`: A MoLang expression that determines the maximum distance from the owner that the entity can be to move to them. Defaults to 14.
### point_to_spawn
The 'point_to_spawn' type creates a task that will make the entity point to the spawn point of the world whenever they
are not moving and are idling. This is basically only here for Nosepass.
- `pitch`: A MoLang expression that determines the pitch angle (degrees) of the entity. Defaults to `0`.
@@ -8,15 +8,17 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.weightedSelection
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.DoNothing
/**
* Randomly chooses one of the possible tasks to add to the brain. This differs from [OneOfTaskConfig] in that
@@ -32,21 +34,19 @@ import net.minecraft.world.entity.ai.behavior.BehaviorControl
class RandomTaskConfig : TaskConfig {
class RandomTaskChoice {
val weight = 1.0
val task: TaskConfig = SingleTaskConfig.nothing()
val task: TaskConfig = SingleTaskConfig { _, _ -> DoNothing(0, 1) }
}
val condition: ExpressionLike = "true".asExpressionLike()
val choices = mutableListOf<RandomTaskChoice>()
override fun getVariables(entity: LivingEntity) = choices.flatMap { it.task.getVariables(entity) }
override fun createTasks(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): List<BehaviorControl<in LivingEntity>> {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return emptyList()
val task = choices.weightedSelection { it.weight }?.task ?: throw IllegalStateException("No tasks to choose from in random_task config")
return task.createTasks(entity, behaviourConfigurationContext)
return task.createTasks(entity, brainConfigurationContext)
}
}
@@ -9,12 +9,11 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
@@ -32,28 +31,18 @@ import net.minecraft.world.entity.ai.memory.MemoryModuleType
* @since December 2nd, 2024
*/
class RunScript : SingleTaskConfig {
companion object {
const val SCRIPT_CATEGORY = "script"
}
val script = stringVariable(SCRIPT_CATEGORY, "script", "cobblemon:dummy_script").asExpressible()
val variables = mutableListOf<MoLangConfigVariable>()
override fun getVariables(entity: LivingEntity) = listOf(script).asVariables() + variables
val script = cobblemonResource("dummy")
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? = BehaviorBuilder.create {
it.group(
it.registered(MemoryModuleType.LOOK_TARGET) // I think I need to have at least something here?
).apply(it) { _ ->
Trigger { world, entity, _ ->
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
return@Trigger CobblemonScripts.run(
identifier = script.resolveString().asIdentifierDefaultingNamespace(),
runtime = runtime
) == DoubleValue.ONE
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: entity.asMostSpecificMoLangValue())
return@Trigger CobblemonScripts.run(script, runtime) == DoubleValue.ONE
}
}
}
@@ -1,22 +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
object SharedEntityVariables {
const val MOVEMENT_CATEGORY = "movement"
const val FEAR_CATEGORY = "fear"
const val ATTACKING_CATEGORY = "attacking"
const val BATTLING_CATEGORY = "battling"
const val LOOKING_CATEGORY = "looking"
const val WALK_SPEED = "walk_speed"
const val FLEE_SPEED_MULTIPLIER = "flee_speed_multiplier"
const val FLEE_DESIRED_DISTANCE = "flee_desired_distance"
const val SEE_DISTANCE = "see_distance"
}
@@ -8,31 +8,17 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.DoNothing
interface SingleTaskConfig : TaskConfig {
companion object {
fun nothing() = object : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return DoNothing(0, 1)
}
}
}
fun interface SingleTaskConfig : TaskConfig {
override fun createTasks(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): List<BehaviorControl<in LivingEntity>> {
return createTask(entity, behaviourConfigurationContext)?.let { listOf(it) } ?: emptyList()
return createTask(entity, brainConfigurationContext)?.let { listOf(it) } ?: emptyList()
}
fun createTask(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): BehaviorControl<in LivingEntity>?
fun createTask(entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext): BehaviorControl<in LivingEntity>?
}
@@ -1,52 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import net.minecraft.tags.BlockTags
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.TamableAnimal
import net.minecraft.world.entity.ai.behavior.BehaviorControl
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
class SleepIfOnTrainerBedTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> = emptyList()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity> {
return BehaviorBuilder.create {
it.group(
it.registered(MemoryModuleType.WALK_TARGET),
it.absent(CobblemonMemories.POKEMON_SLEEPING)
).apply(it) { walkTarget, pokemonSleeping ->
Trigger { world, entity, _ ->
if (entity !is TamableAnimal) {
return@Trigger false
}
val owner = entity.owner ?: return@Trigger false
// If it's no longer moving and is generally within range, cancel any outstanding movement targeting and sleep.
if (!entity.moveControl.hasWanted() && owner.distanceTo(entity) < 1.5 && owner.isSleeping && entity.blockStateOn.`is`(BlockTags.BEDS)) {
pokemonSleeping.set(true)
walkTarget.erase()
entity.onPathfindingDone()
return@Trigger true
}
return@Trigger false
}
}
}
}
}
@@ -8,30 +8,31 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.ai.StayAfloatTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class StayAfloatTaskConfig : SingleTaskConfig {
val condition = booleanVariable(SharedEntityVariables.MOVEMENT_CATEGORY, "can_float", true).asExpressible()
val chance = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, "float_chance", 0.8F).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
chance
).asVariables()
override fun createTask(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): BehaviorControl<LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
val condition: ExpressionLike = "true".asExpressionLike()
val chance: Expression = "0.01".asExpression()
override fun createTask(entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext): BehaviorControl<LivingEntity>? {
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
val chance = runtime.resolveFloat(chance)
return WrapperLivingEntityTask(
StayAfloatTask(chance.resolveFloat()),
StayAfloatTask(chance),
PathfinderMob::class.java
)
}
@@ -8,25 +8,26 @@
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.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.StopBeingAngryIfTargetDead
class StopBeingAngryIfAttackerDeadTaskConfig : SingleTaskConfig {
val condition = booleanVariable(SharedEntityVariables.ATTACKING_CATEGORY, "stop_being_angry_if_attacker_dead", true).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(condition).asVariables()
val condition: ExpressionLike = "true".asExpressionLike()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
return StopBeingAngryIfTargetDead.create()
}
}
@@ -9,17 +9,15 @@
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.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
class SwitchFromActionEffectTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
) = BehaviorBuilder.create {
it.group(
it.absent(CobblemonMemories.ACTIVE_ACTION_EFFECT)
@@ -9,17 +9,15 @@
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.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
class SwitchFromChattingTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
) = BehaviorBuilder.create {
it.group(
it.absent(CobblemonMemories.DIALOGUES)
@@ -8,18 +8,16 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import net.minecraft.world.entity.LivingEntity
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
class SwitchFromFightTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
) = BehaviorBuilder.create {
it.group(
it.absent(MemoryModuleType.ATTACK_TARGET)
@@ -9,18 +9,16 @@
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.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
class SwitchFromNPCBattleTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
return BehaviorBuilder.create {
it.group(it.absent(CobblemonMemories.NPC_BATTLING)).apply(it) { _ ->
@@ -1,34 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
class SwitchFromPokemonBattleTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return BehaviorBuilder.create {
it.group(it.absent(CobblemonMemories.POKEMON_BATTLE)).apply(it) { _ ->
Trigger { level, entity, _ ->
entity.brain.updateActivityFromSchedule(level.dayTime, level.gameTime)
true
}
}
}
}
}
@@ -1,62 +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.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
class SwitchFromSleepOnTrainerBedTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
// Why'd you give them this task then, dipshit?
if (entity !is PokemonEntity) {
return null
}
return BehaviorBuilder.create {
it.group(
it.registered(CobblemonMemories.POKEMON_SLEEPING)
).apply(it) { pokemonSleeping ->
Trigger { world, entity, _ ->
entity as PokemonEntity
// If they're owner is gone then we can consider that a reason to wake up, dogs do this too
val owner = entity.owner
val isCurrentlySleeping = entity.brain.getMemory(CobblemonMemories.POKEMON_SLEEPING).orElse(false)
// If we should move back to regular behaviours...
if (owner == null || !owner.isSleeping) {
// If it's willingly sleeping then it can lose the sleeping memory
if (isCurrentlySleeping && entity.pokemon.status?.status != Statuses.SLEEP) {
pokemonSleeping.erase()
return@Trigger false
}
// Go back to your usual behaviours
entity.brain.useDefaultActivity()
return@Trigger false
}
return@Trigger true
}
}
}
}
}
@@ -10,18 +10,16 @@ package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.CobblemonActivities
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
class SwitchToActionEffectTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
return BehaviorBuilder.create {
it.group(
@@ -10,18 +10,16 @@ package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.CobblemonActivities
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import net.minecraft.world.entity.LivingEntity
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
class SwitchToChattingTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
) = BehaviorBuilder.create {
it.group(
it.present(CobblemonMemories.DIALOGUES),
@@ -8,9 +8,11 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.Difficulty
import net.minecraft.world.entity.LivingEntity
@@ -20,21 +22,20 @@ import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.schedule.Activity
class SwitchToFightTaskConfig : SingleTaskConfig {
val condition = "true".asExpressionLike()
val activity = Activity.FIGHT
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
) = BehaviorBuilder.create {
it.group(
it.present(MemoryModuleType.ATTACK_TARGET)
).apply(it) { _ ->
Trigger { world, entity, _ ->
if (entity.commandSenderWorld.getCurrentDifficultyAt(entity.blockPosition()).difficulty == Difficulty.PEACEFUL) {
return@Trigger false
}
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return@Trigger false
if (entity.commandSenderWorld.getCurrentDifficultyAt(entity.blockPosition()).difficulty == Difficulty.PEACEFUL) return@Trigger false
entity.brain.setActiveActivityIfPossible(activity)
return@Trigger true
@@ -8,16 +8,14 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.entity.npc.ai.SwitchToBattleTask
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class SwitchToNPCBattleTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity> = SwitchToBattleTask.create()
}
@@ -8,37 +8,28 @@
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.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.util.withQueryValue
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.util.asExpression
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.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.schedule.Activity
class SwitchToPanicWhenHostilesNearbyTaskConfig : SingleTaskConfig {
var condition = booleanVariable(SharedEntityVariables.FEAR_CATEGORY, "panic_when_hostiles_nearby", true).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(condition).asVariables()
var condition = "true".asExpression()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
return BehaviorBuilder.create {
it.group(it.present(MemoryModuleType.NEAREST_HOSTILE))
.apply(it) { nearestHostile ->
Trigger { world, entity, _ ->
(entity as? PathfinderMob)?.navigation?.stop()
entity.brain.setActiveActivityIfPossible(Activity.PANIC)
return@Trigger true
}
brainConfigurationContext: BrainConfigurationContext
) = BehaviorBuilder.create {
it.group(it.present(MemoryModuleType.NEAREST_HOSTILE))
.apply(it) { nearestHostile ->
Trigger { world, entity, _ ->
(entity as? PathfinderMob)?.navigation?.stop()
entity.brain.setActiveActivityIfPossible(Activity.PANIC)
return@Trigger true
}
}
}
}
}
@@ -8,9 +8,11 @@
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.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
@@ -21,21 +23,16 @@ import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.schedule.Activity
class SwitchToPanicWhenHurtTaskConfig : SingleTaskConfig {
var condition = booleanVariable(SharedEntityVariables.FEAR_CATEGORY, "panic_when_hurt", true).asExpressible()
var includePassiveDamage = booleanVariable(SharedEntityVariables.FEAR_CATEGORY, "panic_on_passive_damage", false).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
includePassiveDamage
).asVariables()
var condition = "true".asExpression()
var includePassiveDamage = false
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
return if (includePassiveDamage.resolveBoolean()) {
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
return if (includePassiveDamage) {
BehaviorBuilder.create {
it.group(it.present(MemoryModuleType.HURT_BY))
.apply(it) { _ ->
@@ -1,25 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.npc.ai.SwitchToBattleTask
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class SwitchToPokemonBattleTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity> {
return SwitchToBattleTask.createForPokemon()
}
}
@@ -1,65 +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.CobblemonActivities
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.level.block.BedBlock
class SwitchToSleepOnTrainerBedTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (entity !is PokemonEntity) {
return null
}
if (entity.pokemon.isWild()) {
return null
}
return BehaviorBuilder.create {
it.group(
it.absent(CobblemonMemories.POKEMON_BATTLE),
it.absent(CobblemonMemories.POKEMON_SLEEPING),
it.registered(MemoryModuleType.WALK_TARGET)
).apply(it) { _, _, walkTarget ->
Trigger { world, entity, _ ->
entity as PokemonEntity
val owner = entity.owner ?: return@Trigger false
if (!owner.isSleeping || owner.distanceTo(entity) > 12) {
return@Trigger false
}
val blockPos = owner.blockPosition()
val blockState = entity.level().getBlockState(blockPos)
val bedPos = blockState.getOptionalValue(BedBlock.FACING).orElse(null)
?.let { direction -> blockPos.relative(direction.opposite) }
?: BlockPos(blockPos)
walkTarget.set(WalkTarget(bedPos.center, 0.35F, 0))
entity.brain.setActiveActivityIfPossible(CobblemonActivities.POKEMON_SLEEP_ON_TRAINER_BED)
return@Trigger true
}
}
}
}
}
@@ -8,28 +8,9 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.bedrockk.molang.Expression
import com.bedrockk.molang.ast.NumberExpression
import com.bedrockk.molang.ast.StringExpression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.resolveString
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
@@ -42,116 +23,51 @@ import net.minecraft.world.entity.ai.behavior.BehaviorControl
* @author Hiroku
* @since October 14th, 2024
*/
interface TaskConfig {
fun interface TaskConfig {
companion object {
val types = mutableMapOf<ResourceLocation, Class<out TaskConfig>>(
cobblemonResource("one_of") to OneOfTaskConfig::class.java,
cobblemonResource("wander") to WanderTaskConfig::class.java,
cobblemonResource("water_wander") to WaterWanderTaskConfig::class.java,
cobblemonResource("air_wander") to AirWanderTaskConfig::class.java,
cobblemonResource("look_at_target") to LookAtTargetTaskConfig::class.java,
cobblemonResource("follow_walk_target") to FollowWalkTargetTaskConfig::class.java,
cobblemonResource("random") to RandomTaskConfig::class.java,
cobblemonResource("stay_afloat") to StayAfloatTaskConfig::class.java,
cobblemonResource("look_at_entities") to LookAtEntitiesTaskConfig::class.java,
cobblemonResource("do_nothing") to DoNothingTaskConfig::class.java,
cobblemonResource("get_angry_at_attacker") to GetAngryAtAttackerTaskConfig::class.java,
cobblemonResource("stop_being_angry_if_attacker_dead") to StopBeingAngryIfAttackerDeadTaskConfig::class.java,
cobblemonResource("switch_npc_to_battle") to SwitchToNPCBattleTaskConfig::class.java,
cobblemonResource("look_at_battling_pokemon") to LookAtBattlingPokemonTaskConfig::class.java,
cobblemonResource("switch_npc_from_battle") to SwitchFromNPCBattleTaskConfig::class.java,
cobblemonResource("switch_pokemon_to_battle") to SwitchToPokemonBattleTaskConfig::class.java,
cobblemonResource("look_at_targeted_battle_pokemon") to LookAtTargetedBattlePokemonTaskConfig::class.java,
cobblemonResource("switch_pokemon_from_battle") to SwitchFromPokemonBattleTaskConfig::class.java,
cobblemonResource("go_to_healing_machine") to GoToHealingMachineTaskConfig::class.java,
cobblemonResource("heal_using_healing_machine") to HealUsingHealingMachineTaskConfig::class.java,
cobblemonResource("all_of") to AllOfTaskConfig::class.java,
cobblemonResource("attack_angry_at") to AttackAngryAtTaskConfig::class.java,
cobblemonResource("move_to_attack_target") to MoveToAttackTargetTaskConfig::class.java,
cobblemonResource("melee_attack") to MeleeAttackTaskConfig::class.java,
cobblemonResource("switch_from_fight") to SwitchFromFightTaskConfig::class.java,
cobblemonResource("switch_to_fight") to SwitchToFightTaskConfig::class.java,
cobblemonResource("switch_to_chatting") to SwitchToChattingTaskConfig::class.java,
cobblemonResource("switch_from_chatting") to SwitchFromChattingTaskConfig::class.java,
cobblemonResource("look_at_speaker") to LookAtSpeakerTaskConfig::class.java,
cobblemonResource("switch_to_action_effect") to SwitchToActionEffectTaskConfig::class.java,
cobblemonResource("switch_from_action_effect") to SwitchFromActionEffectTaskConfig::class.java,
cobblemonResource("exit_battle_when_hurt") to ExitBattleWhenHurtTaskConfig::class.java,
cobblemonResource("switch_to_panic_when_hurt") to SwitchToPanicWhenHurtTaskConfig::class.java,
cobblemonResource("switch_to_panic_when_hostiles_nearby") to SwitchToPanicWhenHostilesNearbyTaskConfig::class.java,
cobblemonResource("calm_down") to CalmDownTaskConfig::class.java,
cobblemonResource("flee_attacker") to FleeAttackerTaskConfig::class.java,
cobblemonResource("flee_nearest_hostile") to FleeNearestHostileTaskConfig::class.java,
cobblemonResource("run_script") to RunScript::class.java,
cobblemonResource("look_in_direction") to LookInDirectionTaskConfig::class.java,
cobblemonResource("wake_up") to WakeUpTaskConfig::class.java,
cobblemonResource("go_to_sleep") to GoToSleepTaskConfig::class.java,
cobblemonResource("find_resting_place") to FindRestingPlaceTaskConfig::class.java,
cobblemonResource("move_to_owner") to MoveToOwnerTaskConfig::class.java,
cobblemonResource("switch_to_sleep_on_trainer_bed") to SwitchToSleepOnTrainerBedTaskConfig::class.java,
cobblemonResource("switch_from_sleep_on_trainer_bed") to SwitchFromSleepOnTrainerBedTaskConfig::class.java,
cobblemonResource("sleep_if_on_trainer_bed") to SleepIfOnTrainerBedTaskConfig::class.java,
cobblemonResource("point_to_spawn") to PointToSpawnTaskConfig::class.java,
cobblemonResource("eat_grass") to EatGrassTaskConfig::class.java,
cobblemonResource("find_air") to FindAirTaskConfig::class.java,
cobblemonResource("go_to_land") to GoToLandTaskConfig::class.java
val types = mutableMapOf<String, Class<out TaskConfig>>(
"one_of" to OneOfTaskConfig::class.java,
"wander" to WanderTaskConfig::class.java,
"look_at_target" to LookAtTargetTaskConfig::class.java,
"follow_walk_target" to FollowWalkTargetTaskConfig::class.java,
"random" to RandomTaskConfig::class.java,
"stay_afloat" to StayAfloatTaskConfig::class.java,
"look_at_entities" to LookAtEntitiesTaskConfig::class.java,
"do_nothing" to DoNothingTaskConfig::class.java,
"get_angry_at_attacker" to GetAngryAtAttackerTaskConfig::class.java,
"stop_being_angry_if_attacker_dead" to StopBeingAngryIfAttackerDeadTaskConfig::class.java,
"switch_npc_to_battle" to SwitchToNPCBattleTaskConfig::class.java,
"look_at_battling_pokemon" to LookAtBattlingPokemonTaskConfig::class.java,
"switch_npc_from_battle" to SwitchFromNPCBattleTaskConfig::class.java,
"go_to_healing_machine" to GoToHealingMachineTaskConfig::class.java,
"heal_using_healing_machine" to HealUsingHealingMachineTaskConfig::class.java,
"all_of" to AllOfTaskConfig::class.java,
"attack_angry_at" to AttackAngryAtTaskConfig::class.java,
"move_to_attack_target" to MoveToAttackTargetTaskConfig::class.java,
"melee_attack" to MeleeAttackTaskConfig::class.java,
"switch_from_fight" to SwitchFromFightTaskConfig::class.java,
"switch_to_fight" to SwitchToFightTaskConfig::class.java,
"switch_to_chatting" to SwitchToChattingTaskConfig::class.java,
"switch_from_chatting" to SwitchFromChattingTaskConfig::class.java,
"look_at_speaker" to LookAtSpeakerTaskConfig::class.java,
"switch_to_action_effect" to SwitchToActionEffectTaskConfig::class.java,
"switch_from_action_effect" to SwitchFromActionEffectTaskConfig::class.java,
"exit_battle_when_hurt" to ExitBattleWhenHurtTaskConfig::class.java,
"switch_to_panic_when_hurt" to SwitchToPanicWhenHurtTaskConfig::class.java,
"switch_to_panic_when_hostiles_nearby" to SwitchToPanicWhenHostilesNearbyTaskConfig::class.java,
"calm_down" to CalmDownTaskConfig::class.java,
"flee_attacker" to FleeAttackerTaskConfig::class.java,
"flee_nearest_hostile" to FleeNearestHostileTaskConfig::class.java,
"run_script" to RunScript::class.java,
"look_in_direction" to LookInDirectionTaskConfig::class.java,
)
val runtime = MoLangRuntime().setup()
}
val runtime: MoLangRuntime
get() = Companion.runtime
fun checkCondition(entity: LivingEntity, expressionOrEntityVariable: ExpressionOrEntityVariable): Boolean {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
return expressionOrEntityVariable.resolveBoolean()
}
fun ExpressionOrEntityVariable.asSimplifiedExpression(entity: LivingEntity): Expression {
return map(
{ it },
{
if (entity is MoLangScriptingEntity) {
val variable = entity.config.map[it.variableName]
if (variable is DoubleValue) {
return@map NumberExpression(variable.value)
} else if (variable is StringValue) {
return@map StringExpression(variable)
}
}
return@map "q.entity.config.${it.variableName}".asExpression()
}
)
}
fun ExpressionOrEntityVariable.asExpression() = map({ it }, { "q.entity.config.${it.variableName}".asExpression() })
fun ExpressionOrEntityVariable.resolveString() = runtime.resolveString(asExpression())
fun ExpressionOrEntityVariable.resolveBoolean() = runtime.resolveBoolean(asExpression())
fun ExpressionOrEntityVariable.resolveInt() = runtime.resolveInt(asExpression())
fun ExpressionOrEntityVariable.resolveDouble() = runtime.resolveDouble(asExpression())
fun ExpressionOrEntityVariable.resolveFloat() = runtime.resolveFloat(asExpression())
private fun variable(category: String, name: String, type: MoLangConfigVariable.MoLangVariableType, default: String) = MoLangConfigVariable(
variableName = name,
category = lang("entity.variable.category.$category"),
displayName = lang("entity.variable.$name.name"),
description = lang("entity.variable.$name.desc"),
type = type,
defaultValue = default
)
fun stringVariable(category: String, name: String, default: String) = variable(category = category, name = name, type = MoLangConfigVariable.MoLangVariableType.TEXT, default = default)
fun numberVariable(category: String, name: String, default: Number) = variable(category = category, name = name, type = MoLangConfigVariable.MoLangVariableType.NUMBER, default = default.toString())
fun booleanVariable(category: String, name: String, default: Boolean) = variable(category = category, name = name, type = MoLangConfigVariable.MoLangVariableType.BOOLEAN, default = default.toString())
fun getVariableExpression(name: String) = "q.entity.config.$name".asExpression()
fun resolveStringVariable(name: String) = runtime.resolveString(getVariableExpression(name))
fun resolveBooleanVariable(name: String) = runtime.resolveBoolean(getVariableExpression(name))
fun resolveNumberVariable(name: String) = runtime.resolveDouble(getVariableExpression(name))
/** The variables that this task config uses. These are used to declare variables on the entity cleanly. */
fun getVariables(entity: LivingEntity): List<MoLangConfigVariable>
/** Given the entity in construction, returns a list of tasks. */
fun createTasks(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<BehaviorControl<in LivingEntity>>
fun createTasks(entity: LivingEntity, brainConfigurationContext: BrainConfigurationContext): List<BehaviorControl<in LivingEntity>>
}
@@ -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.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.WakeUpTask
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class WakeUpTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
return WrapperLivingEntityTask(WakeUpTask.create(), PokemonEntity::class.java)
}
}
@@ -8,137 +8,64 @@
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.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.ai.CobblemonWalkTarget
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.core.BlockPos
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.memory.WalkTarget
import net.minecraft.world.entity.ai.util.LandRandomPos
import net.minecraft.world.level.pathfinder.PathType
class WanderTaskConfig : SingleTaskConfig {
companion object {
const val WANDER = "wander" // Category
}
val condition = booleanVariable(WANDER, "wanders", true).asExpressible()
val wanderChance = numberVariable(WANDER, "wander_chance", 1/(20 * 6F)).asExpressible()
val horizontalRange = numberVariable(WANDER, "horizontal_wander_range", 10).asExpressible()
val verticalRange = numberVariable(WANDER, "vertical_wander_range", 5).asExpressible()
val speedMultiplier = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
override fun getVariables(entity: LivingEntity) = listOf(
condition,
wanderChance,
horizontalRange,
verticalRange,
speedMultiplier
).asVariables()
val condition: ExpressionLike = "true".asExpressionLike()
val shouldWander: ExpressionLike = "math.random(0, 20 * 8) <= 1".asExpressionLike()
val horizontalRange: Expression = "10".asExpression()
val verticalRange: Expression = "5".asExpression()
val speedMultiplier: Expression = "0.35".asExpression()
val completionRange: Expression = "1".asExpression()
val lookTargetYOffset: Expression = "1.5".asExpression()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
val wanderChanceExpression = wanderChance.asSimplifiedExpression(entity)
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(condition)) return null
return BehaviorBuilder.create {
it.group(
it.absent(MemoryModuleType.WALK_TARGET),
it.registered(MemoryModuleType.LOOK_TARGET),
it.absent(CobblemonMemories.PATH_COOLDOWN)
).apply(it) { walkTarget, lookTarget, pathCooldown ->
it.registered(MemoryModuleType.LOOK_TARGET)
).apply(it) { walkTarget, lookTarget ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || entity.isUnderWater) {
if (entity !is PathfinderMob) {
return@Trigger false
}
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val wanderChance = runtime.resolveFloat(wanderChanceExpression)
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance) return@Trigger false
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
if (!runtime.resolveBoolean(shouldWander)) return@Trigger false
pathCooldown.setWithExpiry(true, 40L)
// val targetVec = getLandTarget(entity) ?: return@Trigger true
val targetVec = LandRandomPos.getPos(entity, horizontalRange.resolveInt(), verticalRange.resolveInt()) ?: return@Trigger false
val pos = BlockPos.containing(targetVec)
walkTarget.set(CobblemonWalkTarget(pos, speedMultiplier.resolveFloat(), 0, { it !in listOf(PathType.WATER, PathType.WATER_BORDER) }))
lookTarget.erase()
val targetVec = LandRandomPos.getPos(entity, runtime.resolveInt(horizontalRange), runtime.resolveInt(verticalRange)) ?: return@Trigger false
walkTarget.set(WalkTarget(targetVec, runtime.resolveFloat(speedMultiplier), runtime.resolveInt(completionRange)))
lookTarget.set(BlockPosTracker(targetVec.add(0.0, runtime.resolveDouble(lookTargetYOffset), 0.0)))
return@Trigger true
}
}
}
}
/*
* This method is from the old wander goal. It is very verbose but reliably finds decent land wander targets. Its only advantage over
* the LandRandomPos method (with our CobblemonWalkTarget) is that it doesn't lead entities in the direction of water.
*/
// fun getLandTarget(entity: PathfinderMob): Vec3? {
// val roamDistanceCondition: (BlockPos) -> Boolean = if (entity is PokemonEntity) ({ entity.tethering?.canRoamTo(it) != false }) else ({ true })
// val iterable: Iterable<BlockPos> = BlockPos.randomBetweenClosed(entity.random, 64, entity.blockX - 10, entity.blockY, entity.blockZ - 10, entity.blockX + 10, entity.blockY, entity.blockZ + 10)
// val condition: (BlockState, BlockPos) -> Boolean = { _, pos -> entity.canFit(pos) && roamDistanceCondition(pos) }
// val iterator = iterable.iterator()
// position@
// while (iterator.hasNext()) {
// val pos = iterator.next().mutable()
// var blockState = entity.level().getBlockState(pos)
//
// val maxSteps = 16
// var steps = 0
// var good = false
// if (!blockState.isSolid && !blockState.liquid()) {
// pos.move(0, -1, 0)
// var previousWasAir = true
// while (steps++ < maxSteps && pos.y > entity.level().minBuildHeight) {
// if (pos.y <= entity.level().minBuildHeight) {
// continue@position
// }
// blockState = entity.level().getBlockState(pos)
// if (blockState.isSolid && !blockState.`is`(BlockTags.LEAVES) && previousWasAir) {
// pos.move(0, 1, 0)
// blockState = entity.level().getBlockState(pos)
// good = true
// break
// } else {
// previousWasAir = blockState.isAir
// }
// pos.move(0, -1, 0)
// }
// } else {
// var previousWasSolid = blockState.isSolid && !blockState.`is`(BlockTags.LEAVES)
// pos.move(0, 1, 0)
// while (steps++ < maxSteps) {
// if (pos.y >= entity.level().maxBuildHeight) {
// continue@position
// }
// blockState = entity.level().getBlockState(pos)
// if (blockState.isAir && previousWasSolid) {
// good = true
// break
// }
// previousWasSolid = blockState.isSolid && !blockState.`is`(BlockTags.LEAVES)
// pos.move(0, 1, 0)
// }
// }
//
// if (good && condition(blockState, pos)) {
// return pos.toVec3d()
// }
// }
//
// return null
// }
}
@@ -1,75 +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.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.ai.config.task.WanderTaskConfig.Companion.WANDER
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
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.BehaviorUtils
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.memory.WalkTarget
class WaterWanderTaskConfig : SingleTaskConfig {
val condition = booleanVariable(WANDER, "water_wanders", true).asExpressible()
val wanderChance = numberVariable(WANDER, "water_wander_chance", 1/(20 * 3F)).asExpressible()
val speedMultiplier = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
val horizontalRange: ExpressionOrEntityVariable = Either.left("10.0".asExpression())
val verticalRange: ExpressionOrEntityVariable = Either.left("3.0".asExpression())
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return listOf(condition, wanderChance, speedMultiplier).asVariables()
}
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
val wanderChanceExpression = wanderChance.asSimplifiedExpression(entity)
return BehaviorBuilder.create {
it.group(
it.absent(MemoryModuleType.WALK_TARGET),
it.registered(MemoryModuleType.LOOK_TARGET)
).apply(it) { walkTarget, lookTarget ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || !entity.isInWater) {
return@Trigger false
}
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val wanderChance = runtime.resolveFloat(wanderChanceExpression)
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance) {
return@Trigger false
}
val target = BehaviorUtils.getRandomSwimmablePos(entity, horizontalRange.resolveInt(), verticalRange.resolveInt()) ?: return@Trigger false
walkTarget.set(WalkTarget(target, speedMultiplier.resolveFloat(), 1))
lookTarget.set(BlockPosTracker(target))
return@Trigger true
}
}
}
}
}
@@ -8,10 +8,10 @@
package com.cobblemon.mod.common.api.data
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.util.endsWith
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.io.BufferedReader
import java.io.File
import java.util.concurrent.ExecutionException
import net.minecraft.resources.ResourceLocation
@@ -54,22 +54,17 @@ interface JsonDataRegistry<T> : DataRegistry {
resource.open().use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
data[resolvedIdentifier] = parse(reader, resolvedIdentifier)
try {
data[resolvedIdentifier] = gson.fromJson(reader, typeToken.type)
} catch (exception: Exception) {
throw ExecutionException("Error loading JSON for data: $identifier", exception)
}
}
}
}
reload(data)
}
fun parse(reader: BufferedReader, identifier: ResourceLocation): T {
return try {
gson.fromJson(reader, typeToken.type)
} catch (exception: Exception) {
throw ExecutionException("Error loading JSON for data: $identifier", exception)
}
}
/**
* Reloads this registry from the deserialized data.
*
@@ -1,37 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.dialogue
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.resources.ResourceLocation
/**
* Settings for the gibbering sound effect that plays gradually as dialogue appears.
*
* @author Hiroku, landonjw
* @since April 19th, 2025
*/
class DialogueGibber(
/** Whether text will gradually appear while the gibbering occurs. */
val graduallyShowText: Boolean = true,
/** Whether clicking will make the gibber skip to the end. */
val allowSkip: Boolean = true,
/** The space between gibbers in terms of characters. 'Holy cow' would play 2 sounds if step = 4. */
val step: Int = 4,
/** The time (in seconds) between gibbers. The smaller this is, the faster the text will progress. */
val interval: Double = 0.1,
val minPitch: Float = 0.9F,
val maxPitch: Float = 1.1F,
val minVolume: Float = 0.9F,
val maxVolume: Float = 1.0F,
/** The possible sounds to play when gibbering. */
val sounds: List<ResourceLocation> = listOf(
cobblemonResource("entity.npc.gibber.generic")
)
)
@@ -31,7 +31,6 @@ class DialoguePage(
var lines: MutableList<DialogueText> = mutableListOf(),
var input: DialogueInput = DialogueNoInput(),
var background: ResourceLocation? = null,
var gibber: DialogueGibber? = null,
var clientActions: MutableList<Expression> = mutableListOf(),
var escapeAction: DialogueAction? = null,
) {
@@ -43,7 +42,6 @@ class DialoguePage(
speaker: String? = null,
lines: Iterable<MutableComponent>,
input: DialogueInput = DialogueNoInput(),
gibber: DialogueGibber? = null,
background: ResourceLocation? = null,
clientActions: Iterable<Expression> = emptyList(),
/** The thing to do when the player presses ESC while on this page. If null, falls back to the same property on [Dialogue]. */
@@ -54,7 +52,6 @@ class DialoguePage(
speaker = speaker,
lines = lines.map { WrappedDialogueText(it) }.toMutableList(),
input = input,
gibber = gibber,
background = background,
clientActions = clientActions.toMutableList(),
escapeAction = escapeAction?.let { func -> FunctionDialogueAction { activeDialogue, _ -> func(activeDialogue) } }
@@ -19,16 +19,7 @@ import net.minecraft.network.chat.MutableComponent
*/
class DialogueSpeaker(
val name: DialogueText? = null,
val face: DialogueFaceProvider? = null,
val gibber: DialogueGibber? = null
val face: DialogueFaceProvider? = null
) {
fun of(
name: MutableComponent = "".text(),
face: DialogueFaceProvider? = null,
gibber: DialogueGibber? = null
) = DialogueSpeaker(
name = WrappedDialogueText(name),
face = face,
gibber = gibber
)
fun of(name: MutableComponent = "".text(), face: DialogueFaceProvider? = null) = DialogueSpeaker(WrappedDialogueText(name), face)
}

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