Compare commits

..

85 Commits

Author SHA1 Message Date
Auxilor
44924cdf83 MiniMessage safety checks 2022-03-04 11:00:36 +00:00
Auxilor
75363181e4 MiniMessage is an endless source of suffering 2022-03-04 10:56:27 +00:00
Auxilor
1fc9d66457 Even more minimessage pain 2022-03-04 10:50:02 +00:00
Auxilor
2fdbe0da28 More minimessage pain 2022-03-04 10:40:56 +00:00
Auxilor
a1922cb195 MiniMessage pain 2022-03-04 10:36:35 +00:00
Auxilor
725e950092 Updated MiniMessage and adventure-platform 2022-03-04 09:21:44 +00:00
Auxilor
bf123d3105 Fixed init order bug 2022-03-04 09:16:30 +00:00
Auxilor
dca952c10a Fixed deprecated adventure api usage 2022-03-04 09:11:10 +00:00
Auxilor
b2e5896885 Updated MiniMessage 2022-03-04 09:08:48 +00:00
Auxilor
38542dad5e Added utility methods to EntityGoals and TargetGoals 2022-03-04 09:07:21 +00:00
Auxilor
852a9c5fb1 Updated v1_18_R2 module, updated adventure 2022-03-04 09:02:12 +00:00
Auxilor
12aa0e349e Updated paper to 1.18.2 2022-03-04 08:53:09 +00:00
Auxilor
351f5d8c16 Updated KingdomsX 2022-03-03 17:29:43 +00:00
Auxilor
7e4b3075ed Added maxmimum size to recipes cache 2022-03-03 14:52:29 +00:00
Auxilor
cf0d725189 Improved removing entity goals 2022-03-03 14:50:26 +00:00
Auxilor
a9f33fb846 Refactoring delegated spellcasters 2022-03-03 14:19:18 +00:00
Auxilor
4a9ab7a0eb Added EntityGoalIllusionerBlindnessSpell and EntityGoalIllusionerMirrorSpell 2022-03-03 14:16:25 +00:00
Auxilor
3982ab99a6 Added some entity goals 2022-03-03 13:32:26 +00:00
Auxilor
1fb39e55d2 Removed redundant suppression 2022-03-03 11:58:25 +00:00
Auxilor
56d32b532d Move improvements to CustomGoal 2022-03-03 10:12:02 +00:00
Auxilor
0d0800dfb5 Improved CustomGoal 2022-03-03 10:07:40 +00:00
Auxilor
f32110687f Added missing nullability parameter 2022-03-03 09:47:36 +00:00
Auxilor
dbf39f3621 Added more helpful utility methods 2022-03-03 09:46:14 +00:00
Auxilor
9a2f0e3df8 Switched from com.willfp.libs.shaded to com.willfp.eco.libs 2022-03-03 08:31:47 +00:00
Auxilor
56fb9b40b9 Added Goal<T>, which EntityGoal<T> and TargetGoal<T> extend from, giving them a shared parent class 2022-03-02 20:17:06 +00:00
Auxilor
fbc7bb6f07 Tempt now uses TestableItem 2022-03-02 19:12:59 +00:00
Auxilor
2294c3758d Fixed ItemStack.mergeIfNeeded 2022-03-02 18:52:12 +00:00
Auxilor
c573da6e31 Suppressed warning 2022-03-02 18:51:07 +00:00
Auxilor
6362931e6b Stopped using percentages in goals 2022-03-02 18:48:44 +00:00
Auxilor
0be1f45edc Fixed refactoring bugs 2022-03-02 18:45:34 +00:00
Auxilor
9700aa7eac Added secondary constructors without predicates 2022-03-02 18:39:32 +00:00
Auxilor
75eaf58aa2 NMS Refactoring 2022-03-02 18:29:11 +00:00
Auxilor
63352989b1 Switched interact and hurt_by to use TestableEntity 2022-03-02 18:05:03 +00:00
Auxilor
dd5e0e3847 Switched class-based entity restrictions to TestableEntity where possible 2022-03-02 17:52:00 +00:00
Auxilor
4f009c6d57 EntityGoalAvoidEntity now functions using TestableEntity 2022-03-02 17:45:40 +00:00
Auxilor
09d4db816e Renaming goals, second pass 2022-03-02 16:22:18 +00:00
Auxilor
fb7799e931 Renaming goals, first pass 2022-03-02 16:01:16 +00:00
Auxilor
13a1965f03 Added EntityController#clearAllGoals 2022-03-02 15:06:53 +00:00
Auxilor
3abefb73f9 Added deserializers for all entity goals 2022-03-02 14:38:46 +00:00
Auxilor
a35f5bb405 Added deserializers for all target goals 2022-03-02 14:13:49 +00:00
Auxilor
88a7fb53a3 Added ConfigSerializer, ConfigDeserializer, made Testable extend Predicate 2022-03-02 13:48:05 +00:00
Auxilor
866ba8440d Refactored EntityController.of to EntityController.getFor 2022-03-02 13:10:39 +00:00
Auxilor
61896bbddd Commons refactoring 2022-03-02 12:53:21 +00:00
Auxilor
7a4891d4e3 Fixed refactoring bugs 2022-03-02 12:48:13 +00:00
Auxilor
dbd5cd341e Moved FastItemStack into commons 2022-03-02 12:42:41 +00:00
Auxilor
7df707a59d Updated goal javadoc 2022-03-02 12:33:35 +00:00
Auxilor
92dfa32d07 CustomGoal inheritance fixes 2022-03-02 12:22:37 +00:00
Auxilor
5cad1d31e3 Added v1_18_R2 Module (to be updated when papermc updates) 2022-03-02 12:18:41 +00:00
Auxilor
e4c0418fd4 Removed requirement system 2022-03-02 12:14:21 +00:00
Auxilor
54c74d0138 Added more goals, added shorthand goal syntax, added kotlin extension for entity controllers 2022-03-02 12:14:03 +00:00
Auxilor
6e21bdeb5b Updated/Added some goals 2022-03-02 12:03:28 +00:00
Auxilor
00439a1b04 Began refactoring NMS 2022-03-02 11:52:49 +00:00
Auxilor
87684abbbb Fixed NMS buildscripts some more 2022-03-02 11:28:16 +00:00
Auxilor
0cbf78733e Changed NMS Commons relocation 2022-03-02 11:24:37 +00:00
Auxilor
ecf0e7c356 NMS Buildscript changes 2022-03-02 11:22:49 +00:00
Auxilor
f20d9884b5 NMS Commons changes 2022-03-02 11:18:55 +00:00
Auxilor
61d713ad4d Moved NMS AI implementations into craftbukkit-less commons 2022-03-02 11:15:44 +00:00
Auxilor
ca5cce3f40 Fixed shadow relocations 2022-03-01 22:28:33 +00:00
Auxilor
1c8edac807 Finally fixed intersection generics 2022-03-01 22:26:47 +00:00
Auxilor
98b465f8f3 Take 3 at fixing generic issues 2022-03-01 22:20:12 +00:00
Auxilor
3c3e34eda3 Take 2 at fixing generic issues 2022-03-01 22:16:09 +00:00
Auxilor
02ad6f7810 Fixed issues with generics 2022-03-01 22:14:25 +00:00
Auxilor
5369ee7d42 Improved CustomGoal 2022-03-01 22:06:39 +00:00
Auxilor
c441e0761f Updated to 6.27.0 2022-03-01 21:10:42 +00:00
Auxilor
a02dd06c20 Added entity goals in for 1_18_R1, minor refactoring 2022-03-01 21:10:02 +00:00
Auxilor
957af5c5bf Continued work on entity controllers 2022-03-01 21:01:00 +00:00
Auxilor
2c51a6900f Added remaining entity goals 2022-03-01 20:40:45 +00:00
Auxilor
e09e9b4e8d Added more entity goals 2022-03-01 20:20:02 +00:00
Auxilor
02497d485b Added more entity goals 2022-03-01 20:04:04 +00:00
Auxilor
bd32f3bc8d Continued implementing entity goals 2022-03-01 19:58:05 +00:00
Auxilor
7176342e15 Continued goal implementation 2022-03-01 19:44:27 +00:00
Auxilor
32ef8d8c5c Began entity controller system 2022-03-01 18:04:27 +00:00
Auxilor
92f8787eb9 Updated to 6.26.3 2022-03-01 13:03:56 +00:00
Auxilor
5403f7a9ed Fixed multiple option display 2022-03-01 11:29:51 +00:00
Auxilor
2924d6f560 All options are now shown to players in crafting 2022-03-01 11:22:31 +00:00
Auxilor
36c0708c17 (Narrator voice) It wasn't 2022-03-01 10:56:54 +00:00
Auxilor
b9fe54e883 This isn't going to be the last commit, is it 2022-03-01 10:49:09 +00:00
Auxilor
5cc2f23547 Removed debug messages 2022-03-01 10:40:07 +00:00
Auxilor
30514ba780 Suffering 2022-03-01 10:39:24 +00:00
Auxilor
02e6c5e8f3 More working around how broken spigot is 2022-03-01 10:35:49 +00:00
Auxilor
fac97329aa Working around how broken spigot is 2022-03-01 10:31:19 +00:00
Auxilor
cdf5cc3abe Suppressed warnings 2022-03-01 10:25:43 +00:00
Auxilor
4cb630d201 Fixed crafting, deprecated now-unneeded API 2022-03-01 10:24:38 +00:00
Auxilor
79d6277d94 Updated to 6.26.2 2022-03-01 08:33:24 +00:00
Auxilor
1a90fa6707 Fixed ComplexInEco 2022-03-01 08:33:14 +00:00
178 changed files with 6033 additions and 812 deletions

View File

@@ -23,6 +23,7 @@ dependencies {
implementation(project(":eco-core:core-backend"))
implementation(project(path = ":eco-core:core-nms:v1_17_R1", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_18_R1", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_18_R2", configuration = "reobf"))
}
allprojects {
@@ -92,9 +93,9 @@ allprojects {
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2")
// Adventure
compileOnly("net.kyori:adventure-api:4.9.3")
compileOnly("net.kyori:adventure-text-serializer-gson:4.9.3")
compileOnly("net.kyori:adventure-text-serializer-legacy:4.9.3")
compileOnly("net.kyori:adventure-api:4.10.0")
compileOnly("net.kyori:adventure-text-serializer-gson:4.10.0")
compileOnly("net.kyori:adventure-text-serializer-legacy:4.10.0")
// Other
compileOnly("com.google.guava:guava:31.0.1-jre")
@@ -113,6 +114,7 @@ allprojects {
exclude(group = "org.spongepowered", module = "configurate-hocon")
exclude(group = "com.darkblade12", module = "particleeffect")
exclude(group = "com.github.cryptomorin", module = "XSeries")
exclude(group = "net.wesjd", module = "anvilgui")
}
configurations.testImplementation {
@@ -129,8 +131,8 @@ allprojects {
}
shadowJar {
relocate("org.bstats", "com.willfp.eco.shaded.bstats")
relocate("net.kyori.adventure.text.minimessage", "com.willfp.eco.shaded.minimessage")
relocate("org.bstats", "com.willfp.eco.libs.bstats")
relocate("redempt.crunch", "com.willfp.eco.libs.crunch")
}
compileJava {

View File

@@ -1,7 +1,6 @@
dependencies {
// Adventure
compileOnly 'net.kyori:adventure-platform-bukkit:4.0.0'
compileOnly 'net.kyori:adventure-text-minimessage:4.1.0-SNAPSHOT'
compileOnly 'net.kyori:adventure-platform-bukkit:4.1.0'
// Other
compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT'
@@ -33,4 +32,4 @@ publishing {
}
}
}
}
}

View File

@@ -5,6 +5,7 @@ import com.willfp.eco.core.config.wrapper.ConfigFactory;
import com.willfp.eco.core.data.ProfileHandler;
import com.willfp.eco.core.data.keys.KeyRegistry;
import com.willfp.eco.core.drops.DropQueueFactory;
import com.willfp.eco.core.entities.ai.EntityController;
import com.willfp.eco.core.events.EventManager;
import com.willfp.eco.core.extensions.ExtensionLoader;
import com.willfp.eco.core.factory.MetadataValueFactory;
@@ -20,6 +21,7 @@ import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Mob;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@@ -32,7 +34,6 @@ import java.util.logging.Logger;
* @see Eco#getHandler()
*/
@ApiStatus.Internal
@SuppressWarnings("removal")
public interface Handler {
/**
* Create a scheduler.
@@ -262,4 +263,23 @@ public interface Handler {
@NotNull
PluginProps getProps(@Nullable PluginProps existing,
@NotNull Class<? extends EcoPlugin> plugin);
/**
* Format a string with MiniMessage.
*
* @param message The message.
* @return The formatted string.
*/
@NotNull
String formatMiniMessage(@NotNull String message);
/**
* Create controlled entity from a mob.
*
* @param mob The mob.
* @param <T> The mob type.
* @return The controlled entity.
*/
@NotNull
<T extends Mob> EntityController<T> createEntityController(@NotNull T mob);
}

View File

@@ -0,0 +1,122 @@
package com.willfp.eco.core.entities.ai;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
/**
* Base interface for all custom goals.
* <p>
* Can be used both for entity goals and target goals.
*
* @param <T> The type of mob that this goal can be applied to.
*/
public abstract class CustomGoal<T extends Mob> implements EntityGoal<T>, TargetGoal<T> {
/**
* The flags for the goal.
*/
private final Set<GoalFlag> flags = EnumSet.noneOf(GoalFlag.class);
/**
* Initialize the goal with a mob.
* <p>
* This will be run before any implementation code, treat this as the constructor.
*
* @param mob The mob.
*/
public abstract void initialize(@NotNull T mob);
/**
* Get if the goal can be used.
* Will start the goal if this returns true.
*
* @return If the goal can be used.
*/
public abstract boolean canUse();
/**
* Tick the goal.
* <p>
* Runs ever tick as long as {@link this#canUse()} returns true.
* <p>
* Runs after {@link this#start()}.
*/
public void tick() {
// Override when needed.
}
/**
* Start the goal.
* <p>
* Runs once {@link this#canUse()} returns true.
*/
public void start() {
// Override when needed.
}
/**
* Stop the goal.
* <p>
* Runs once {@link this#canUse()} returns false.
*/
public void stop() {
// Override when needed.
}
/**
* Get if the goal can continue to be used.
*
* @return If the goal can continue to be used.
*/
public boolean canContinueToUse() {
return this.canUse();
}
/**
* Get if the goal is interruptable.
*
* @return If interruptable.
*/
public boolean isInterruptable() {
return true;
}
/**
* Get the goal flags.
*
* @return The flags.
*/
public EnumSet<GoalFlag> getFlags() {
return EnumSet.copyOf(this.flags);
}
/**
* Set the flags for the goal.
*
* @param flags The flags.
*/
public final void setFlags(@NotNull final GoalFlag... flags) {
this.setFlags(EnumSet.copyOf(List.of(flags)));
}
/**
* Set the flags for the goal.
*
* @param flags The flags.
*/
public void setFlags(@NotNull final EnumSet<GoalFlag> flags) {
this.flags.clear();
this.flags.addAll(flags);
}
@Override
public T addToEntity(@NotNull final T entity,
final int priority) {
throw new UnsupportedOperationException(
"Shorthand syntax is not supported for custom goals by default as they can be both entity and target goals."
);
}
}

View File

@@ -0,0 +1,106 @@
package com.willfp.eco.core.entities.ai;
import com.willfp.eco.core.Eco;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
/**
* An entity controller allows for adding targets and goals to entities.
*
* @param <T> The wrapped mob.
*/
public interface EntityController<T extends Mob> {
/**
* Add a target goal to the entity.
* <p>
* Mutates the instance.
*
* @param priority The priority.
* @param goal The goal.
* @return The entity controller.
*/
EntityController<T> addTargetGoal(int priority,
@NotNull TargetGoal<? super T> goal);
/**
* Remove all target goals from the entity.
* <p>
* Mutates the instance.
*
* @return The entity controller.
*/
EntityController<T> clearTargetGoals();
/**
* Remove a target goal from the entity.
* <p>
* Mutates the instance.
*
* @param goal The goal.
* @return The entity controller.
*/
EntityController<T> removeTargetGoal(@NotNull TargetGoal<? super T> goal);
/**
* Add an entity goal to the entity.
* <p>
* Mutates the instance.
*
* @param priority The priority.
* @param goal The goal.
* @return The entity controller.
*/
EntityController<T> addEntityGoal(int priority,
@NotNull EntityGoal<? super T> goal);
/**
* Remove an entity goal from the entity.
* <p>
* Mutates the instance.
*
* @param goal The goal.
* @return The entity controller.
*/
EntityController<T> removeEntityGoal(@NotNull EntityGoal<? super T> goal);
/**
* Remove all entity goals from the entity.
* <p>
* Mutates the instance.
*
* @return The entity controller.
*/
EntityController<T> clearEntityGoals();
/**
* Remove all goals from the entity.
* <p>
* Mutates the instance.
*
* @return The entity controller.
*/
default EntityController<T> clearAllGoals() {
this.clearTargetGoals();
return this.clearEntityGoals();
}
/**
* Get the mob back from the controlled entity.
* <p>
* Not required to apply changes, as the mob instance will be altered.
*
* @return The mob.
*/
T getEntity();
/**
* Create an entity controller for an entity in order to modify targets and goals.
*
* @param entity The entity.
* @param <T> The mob type.
* @return The entity controller.
*/
static <T extends Mob> EntityController<T> getFor(@NotNull final T entity) {
return Eco.getHandler().createEntityController(entity);
}
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.core.entities.ai;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
/**
* A goal for entity AI.
*
* @param <T> The type of mob that the goal can be applied to.
*/
public interface EntityGoal<T extends Mob> extends Goal<T> {
@Override
default T addToEntity(@NotNull T entity, int priority) {
return EntityController.getFor(entity)
.addEntityGoal(priority, this)
.getEntity();
}
}

View File

@@ -0,0 +1,175 @@
package com.willfp.eco.core.entities.ai;
import com.google.common.collect.HashBiMap;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.entity.EntityGoalAvoidEntity;
import com.willfp.eco.core.entities.ai.entity.EntityGoalBreakDoors;
import com.willfp.eco.core.entities.ai.entity.EntityGoalBreatheAir;
import com.willfp.eco.core.entities.ai.entity.EntityGoalBreed;
import com.willfp.eco.core.entities.ai.entity.EntityGoalCatLieOnBed;
import com.willfp.eco.core.entities.ai.entity.EntityGoalCatSitOnBed;
import com.willfp.eco.core.entities.ai.entity.EntityGoalEatGrass;
import com.willfp.eco.core.entities.ai.entity.EntityGoalFleeSun;
import com.willfp.eco.core.entities.ai.entity.EntityGoalFloat;
import com.willfp.eco.core.entities.ai.entity.EntityGoalFollowBoats;
import com.willfp.eco.core.entities.ai.entity.EntityGoalFollowMobs;
import com.willfp.eco.core.entities.ai.entity.EntityGoalIllusionerBlindnessSpell;
import com.willfp.eco.core.entities.ai.entity.EntityGoalIllusionerMirrorSpell;
import com.willfp.eco.core.entities.ai.entity.EntityGoalInteract;
import com.willfp.eco.core.entities.ai.entity.EntityGoalLeapAtTarget;
import com.willfp.eco.core.entities.ai.entity.EntityGoalLookAtPlayer;
import com.willfp.eco.core.entities.ai.entity.EntityGoalMeleeAttack;
import com.willfp.eco.core.entities.ai.entity.EntityGoalMoveBackToVillage;
import com.willfp.eco.core.entities.ai.entity.EntityGoalMoveThroughVillage;
import com.willfp.eco.core.entities.ai.entity.EntityGoalMoveTowardsRestriction;
import com.willfp.eco.core.entities.ai.entity.EntityGoalMoveTowardsTarget;
import com.willfp.eco.core.entities.ai.entity.EntityGoalOcelotAttack;
import com.willfp.eco.core.entities.ai.entity.EntityGoalOpenDoors;
import com.willfp.eco.core.entities.ai.entity.EntityGoalPanic;
import com.willfp.eco.core.entities.ai.entity.EntityGoalRandomLookAround;
import com.willfp.eco.core.entities.ai.entity.EntityGoalRandomStroll;
import com.willfp.eco.core.entities.ai.entity.EntityGoalRandomSwimming;
import com.willfp.eco.core.entities.ai.entity.EntityGoalRangedAttack;
import com.willfp.eco.core.entities.ai.entity.EntityGoalRangedBowAttack;
import com.willfp.eco.core.entities.ai.entity.EntityGoalRangedCrossbowAttack;
import com.willfp.eco.core.entities.ai.entity.EntityGoalRestrictSun;
import com.willfp.eco.core.entities.ai.entity.EntityGoalStrollThroughVillage;
import com.willfp.eco.core.entities.ai.entity.EntityGoalTempt;
import com.willfp.eco.core.entities.ai.entity.EntityGoalTryFindWater;
import com.willfp.eco.core.entities.ai.entity.EntityGoalUseItem;
import com.willfp.eco.core.entities.ai.entity.EntityGoalWaterAvoidingRandomFlying;
import com.willfp.eco.core.entities.ai.entity.EntityGoalWaterAvoidingRandomStroll;
import com.willfp.eco.core.entities.ai.entity.EntityGoalWolfBeg;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* Class to manage entity goals.
*/
public final class EntityGoals {
/**
* All registered deserializers.
*/
private static final Map<NamespacedKey, KeyedDeserializer<? extends EntityGoal<?>>> BY_KEY = HashBiMap.create();
static {
register(EntityGoalAvoidEntity.DESERIALIZER);
register(EntityGoalBreakDoors.DESERIALIZER);
register(EntityGoalBreatheAir.DESERIALIZER);
register(EntityGoalEatGrass.DESERIALIZER);
register(EntityGoalFleeSun.DESERIALIZER);
register(EntityGoalFloat.DESERIALIZER);
register(EntityGoalFollowBoats.DESERIALIZER);
register(EntityGoalFollowMobs.DESERIALIZER);
register(EntityGoalInteract.DESERIALIZER);
register(EntityGoalLeapAtTarget.DESERIALIZER);
register(EntityGoalLookAtPlayer.DESERIALIZER);
register(EntityGoalMeleeAttack.DESERIALIZER);
register(EntityGoalMoveBackToVillage.DESERIALIZER);
register(EntityGoalMoveThroughVillage.DESERIALIZER);
register(EntityGoalMoveTowardsRestriction.DESERIALIZER);
register(EntityGoalMoveTowardsTarget.DESERIALIZER);
register(EntityGoalOcelotAttack.DESERIALIZER);
register(EntityGoalOpenDoors.DESERIALIZER);
register(EntityGoalPanic.DESERIALIZER);
register(EntityGoalRandomLookAround.DESERIALIZER);
register(EntityGoalRandomStroll.DESERIALIZER);
register(EntityGoalRandomSwimming.DESERIALIZER);
register(EntityGoalRangedAttack.DESERIALIZER);
register(EntityGoalRangedBowAttack.DESERIALIZER);
register(EntityGoalRangedCrossbowAttack.DESERIALIZER);
register(EntityGoalRestrictSun.DESERIALIZER);
register(EntityGoalStrollThroughVillage.DESERIALIZER);
register(EntityGoalTempt.DESERIALIZER);
register(EntityGoalTryFindWater.DESERIALIZER);
register(EntityGoalUseItem.DESERIALIZER);
register(EntityGoalWaterAvoidingRandomFlying.DESERIALIZER);
register(EntityGoalWaterAvoidingRandomStroll.DESERIALIZER);
register(EntityGoalWolfBeg.DESERIALIZER);
register(EntityGoalBreed.DESERIALIZER);
register(EntityGoalCatSitOnBed.DESERIALIZER);
register(EntityGoalCatLieOnBed.DESERIALIZER);
register(EntityGoalIllusionerBlindnessSpell.DESERIALIZER);
register(EntityGoalIllusionerMirrorSpell.DESERIALIZER);
}
/**
* Get deserializer by key.
*
* @param key The key.
* @return The deserializer, or null if not found.
*/
@Nullable
public static KeyedDeserializer<? extends EntityGoal<? extends Mob>> getByKey(@NotNull final NamespacedKey key) {
return BY_KEY.get(key);
}
/**
* Get deserializer by key, with a defined type (to prevent cluttering code with unsafe casts).
*
* @param key The key.
* @param clazz The type of target goal.
* @param <T> The type of mob the goal can be applied to.
* @return The deserializer, or null if not found.
*/
@Nullable
@SuppressWarnings({"unchecked", "unused"})
public static <T extends Mob> KeyedDeserializer<EntityGoal<T>> getByKeyOfType(@NotNull final NamespacedKey key,
@NotNull final Class<T> clazz) {
return (KeyedDeserializer<EntityGoal<T>>) BY_KEY.get(key);
}
/**
* Apply goal to entity given key and config.
* <p>
* If the key or config are invalid, the goal will not be applied.
*
* @param entity The entity.
* @param key The key.
* @param config The config.
* @param priority The priority.
* @param <T> The entity type.
* @return The entity.
*/
@NotNull
@SuppressWarnings("unchecked")
public static <T extends Mob> T applyToEntity(@NotNull final T entity,
@NotNull final NamespacedKey key,
@NotNull final Config config,
final int priority) {
KeyedDeserializer<EntityGoal<T>> deserializer = getByKeyOfType(key, (Class<T>) entity.getClass());
if (deserializer == null) {
return entity;
}
EntityGoal<T> goal = deserializer.deserialize(config);
if (goal == null) {
return entity;
}
return goal.addToEntity(entity, priority);
}
/**
* Register a deserializer for an entity goal.
*
* @param toRegister The entity goal to register.
* @param <T> The type of deserializer.
* @return The deserializer.
*/
@NotNull
public static <T extends KeyedDeserializer<? extends EntityGoal<?>>> T register(@NotNull final T toRegister) {
BY_KEY.put(toRegister.getKey(), toRegister);
return toRegister;
}
private EntityGoals() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -0,0 +1,24 @@
package com.willfp.eco.core.entities.ai;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
/**
* A generic goal for entity AI.
*
* @param <T> The type of mob that the goal can be applied to.
*/
public interface Goal<T extends Mob> {
/**
* Add the entity goal to an entity.
* <p>
* The lower the priority, the higher up the execution order; so
* priority 0 will execute first. Lower priority (higher number) goals
* will only execute if all higher priority goals are stopped.
*
* @param entity The entity.
* @param priority The priority.
* @return The entity, modified.
*/
T addToEntity(@NotNull T entity, int priority);
}

View File

@@ -0,0 +1,26 @@
package com.willfp.eco.core.entities.ai;
/**
* Flags for ai goals.
*/
public enum GoalFlag {
/**
* Move.
*/
MOVE,
/**
* Look around.
*/
LOOK,
/**
* Jump.
*/
JUMP,
/**
* Target.
*/
TARGET
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.core.entities.ai;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
/**
* A goal for entity target AI.
*
* @param <T> The type of mob that the goal can be applied to.
*/
public interface TargetGoal<T extends Mob> extends Goal<T> {
@Override
default T addToEntity(@NotNull T entity, int priority) {
return EntityController.getFor(entity)
.addTargetGoal(priority, this)
.getEntity();
}
}

View File

@@ -0,0 +1,117 @@
package com.willfp.eco.core.entities.ai;
import com.google.common.collect.HashBiMap;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.target.TargetGoalDefendVillage;
import com.willfp.eco.core.entities.ai.target.TargetGoalHurtBy;
import com.willfp.eco.core.entities.ai.target.TargetGoalNearestAttackable;
import com.willfp.eco.core.entities.ai.target.TargetGoalNearestAttackableWitch;
import com.willfp.eco.core.entities.ai.target.TargetGoalNearestHealableRaider;
import com.willfp.eco.core.entities.ai.target.TargetGoalNonTameRandom;
import com.willfp.eco.core.entities.ai.target.TargetGoalOwnerHurtBy;
import com.willfp.eco.core.entities.ai.target.TargetGoalOwnerTarget;
import com.willfp.eco.core.entities.ai.target.TargetGoalResetUniversalAnger;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* Class to manage target goals.
*/
public final class TargetGoals {
/**
* All registered deserializers.
*/
private static final Map<NamespacedKey, KeyedDeserializer<? extends TargetGoal<?>>> BY_KEY = HashBiMap.create();
static {
register(TargetGoalDefendVillage.DESERIALIZER);
register(TargetGoalHurtBy.DESERIALIZER);
register(TargetGoalNearestAttackable.DESERIALIZER);
register(TargetGoalNearestAttackableWitch.DESERIALIZER);
register(TargetGoalNearestHealableRaider.DESERIALIZER);
register(TargetGoalNonTameRandom.DESERIALIZER);
register(TargetGoalOwnerTarget.DESERIALIZER);
register(TargetGoalOwnerHurtBy.DESERIALIZER);
register(TargetGoalResetUniversalAnger.DESERIALIZER);
}
/**
* Get deserializer by key.
*
* @param key The key.
* @return The deserializer, or null if not found.
*/
@Nullable
public static KeyedDeserializer<? extends TargetGoal<? extends Mob>> getByKey(@NotNull final NamespacedKey key) {
return BY_KEY.get(key);
}
/**
* Get deserializer by key, with a defined type (to prevent cluttering code with unsafe casts).
*
* @param key The key.
* @param clazz The type of target goal.
* @param <T> The type of mob the goal can be applied to.
* @return The deserializer, or null if not found.
*/
@Nullable
@SuppressWarnings({"unchecked", "unused"})
public static <T extends Mob> KeyedDeserializer<TargetGoal<T>> getByKeyOfType(@NotNull final NamespacedKey key,
@NotNull final Class<T> clazz) {
return (KeyedDeserializer<TargetGoal<T>>) BY_KEY.get(key);
}
/**
* Apply goal to entity given key and config.
* <p>
* If the key or config are invalid, the goal will not be applied.
*
* @param entity The entity.
* @param key The key.
* @param config The config.
* @param priority The priority.
* @param <T> The entity type.
* @return The entity.
*/
@NotNull
@SuppressWarnings("unchecked")
public static <T extends Mob> T applyToEntity(@NotNull final T entity,
@NotNull final NamespacedKey key,
@NotNull final Config config,
final int priority) {
KeyedDeserializer<TargetGoal<T>> deserializer = getByKeyOfType(key, (Class<T>) entity.getClass());
if (deserializer == null) {
return entity;
}
TargetGoal<T> goal = deserializer.deserialize(config);
if (goal == null) {
return entity;
}
return goal.addToEntity(entity, priority);
}
/**
* Register a deserializer for a target goal.
*
* @param toRegister The target goal to register.
* @param <T> The type of deserializer.
* @return The deserializer.
*/
@NotNull
public static <T extends KeyedDeserializer<? extends TargetGoal<?>>> T register(@NotNull final T toRegister) {
BY_KEY.put(toRegister.getKey(), toRegister);
return toRegister;
}
private TargetGoals() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -0,0 +1,73 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.Entities;
import com.willfp.eco.core.entities.TestableEntity;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Avoid entities.
*
* @param entity The entity type to avoid.
* @param distance The distance to flee to.
* @param slowSpeed The slow movement speed.
* @param fastSpeed The fast movement speed.
*/
public record EntityGoalAvoidEntity(
@NotNull TestableEntity entity,
double distance,
double slowSpeed,
double fastSpeed
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalAvoidEntity> DESERIALIZER = new Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalAvoidEntity> {
@Override
@Nullable
public EntityGoalAvoidEntity deserialize(@NotNull final Config config) {
if (!(
config.has("entity")
&& config.has("distance")
&& config.has("slowSpeed")
&& config.has("fastSpeed")
)) {
return null;
}
try {
TestableEntity entity = Entities.lookup(config.getString("entity"));
return new EntityGoalAvoidEntity(
entity,
config.getDouble("distance"),
config.getDouble("slowSpeed"),
config.getDouble("fastSpeed")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("avoid_entity");
}
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows an entity to break down doors.
*
* @param ticks The time taken to break the door. Minimum value is 240, as set by the game.
*/
public record EntityGoalBreakDoors(
int ticks
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalBreakDoors> DESERIALIZER = new EntityGoalBreakDoors.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalBreakDoors> {
@Override
@Nullable
public EntityGoalBreakDoors deserialize(@NotNull final Config config) {
if (!(
config.has("ticks")
)) {
return null;
}
try {
return new EntityGoalBreakDoors(
config.getInt("ticks")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("break_doors");
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
/**
* Breathe air.
*/
public record EntityGoalBreatheAir(
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalBreatheAir> DESERIALIZER = new EntityGoalBreatheAir.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalBreatheAir> {
@Override
public EntityGoalBreatheAir deserialize(@NotNull final Config config) {
return new EntityGoalBreatheAir();
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("breathe_air");
}
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Animals;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows animals to breed.
*
* @param speed The speed at which to move to a partner.
*/
public record EntityGoalBreed(
double speed
) implements EntityGoal<Animals> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalBreed> DESERIALIZER = new EntityGoalBreed.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalBreed> {
@Override
@Nullable
public EntityGoalBreed deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
)) {
return null;
}
try {
return new EntityGoalBreed(
config.getDouble("speed")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("breed");
}
}
}

View File

@@ -0,0 +1,61 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Cat;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows a cat to lie on a bed.
*
* @param speed The speed at which to move to the bed.
* @param range The range at which to search for beds.
*/
public record EntityGoalCatLieOnBed(
double speed,
int range
) implements EntityGoal<Cat> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalCatLieOnBed> DESERIALIZER = new EntityGoalCatLieOnBed.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalCatLieOnBed> {
@Override
@Nullable
public EntityGoalCatLieOnBed deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
&& config.has("range")
)) {
return null;
}
try {
return new EntityGoalCatLieOnBed(
config.getDouble("speed"),
config.getInt("range")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("cat_lie_on_bed");
}
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Cat;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows a cat to sit on a bed.
*
* @param speed The speed at which to move to the bed.
*/
public record EntityGoalCatSitOnBed(
double speed
) implements EntityGoal<Cat> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalCatSitOnBed> DESERIALIZER = new EntityGoalCatSitOnBed.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalCatSitOnBed> {
@Override
@Nullable
public EntityGoalCatSitOnBed deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
)) {
return null;
}
try {
return new EntityGoalCatSitOnBed(
config.getDouble("speed")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("cat_sit_on_bed");
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
/**
* Allows an entity to eat the ground.
*/
public record EntityGoalEatGrass(
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalEatGrass> DESERIALIZER = new EntityGoalEatGrass.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalEatGrass> {
@Override
public EntityGoalEatGrass deserialize(@NotNull final Config config) {
return new EntityGoalEatGrass();
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("eat_grass");
}
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Will make the entity actively avoid the sunlight.
*
* @param speed The speed at which to flee.
*/
public record EntityGoalFleeSun(
double speed
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalFleeSun> DESERIALIZER = new EntityGoalFleeSun.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalFleeSun> {
@Override
@Nullable
public EntityGoalFleeSun deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
)) {
return null;
}
try {
return new EntityGoalFleeSun(
config.getDouble("speed")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("flee_sun");
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
/**
* Allows an entity to float on water.
*/
public record EntityGoalFloat(
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalFloat> DESERIALIZER = new EntityGoalFloat.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalFloat> {
@Override
public EntityGoalFloat deserialize(@NotNull final Config config) {
return new EntityGoalFloat();
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("float");
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
/**
* Follow boats.
*/
public record EntityGoalFollowBoats(
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalFollowBoats> DESERIALIZER = new EntityGoalFollowBoats.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalFollowBoats> {
@Override
public EntityGoalFollowBoats deserialize(@NotNull final Config config) {
return new EntityGoalFollowBoats();
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("follow_boats");
}
}
}

View File

@@ -0,0 +1,65 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows an entity to follow and gather around all types of mobs, both hostile and neutral mobs.
*
* @param speed The speed at which to follow.
* @param minDistance The minimum follow distance.
* @param maxDistance The maximum follow distance.
*/
public record EntityGoalFollowMobs(
double speed,
double minDistance,
double maxDistance
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalFollowMobs> DESERIALIZER = new EntityGoalFollowMobs.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalFollowMobs> {
@Override
@Nullable
public EntityGoalFollowMobs deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
&& config.has("minDistance")
&& config.has("maxDistance")
)) {
return null;
}
try {
return new EntityGoalFollowMobs(
config.getDouble("speed"),
config.getDouble("minDistance"),
config.getDouble("maxDistance")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("follow_mobs");
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Illusioner;
import org.jetbrains.annotations.NotNull;
/**
* Allows an illusioner to perform the blindness spell.
*/
public record EntityGoalIllusionerBlindnessSpell(
) implements EntityGoal<Illusioner> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalIllusionerBlindnessSpell> DESERIALIZER = new EntityGoalIllusionerBlindnessSpell.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalIllusionerBlindnessSpell> {
@Override
public EntityGoalIllusionerBlindnessSpell deserialize(@NotNull final Config config) {
return new EntityGoalIllusionerBlindnessSpell();
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("illusioner_blindness_spell");
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Illusioner;
import org.jetbrains.annotations.NotNull;
/**
* Allows an illusioner to perform the mirror spell.
*/
public record EntityGoalIllusionerMirrorSpell(
) implements EntityGoal<Illusioner> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalIllusionerMirrorSpell> DESERIALIZER = new EntityGoalIllusionerMirrorSpell.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalIllusionerMirrorSpell> {
@Override
public EntityGoalIllusionerMirrorSpell deserialize(@NotNull final Config config) {
return new EntityGoalIllusionerMirrorSpell();
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("illusioner_mirror_spell");
}
}
}

View File

@@ -0,0 +1,67 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.Entities;
import com.willfp.eco.core.entities.TestableEntity;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Interact with other mobs.
*
* @param target The type of entity to interact with.
* @param range The range at which to interact.
* @param chance The chance for interaction, between 0 and 1.
*/
public record EntityGoalInteract(
@NotNull TestableEntity target,
double range,
double chance
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalInteract> DESERIALIZER = new EntityGoalInteract.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalInteract> {
@Override
@Nullable
public EntityGoalInteract deserialize(@NotNull final Config config) {
if (!(
config.has("target")
&& config.has("range")
&& config.has("chance")
)) {
return null;
}
try {
return new EntityGoalInteract(
Entities.lookup(config.getString("target")),
config.getDouble("range"),
config.getDouble("chance")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("interact");
}
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows an entity to jump towards a target.
*
* @param velocity The leap velocity.
*/
public record EntityGoalLeapAtTarget(
double velocity
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalLeapAtTarget> DESERIALIZER = new EntityGoalLeapAtTarget.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalLeapAtTarget> {
@Override
@Nullable
public EntityGoalLeapAtTarget deserialize(@NotNull final Config config) {
if (!(
config.has("velocity")
)) {
return null;
}
try {
return new EntityGoalLeapAtTarget(
config.getDouble("velocity")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("leap_at_target");
}
}
}

View File

@@ -0,0 +1,61 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows an entity to look at the player by rotating the head bone pose within a set limit.
*
* @param range The range at which to look at the player.
* @param chance The chance to look at the player, between 0 and 1.
*/
public record EntityGoalLookAtPlayer(
double range,
double chance
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalLookAtPlayer> DESERIALIZER = new EntityGoalLookAtPlayer.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalLookAtPlayer> {
@Override
@Nullable
public EntityGoalLookAtPlayer deserialize(@NotNull final Config config) {
if (!(
config.has("range")
&& config.has("chance")
)) {
return null;
}
try {
return new EntityGoalLookAtPlayer(
config.getDouble("range"),
config.getDouble("chance")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("look_at_player");
}
}
}

View File

@@ -0,0 +1,61 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows entities to make close combat melee attacks.
*
* @param speed The speed at which to attack the target.
* @param pauseWhenMobIdle If the entity should pause attacking when the target is idle.
*/
public record EntityGoalMeleeAttack(
double speed,
boolean pauseWhenMobIdle
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalMeleeAttack> DESERIALIZER = new EntityGoalMeleeAttack.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalMeleeAttack> {
@Override
@Nullable
public EntityGoalMeleeAttack deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
&& config.has("pauseWhenMobIdle")
)) {
return null;
}
try {
return new EntityGoalMeleeAttack(
config.getDouble("speed"),
config.getBool("pauseWhenMobIdle")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("melee_attack");
}
}
}

View File

@@ -0,0 +1,61 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows an entity to navigate and search for a nearby village.
*
* @param speed The speed at which to move back to the village.
* @param canDespawn If the entity can despawn.
*/
public record EntityGoalMoveBackToVillage(
double speed,
boolean canDespawn
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalMoveBackToVillage> DESERIALIZER = new EntityGoalMoveBackToVillage.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalMoveBackToVillage> {
@Override
@Nullable
public EntityGoalMoveBackToVillage deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
&& config.has("canDespawn")
)) {
return null;
}
try {
return new EntityGoalMoveBackToVillage(
config.getDouble("speed"),
config.getBool("canDespawn")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("move_back_to_village");
}
}
}

View File

@@ -0,0 +1,69 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows the entity to create paths around the village.
*
* @param speed The speed at which to move through the village.
* @param onlyAtNight If the entity can only move through village at night.
* @param distance The distance to move through the village.
* @param canPassThroughDoors If the entity can pass through doors.
*/
public record EntityGoalMoveThroughVillage(
double speed,
boolean onlyAtNight,
int distance,
boolean canPassThroughDoors
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalMoveThroughVillage> DESERIALIZER = new EntityGoalMoveThroughVillage.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalMoveThroughVillage> {
@Override
@Nullable
public EntityGoalMoveThroughVillage deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
&& config.has("onlyAtNight")
&& config.has("distance")
&& config.has("canPassThroughDoors")
)) {
return null;
}
try {
return new EntityGoalMoveThroughVillage(
config.getDouble("speed"),
config.getBool("onlyAtNight"),
config.getInt("distance"),
config.getBool("canPassThroughDoors")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("move_through_village");
}
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Move towards restriction.
*
* @param speed The speed at which to move towards the restriction.
*/
public record EntityGoalMoveTowardsRestriction(
double speed
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalMoveTowardsRestriction> DESERIALIZER = new EntityGoalMoveTowardsRestriction.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalMoveTowardsRestriction> {
@Override
@Nullable
public EntityGoalMoveTowardsRestriction deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
)) {
return null;
}
try {
return new EntityGoalMoveTowardsRestriction(
config.getDouble("speed")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("move_towards_restriction");
}
}
}

View File

@@ -0,0 +1,61 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows an entity to move towards a target.
*
* @param speed The speed at which to move towards the target.
* @param maxDistance The maximum distance the target can be where the entity will still move towards it.
*/
public record EntityGoalMoveTowardsTarget(
double speed,
double maxDistance
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalMoveTowardsTarget> DESERIALIZER = new EntityGoalMoveTowardsTarget.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalMoveTowardsTarget> {
@Override
@Nullable
public EntityGoalMoveTowardsTarget deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
&& config.has("maxDistance")
)) {
return null;
}
try {
return new EntityGoalMoveTowardsTarget(
config.getDouble("speed"),
config.getDouble("maxDistance")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("move_towards_target");
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
/**
* Attack like an ocelot.
*/
public record EntityGoalOcelotAttack(
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalOcelotAttack> DESERIALIZER = new EntityGoalOcelotAttack.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalOcelotAttack> {
@Override
public EntityGoalOcelotAttack deserialize(@NotNull final Config config) {
return new EntityGoalOcelotAttack();
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("ocelot_attack");
}
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows an entity to interact and open a door.
*
* @param delayClosing If closing the door should be delayed.
*/
public record EntityGoalOpenDoors(
boolean delayClosing
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalOpenDoors> DESERIALIZER = new EntityGoalOpenDoors.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalOpenDoors> {
@Override
@Nullable
public EntityGoalOpenDoors deserialize(@NotNull final Config config) {
if (!(
config.has("delayClosing")
)) {
return null;
}
try {
return new EntityGoalOpenDoors(
config.getBool("delayClosing")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("open_doors");
}
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows an entity to react when it receives damage.
*
* @param speed The speed at which to panic.
*/
public record EntityGoalPanic(
double speed
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalPanic> DESERIALIZER = new EntityGoalPanic.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalPanic> {
@Override
@Nullable
public EntityGoalPanic deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
)) {
return null;
}
try {
return new EntityGoalPanic(
config.getDouble("speed")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("panic");
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
/**
* Allows an entity to choose a random direction to look in for a random duration within a range.
*/
public record EntityGoalRandomLookAround(
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalRandomLookAround> DESERIALIZER = new EntityGoalRandomLookAround.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalRandomLookAround> {
@Override
public EntityGoalRandomLookAround deserialize(@NotNull final Config config) {
return new EntityGoalRandomLookAround();
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("random_look_around");
}
}
}

View File

@@ -0,0 +1,65 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows an entity to choose a random direction to walk towards.
*
* @param speed The speed at which to move around.
* @param interval The amount of ticks to wait (on average) between strolling around.
* @param canDespawn If the entity can despawn.
*/
public record EntityGoalRandomStroll(
double speed,
int interval,
boolean canDespawn
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalRandomStroll> DESERIALIZER = new EntityGoalRandomStroll.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalRandomStroll> {
@Override
@Nullable
public EntityGoalRandomStroll deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
&& config.has("interval")
&& config.has("canDespawn")
)) {
return null;
}
try {
return new EntityGoalRandomStroll(
config.getDouble("speed"),
config.getInt("interval"),
config.getBool("canDespawn")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("random_stroll");
}
}
}

View File

@@ -0,0 +1,61 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows an entity to swim in a random point in water.
*
* @param speed The speed at which to move around.
* @param interval The amount of ticks to wait (on average) between strolling around.
*/
public record EntityGoalRandomSwimming(
double speed,
int interval
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalRandomSwimming> DESERIALIZER = new EntityGoalRandomSwimming.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalRandomSwimming> {
@Override
@Nullable
public EntityGoalRandomSwimming deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
&& config.has("interval")
)) {
return null;
}
try {
return new EntityGoalRandomSwimming(
config.getDouble("speed"),
config.getInt("interval")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("random_swimming");
}
}
}

View File

@@ -0,0 +1,71 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Ranged attack.
* <p>
* Only supports mobs that have ranged attacks.
*
* @param speed The speed.
* @param minInterval The minimum interval between attacks (in ticks).
* @param maxInterval The maximum interval between attacks (in ticks).
* @param maxRange The max range at which to attack.
*/
public record EntityGoalRangedAttack(
double speed,
int minInterval,
int maxInterval,
double maxRange
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalRangedAttack> DESERIALIZER = new EntityGoalRangedAttack.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalRangedAttack> {
@Override
@Nullable
public EntityGoalRangedAttack deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
&& config.has("minInterval")
&& config.has("maxInterval")
&& config.has("range")
)) {
return null;
}
try {
return new EntityGoalRangedAttack(
config.getDouble("speed"),
config.getInt("minInterval"),
config.getInt("maxInterval"),
config.getDouble("range")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("ranged_attack");
}
}
}

View File

@@ -0,0 +1,67 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Monster;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Ranged attack.
* <p>
* Only supports monsters that have bow attacks.
*
* @param speed The speed.
* @param interval The interval between attacks (in ticks).
* @param range The max range at which to attack.
*/
public record EntityGoalRangedBowAttack(
double speed,
int interval,
double range
) implements EntityGoal<Monster> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalRangedBowAttack> DESERIALIZER = new EntityGoalRangedBowAttack.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalRangedBowAttack> {
@Override
@Nullable
public EntityGoalRangedBowAttack deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
&& config.has("interval")
&& config.has("range")
)) {
return null;
}
try {
return new EntityGoalRangedBowAttack(
config.getDouble("speed"),
config.getInt("interval"),
config.getDouble("range")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("ranged_bow_attack");
}
}
}

View File

@@ -0,0 +1,63 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Monster;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Ranged attack.
* <p>
* Only supports monsters that have crossbow attacks.
*
* @param speed The speed.
* @param range The max range at which to attack.
*/
public record EntityGoalRangedCrossbowAttack(
double speed,
double range
) implements EntityGoal<Monster> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalRangedCrossbowAttack> DESERIALIZER = new EntityGoalRangedCrossbowAttack.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalRangedCrossbowAttack> {
@Override
@Nullable
public EntityGoalRangedCrossbowAttack deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
&& config.has("range")
)) {
return null;
}
try {
return new EntityGoalRangedCrossbowAttack(
config.getDouble("speed"),
config.getDouble("range")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("ranged_crossbow_attack");
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
/**
* Allows an entity to actively avoid direct sunlight.
*/
public record EntityGoalRestrictSun(
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalRestrictSun> DESERIALIZER = new EntityGoalRestrictSun.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalRestrictSun> {
@Override
public EntityGoalRestrictSun deserialize(@NotNull final Config config) {
return new EntityGoalRestrictSun();
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("restrict_sun");
}
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows the entity to create paths around the village.
*
* @param searchRange The search range.
*/
public record EntityGoalStrollThroughVillage(
int searchRange
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalStrollThroughVillage> DESERIALIZER = new EntityGoalStrollThroughVillage.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalStrollThroughVillage> {
@Override
@Nullable
public EntityGoalStrollThroughVillage deserialize(@NotNull final Config config) {
if (!(
config.has("searchRange")
)) {
return null;
}
try {
return new EntityGoalStrollThroughVillage(
config.getInt("searchRange")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("stroll_through_village");
}
}
}

View File

@@ -0,0 +1,88 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.items.Items;
import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.recipe.parts.EmptyTestableItem;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* Allows an entity to be tempted by a set item.
*
* @param speed The speed at which the entity follows the item.
* @param items The items that the entity will be attracted by.
* @param canBeScared If the entity can be scared and lose track of the item.
*/
public record EntityGoalTempt(
double speed,
@NotNull Collection<TestableItem> items,
boolean canBeScared
) implements EntityGoal<Mob> {
/**
* @param speed The speed at which the entity follows the item.
* @param item The item that the entity will be attracted by.
* @param canBeScared If the entity can be scared and lose track of the item.
*/
public EntityGoalTempt(final double speed,
@NotNull final TestableItem item,
final boolean canBeScared) {
this(speed, List.of(item), canBeScared);
}
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalTempt> DESERIALIZER = new EntityGoalTempt.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalTempt> {
@Override
@Nullable
public EntityGoalTempt deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
&& config.has("items")
&& config.has("canBeScared")
)) {
return null;
}
try {
Collection<TestableItem> items = config.getStrings("items").stream()
.map(Items::lookup)
.filter(it -> !(it instanceof EmptyTestableItem))
.collect(Collectors.toList());
return new EntityGoalTempt(
config.getDouble("speed"),
items,
config.getBool("canBeScared")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("tempt");
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
/**
* Allows an entity to move to water when on land.
*/
public record EntityGoalTryFindWater(
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalTryFindWater> DESERIALIZER = new EntityGoalTryFindWater.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalTryFindWater> {
@Override
public EntityGoalTryFindWater deserialize(@NotNull final Config config) {
return new EntityGoalTryFindWater();
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("try_find_water");
}
}
}

View File

@@ -0,0 +1,75 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.Entities;
import com.willfp.eco.core.entities.TestableEntity;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.items.Items;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.Sound;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Mob;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Predicate;
/**
* Use item.
*
* @param item The item.
* @param sound The sound to play on use.
* @param condition The condition when to use the item.
*/
public record EntityGoalUseItem(
@NotNull ItemStack item,
@NotNull Sound sound,
@NotNull Predicate<LivingEntity> condition
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalUseItem> DESERIALIZER = new EntityGoalUseItem.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalUseItem> {
@Override
@Nullable
public EntityGoalUseItem deserialize(@NotNull final Config config) {
if (!(
config.has("item")
&& config.has("sound")
&& config.has("condition")
)) {
return null;
}
try {
TestableEntity filter = Entities.lookup(config.getString("condition"));
return new EntityGoalUseItem(
Items.lookup(config.getString("item")).getItem(),
Sound.valueOf(config.getString("sound").toUpperCase()),
filter::matches
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("use_item");
}
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Fly randomly while avoiding water.
*
* @param speed The speed.
*/
public record EntityGoalWaterAvoidingRandomFlying(
double speed
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalWaterAvoidingRandomFlying> DESERIALIZER = new EntityGoalWaterAvoidingRandomFlying.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalWaterAvoidingRandomFlying> {
@Override
@Nullable
public EntityGoalWaterAvoidingRandomFlying deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
)) {
return null;
}
try {
return new EntityGoalWaterAvoidingRandomFlying(
config.getDouble("speed")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("water_avoiding_random_flying");
}
}
}

View File

@@ -0,0 +1,61 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Stroll randomly while avoiding water.
*
* @param speed The speed.
* @param chance The chance to stroll every tick, between 0 and 1.
*/
public record EntityGoalWaterAvoidingRandomStroll(
double speed,
double chance
) implements EntityGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalWaterAvoidingRandomStroll> DESERIALIZER = new EntityGoalWaterAvoidingRandomStroll.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalWaterAvoidingRandomStroll> {
@Override
@Nullable
public EntityGoalWaterAvoidingRandomStroll deserialize(@NotNull final Config config) {
if (!(
config.has("speed")
&& config.has("chance")
)) {
return null;
}
try {
return new EntityGoalWaterAvoidingRandomStroll(
config.getDouble("speed"),
config.getDouble("chance")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("water_avoiding_random_stroll");
}
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.entities.ai.entity;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Wolf;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows a wolf to beg.
*
* @param distance The distance at which to beg from.
*/
public record EntityGoalWolfBeg(
double distance
) implements EntityGoal<Wolf> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<EntityGoalWolfBeg> DESERIALIZER = new EntityGoalWolfBeg.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<EntityGoalWolfBeg> {
@Override
@Nullable
public EntityGoalWolfBeg deserialize(@NotNull final Config config) {
if (!(
config.has("distance")
)) {
return null;
}
try {
return new EntityGoalWolfBeg(
config.getDouble("distance")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("wolf_beg");
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.core.entities.ai.target;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.TargetGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.IronGolem;
import org.jetbrains.annotations.NotNull;
/**
* Defend village.
*/
public record TargetGoalDefendVillage(
) implements TargetGoal<IronGolem> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<TargetGoalDefendVillage> DESERIALIZER = new TargetGoalDefendVillage.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<TargetGoalDefendVillage> {
@Override
public TargetGoalDefendVillage deserialize(@NotNull final Config config) {
return new TargetGoalDefendVillage();
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("defend_village");
}
}
}

View File

@@ -0,0 +1,60 @@
package com.willfp.eco.core.entities.ai.target;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.Entities;
import com.willfp.eco.core.entities.TestableEntity;
import com.willfp.eco.core.entities.ai.TargetGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Allows an entity to react when hit by set target.
*
* @param blacklist The entities not to attack when hurt by.
*/
@SuppressWarnings({"varargs"})
public record TargetGoalHurtBy(
@NotNull TestableEntity blacklist
) implements TargetGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<TargetGoalHurtBy> DESERIALIZER = new TargetGoalHurtBy.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<TargetGoalHurtBy> {
@Override
@Nullable
public TargetGoalHurtBy deserialize(@NotNull final Config config) {
if (!(
config.has("blacklist")
)) {
return null;
}
try {
return new TargetGoalHurtBy(
Entities.lookup(config.getString("blacklist"))
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("hurt_by");
}
}
}

View File

@@ -0,0 +1,101 @@
package com.willfp.eco.core.entities.ai.target;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.Entities;
import com.willfp.eco.core.entities.TestableEntity;
import com.willfp.eco.core.entities.ai.TargetGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Raider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Predicate;
/**
* Allows an entity to attack the closest target within a given subset of specific target types.
*
* @param target The type of entities to attack.
* @param checkVisibility If visibility should be checked.
* @param checkCanNavigate If navigation should be checked.
* @param reciprocalChance 1 in reciprocalChance chance of not activating on any tick.
* @param targetFilter The filter for targets to match.
*/
public record TargetGoalNearestAttackable(
@NotNull TestableEntity target,
boolean checkVisibility,
boolean checkCanNavigate,
int reciprocalChance,
@NotNull Predicate<LivingEntity> targetFilter
) implements TargetGoal<Raider> {
/**
* @param target The type of entities to attack.
* @param checkVisibility If visibility should be checked.
* @param checkCanNavigate If navigation should be checked.
* @param reciprocalChance 1 in reciprocalChance chance of not activating on any tick.
*/
public TargetGoalNearestAttackable(@NotNull final TestableEntity target,
final boolean checkVisibility,
final boolean checkCanNavigate,
final int reciprocalChance) {
this(target, checkVisibility, checkCanNavigate, reciprocalChance, it -> true);
}
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<TargetGoalNearestAttackable> DESERIALIZER = new TargetGoalNearestAttackable.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<TargetGoalNearestAttackable> {
@Override
@Nullable
public TargetGoalNearestAttackable deserialize(@NotNull final Config config) {
if (!(
config.has("target")
&& config.has("checkVisibility")
&& config.has("checkCanNavigate")
&& config.has("reciprocalChance")
)) {
return null;
}
try {
if (config.has("targetFilter")) {
TestableEntity filter = Entities.lookup(config.getString("targetFilter"));
return new TargetGoalNearestAttackable(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
config.getBool("checkCanNavigate"),
config.getInt("reciprocalChance"),
filter::matches
);
} else {
return new TargetGoalNearestAttackable(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
config.getBool("checkCanNavigate"),
config.getInt("reciprocalChance")
);
}
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("nearest_attackable");
}
}
}

View File

@@ -0,0 +1,101 @@
package com.willfp.eco.core.entities.ai.target;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.Entities;
import com.willfp.eco.core.entities.TestableEntity;
import com.willfp.eco.core.entities.ai.TargetGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Raider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Predicate;
/**
* Allows an entity to attack the closest target within a given subset of specific target types.
*
* @param target The type of entities to attack.
* @param checkVisibility If visibility should be checked.
* @param checkCanNavigate If navigation should be checked.
* @param reciprocalChance 1 in reciprocalChance chance of not activating on any tick.
* @param targetFilter The filter for targets to match.
*/
public record TargetGoalNearestAttackableWitch(
@NotNull TestableEntity target,
boolean checkVisibility,
boolean checkCanNavigate,
int reciprocalChance,
@NotNull Predicate<LivingEntity> targetFilter
) implements TargetGoal<Raider> {
/**
* @param target The type of entities to attack.
* @param checkVisibility If visibility should be checked.
* @param checkCanNavigate If navigation should be checked.
* @param reciprocalChance 1 in reciprocalChance chance of not activating on any tick.
*/
public TargetGoalNearestAttackableWitch(@NotNull final TestableEntity target,
final boolean checkVisibility,
final boolean checkCanNavigate,
final int reciprocalChance) {
this(target, checkVisibility, checkCanNavigate, reciprocalChance, it -> true);
}
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<TargetGoalNearestAttackableWitch> DESERIALIZER = new TargetGoalNearestAttackableWitch.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<TargetGoalNearestAttackableWitch> {
@Override
@Nullable
public TargetGoalNearestAttackableWitch deserialize(@NotNull final Config config) {
if (!(
config.has("target")
&& config.has("checkVisibility")
&& config.has("checkCanNavigate")
&& config.has("reciprocalChance")
)) {
return null;
}
try {
if (config.has("targetFilter")) {
TestableEntity filter = Entities.lookup(config.getString("targetFilter"));
return new TargetGoalNearestAttackableWitch(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
config.getBool("checkCanNavigate"),
config.getInt("reciprocalChance"),
filter::matches
);
} else {
return new TargetGoalNearestAttackableWitch(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
config.getBool("checkCanNavigate"),
config.getInt("reciprocalChance")
);
}
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("nearest_attackable_witch");
}
}
}

View File

@@ -0,0 +1,87 @@
package com.willfp.eco.core.entities.ai.target;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.Entities;
import com.willfp.eco.core.entities.TestableEntity;
import com.willfp.eco.core.entities.ai.TargetGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Raider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Predicate;
/**
* Target nearest attackable raider.
*
* @param target The types of entities to heal.
* @param checkVisibility If visibility should be checked.
* @param targetFilter The filter for targets to match.
*/
public record TargetGoalNearestHealableRaider(
@NotNull TestableEntity target,
boolean checkVisibility,
@NotNull Predicate<LivingEntity> targetFilter
) implements TargetGoal<Raider> {
/**
* @param target The target.
* @param checkVisibility If visibility should be checked.
*/
public TargetGoalNearestHealableRaider(@NotNull final TestableEntity target,
final boolean checkVisibility) {
this(target, checkVisibility, it -> true);
}
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<TargetGoalNearestHealableRaider> DESERIALIZER = new TargetGoalNearestHealableRaider.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<TargetGoalNearestHealableRaider> {
@Override
@Nullable
public TargetGoalNearestHealableRaider deserialize(@NotNull final Config config) {
if (!(
config.has("target")
&& config.has("checkVisibility")
)) {
return null;
}
try {
if (config.has("targetFilter")) {
TestableEntity filter = Entities.lookup(config.getString("targetFilter"));
return new TargetGoalNearestHealableRaider(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
filter::matches
);
} else {
return new TargetGoalNearestHealableRaider(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility")
);
}
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("nearest_healable_raider");
}
}
}

View File

@@ -0,0 +1,87 @@
package com.willfp.eco.core.entities.ai.target;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.Entities;
import com.willfp.eco.core.entities.TestableEntity;
import com.willfp.eco.core.entities.ai.TargetGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Tameable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Predicate;
/**
* Target random non-tame entity.
*
* @param target The types of entities to heal.
* @param checkVisibility If visibility should be checked.
* @param targetFilter The filter for targets to match.
*/
public record TargetGoalNonTameRandom(
@NotNull TestableEntity target,
boolean checkVisibility,
@NotNull Predicate<LivingEntity> targetFilter
) implements TargetGoal<Tameable> {
/**
* @param target The types of entities to heal.
* @param checkVisibility If visibility should be checked.
*/
public TargetGoalNonTameRandom(@NotNull final TestableEntity target,
final boolean checkVisibility) {
this(target, checkVisibility, it -> true);
}
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<TargetGoalNonTameRandom> DESERIALIZER = new TargetGoalNonTameRandom.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<TargetGoalNonTameRandom> {
@Override
@Nullable
public TargetGoalNonTameRandom deserialize(@NotNull final Config config) {
if (!(
config.has("targetClass")
&& config.has("checkVisibility")
)) {
return null;
}
try {
if (config.has("targetFilter")) {
TestableEntity filter = Entities.lookup(config.getString("targetFilter"));
return new TargetGoalNonTameRandom(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility"),
filter::matches
);
} else {
return new TargetGoalNonTameRandom(
Entities.lookup(config.getString("target")),
config.getBool("checkVisibility")
);
}
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("non_tame_random");
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.core.entities.ai.target;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.TargetGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Tameable;
import org.jetbrains.annotations.NotNull;
/**
* Allows an entity to react when the owner is hit by a target.
*/
public record TargetGoalOwnerHurtBy(
) implements TargetGoal<Tameable> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<TargetGoalOwnerHurtBy> DESERIALIZER = new TargetGoalOwnerHurtBy.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<TargetGoalOwnerHurtBy> {
@Override
public TargetGoalOwnerHurtBy deserialize(@NotNull final Config config) {
return new TargetGoalOwnerHurtBy();
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("owner_hurt_by");
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.core.entities.ai.target;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.TargetGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Tameable;
import org.jetbrains.annotations.NotNull;
/**
* Allows an entity to react when the owner hits a target.
*/
public record TargetGoalOwnerTarget(
) implements TargetGoal<Tameable> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<TargetGoalOwnerTarget> DESERIALIZER = new TargetGoalOwnerTarget.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<TargetGoalOwnerTarget> {
@Override
public TargetGoalOwnerTarget deserialize(@NotNull final Config config) {
return new TargetGoalOwnerTarget();
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("owner_target");
}
}
}

View File

@@ -0,0 +1,59 @@
package com.willfp.eco.core.entities.ai.target;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.entities.ai.TargetGoal;
import com.willfp.eco.core.serialization.KeyedDeserializer;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Reset universal anger.
* <p>
* Can only be applied to neutral mobs.
*
* @param triggerOthers If this should cause other nearby entities to trigger.
*/
public record TargetGoalResetUniversalAnger(
boolean triggerOthers
) implements TargetGoal<Mob> {
/**
* The deserializer for the goal.
*/
public static final KeyedDeserializer<TargetGoalResetUniversalAnger> DESERIALIZER = new TargetGoalResetUniversalAnger.Deserializer();
/**
* Deserialize configs into the goal.
*/
private static final class Deserializer implements KeyedDeserializer<TargetGoalResetUniversalAnger> {
@Override
@Nullable
public TargetGoalResetUniversalAnger deserialize(@NotNull final Config config) {
if (!(
config.has("triggerOthers")
)) {
return null;
}
try {
return new TargetGoalResetUniversalAnger(
config.getBool("triggerOthers")
);
} catch (Exception e) {
/*
Exceptions could be caused by configs having values of a wrong type,
invalid enum parameters, etc. Serializers shouldn't throw exceptions,
so we encapsulate them as null.
*/
return null;
}
}
@NotNull
@Override
public NamespacedKey getKey() {
return NamespacedKey.minecraft("reset_universal_anger");
}
}
}

View File

@@ -2,12 +2,14 @@ package com.willfp.eco.core.lookup;
import org.jetbrains.annotations.Nullable;
import java.util.function.Predicate;
/**
* Interface for testing if any object matches another object.
*
* @param <T> The type of object.
*/
public interface Testable<T> {
public interface Testable<T> extends Predicate<T> {
/**
* If object matches the test.
*
@@ -15,4 +17,9 @@ public interface Testable<T> {
* @return If matches.
*/
boolean matches(@Nullable T other);
@Override
default boolean test(@Nullable T other) {
return this.matches(other);
}
}

View File

@@ -19,7 +19,8 @@ public final class ProxyConstants {
*/
public static final List<String> SUPPORTED_VERSIONS = Arrays.asList(
"v1_17_R1",
"v1_18_R1"
"v1_18_R1",
"v1_18_R2"
);
private ProxyConstants() {

View File

@@ -31,6 +31,7 @@ public final class Recipes {
* Cached recipes from matrix.
*/
private static final LoadingCache<ItemStack[], Optional<CraftingRecipe>> RECIPES_FROM_MATRIX = Caffeine.newBuilder()
.maximumSize(2048L)
.build(
matrix -> RECIPES.values().stream().filter(recipe -> recipe.test(matrix)).findFirst()
);

View File

@@ -57,6 +57,23 @@ public class GroupedTestableItems implements TestableItem {
throw new IllegalStateException("Empty group of children!");
}
/**
* Get matching child for an ItemStack.
*
* @param itemStack The ItemStack.
* @return The matching child, or null if the item matches nothing.
*/
@Nullable
public TestableItem getMatchingChild(@NotNull final ItemStack itemStack) {
for (TestableItem child : children) {
if (child.matches(itemStack)) {
return child;
}
}
return null;
}
/**
* Get the children.
*

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.recipe.parts;
import com.willfp.eco.core.items.Items;
import com.willfp.eco.core.items.TestableItem;
import org.apache.commons.lang.Validate;
import org.bukkit.Material;
@@ -35,7 +36,13 @@ public class MaterialTestableItem implements TestableItem {
*/
@Override
public boolean matches(@Nullable final ItemStack itemStack) {
return itemStack != null && itemStack.getType() == material;
boolean simpleMatches = itemStack != null && itemStack.getType() == material;
if (!simpleMatches) {
return false;
}
return !Items.isCustomItem(itemStack);
}
@Override

View File

@@ -7,7 +7,9 @@ import com.willfp.eco.core.Prerequisite;
import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.recipe.Recipes;
import com.willfp.eco.core.recipe.parts.EmptyTestableItem;
import com.willfp.eco.core.recipe.parts.GroupedTestableItems;
import com.willfp.eco.core.recipe.parts.TestableStack;
import com.willfp.eco.util.ListUtils;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
@@ -66,9 +68,10 @@ public final class ShapedCraftingRecipe extends PluginDependent<EcoPlugin> imple
@Override
public boolean test(@NotNull final ItemStack[] matrix) {
List<ItemStack> dynamicMatrix = Arrays.asList(matrix);
boolean matches = true;
for (int i = 0; i < 9; i++) {
if (!parts.get(i).matches(matrix[i])) {
if (!parts.get(i).matches(ListUtils.getOrNull(dynamicMatrix, i))) {
matches = false;
}
}
@@ -102,23 +105,38 @@ public final class ShapedCraftingRecipe extends PluginDependent<EcoPlugin> imple
}
char character = String.valueOf(i).toCharArray()[0];
ItemStack item = parts.get(i).getItem();
if (parts.get(i) instanceof TestableStack) {
ItemMeta meta = item.getItemMeta();
assert meta != null;
List<String> lore = meta.hasLore() ? meta.getLore() : new ArrayList<>();
assert lore != null;
lore.add("");
String add = Eco.getHandler().getEcoPlugin().getLangYml().getFormattedString("multiple-in-craft");
add = add.replace("%amount%", String.valueOf(item.getAmount()));
lore.add(add);
meta.setLore(lore);
item.setItemMeta(meta);
List<TestableItem> items = new ArrayList<>();
if (parts.get(i) instanceof GroupedTestableItems group) {
items.addAll(group.getChildren());
} else {
items.add(parts.get(i));
}
displayedRecipe.setIngredient(character, new RecipeChoice.ExactChoice(item));
List<ItemStack> displayedItems = new ArrayList<>();
for (TestableItem testableItem : items) {
if (testableItem instanceof TestableStack) {
ItemStack item = testableItem.getItem().clone();
ItemMeta meta = item.getItemMeta();
assert meta != null;
List<String> lore = meta.hasLore() ? meta.getLore() : new ArrayList<>();
assert lore != null;
lore.add("");
String add = Eco.getHandler().getEcoPlugin().getLangYml().getFormattedString("multiple-in-craft");
add = add.replace("%amount%", String.valueOf(item.getAmount()));
lore.add(add);
meta.setLore(lore);
item.setItemMeta(meta);
displayedItems.add(item);
} else {
displayedItems.add(testableItem.getItem());
}
}
displayedRecipe.setIngredient(character, new RecipeChoice.ExactChoice(displayedItems));
}
if (Prerequisite.HAS_1_18.isMet() && !Prerequisite.HAS_PAPER.isMet()) {

View File

@@ -1,39 +0,0 @@
package com.willfp.eco.core.requirement;
import com.willfp.eco.core.Eco;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* A requirement is a defined goal that a player must meet.
*
* @deprecated No typing, weak definitions, and not an API component. Shouldn't be in eco.
*/
@ApiStatus.ScheduledForRemoval(inVersion = "6.27.0")
@Deprecated(since = "6.24.0", forRemoval = true)
public abstract class Requirement {
/**
* Create a new requirement.
*/
protected Requirement() {
}
/**
* Test if the player meets the requirement.
*
* @param player The player.
* @param args The arguments.
* @return The requirement.
*/
public abstract boolean doesPlayerMeet(@NotNull Player player,
@NotNull List<String> args);
static {
Eco.getHandler().getEcoPlugin().getLogger().severe("Loading for-removal Requirements system! This will throw an error once 6.27.0 is released."
+ "Make sure you're running the latest version of all your plugins!");
}
}

View File

@@ -1,69 +0,0 @@
package com.willfp.eco.core.requirement;
import com.willfp.eco.core.Eco;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Contains methods and fields pertaining to requirements.
*
* @deprecated See {@link Requirement}.
*/
@ApiStatus.ScheduledForRemoval(inVersion = "6.27.0")
@Deprecated(since = "6.24.0", forRemoval = true)
@SuppressWarnings("removal")
public final class Requirements {
/**
* Requires a player to have a permission.
*/
public static final Requirement HAS_PERMISSION = new com.willfp.eco.core.requirement.impl.RequirementHasPermission();
/**
* Placeholder equals value.
*/
public static final Requirement PLACEHOLDER_EQUALS = new com.willfp.eco.core.requirement.impl.RequirementPlaceholderEquals();
/**
* Numeric placeholder greater than value.
*/
public static final Requirement PLACEHOLDER_GREATER_THAN = new com.willfp.eco.core.requirement.impl.RequirementPlaceholderGreaterThan();
/**
* Numeric placeholder less than value.
*/
public static final Requirement PLACEHOLDER_LESS_THAN = new com.willfp.eco.core.requirement.impl.RequirementPlaceholderLessThan();
/**
* Get Requirements matching ID.
*
* @param name The ID to search for.
* @return The matching Requirements.
*/
public static Requirement getByID(@NotNull final String name) {
return switch (name.toLowerCase()) {
case "has-permission" -> HAS_PERMISSION;
case "placeholder-equals" -> PLACEHOLDER_EQUALS;
case "placeholder-greater-than" -> PLACEHOLDER_GREATER_THAN;
case "placeholder-less-than" -> PLACEHOLDER_LESS_THAN;
default -> new Requirement() {
@Override
public boolean doesPlayerMeet(@NotNull final Player player,
@NotNull final List<String> args) {
return true;
}
};
};
}
private Requirements() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
static {
Eco.getHandler().getEcoPlugin().getLogger().severe("Loading for-removal Requirements system! This will throw an error once 6.27.0 is released."
+ "Make sure you're running the latest version of all your plugins!");
}
}

View File

@@ -1,34 +0,0 @@
package com.willfp.eco.core.requirement.impl;
import com.willfp.eco.core.Eco;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Moved to API in 6.26.0 after being marked for removal. This class should never be referenced, it
* was moved back to the API in order to remove backend components related to Requirements.
*
* @deprecated No typing, weak definitions, and not an API component. Shouldn't be in eco.
*/
@SuppressWarnings("removal")
@ApiStatus.ScheduledForRemoval(inVersion = "6.27.0")
@Deprecated(since = "6.26.0", forRemoval = true)
public class RequirementHasPermission extends com.willfp.eco.core.requirement.Requirement {
@Override
public boolean doesPlayerMeet(@NotNull final Player player,
@NotNull final List<String> args) {
if (args.isEmpty()) {
return false;
}
return player.hasPermission(args.get(0));
}
static {
Eco.getHandler().getEcoPlugin().getLogger().severe("Loading for-removal Requirements system! This will throw an error once 6.27.0 is released."
+ "Make sure you're running the latest version of all your plugins!");
}
}

View File

@@ -1,35 +0,0 @@
package com.willfp.eco.core.requirement.impl;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Moved to API in 6.26.0 after being marked for removal. This class should never be referenced, it
* was moved back to the API in order to remove backend components related to Requirements.
*
* @deprecated No typing, weak definitions, and not an API component. Shouldn't be in eco.
*/
@SuppressWarnings("removal")
@ApiStatus.ScheduledForRemoval(inVersion = "6.27.0")
@Deprecated(since = "6.26.0", forRemoval = true)
public class RequirementPlaceholderEquals extends com.willfp.eco.core.requirement.Requirement {
@Override
public boolean doesPlayerMeet(@NotNull final Player player,
@NotNull final List<String> args) {
if (args.size() < 2) {
return false;
}
return PlaceholderManager.translatePlaceholders(args.get(0), player).equalsIgnoreCase(args.get(1));
}
static {
Eco.getHandler().getEcoPlugin().getLogger().severe("Loading for-removal Requirements system! This will throw an error once 6.27.0 is released."
+ "Make sure you're running the latest version of all your plugins!");
}
}

View File

@@ -1,49 +0,0 @@
package com.willfp.eco.core.requirement.impl;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Moved to API in 6.26.0 after being marked for removal. This class should never be referenced, it
* was moved back to the API in order to remove backend components related to Requirements.
*
* @deprecated No typing, weak definitions, and not an API component. Shouldn't be in eco.
*/
@SuppressWarnings("removal")
@ApiStatus.ScheduledForRemoval(inVersion = "6.27.0")
@Deprecated(since = "6.26.0", forRemoval = true)
public class RequirementPlaceholderGreaterThan extends com.willfp.eco.core.requirement.Requirement {
@Override
public boolean doesPlayerMeet(@NotNull final Player player,
@NotNull final List<String> args) {
if (args.size() < 2) {
return false;
}
double actual;
try {
actual = Double.parseDouble(PlaceholderManager.translatePlaceholders(args.get(0), player));
} catch (NumberFormatException e) {
return false;
}
double expected;
try {
expected = Double.parseDouble(args.get(1));
} catch (NumberFormatException e) {
return false;
}
return actual >= expected;
}
static {
Eco.getHandler().getEcoPlugin().getLogger().severe("Loading for-removal Requirements system! This will throw an error once 6.27.0 is released."
+ "Make sure you're running the latest version of all your plugins!");
}
}

View File

@@ -1,49 +0,0 @@
package com.willfp.eco.core.requirement.impl;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Moved to API in 6.26.0 after being marked for removal. This class should never be referenced, it
* was moved back to the API in order to remove backend components related to Requirements.
*
* @deprecated No typing, weak definitions, and not an API component. Shouldn't be in eco.
*/
@SuppressWarnings("removal")
@ApiStatus.ScheduledForRemoval(inVersion = "6.27.0")
@Deprecated(since = "6.26.0", forRemoval = true)
public class RequirementPlaceholderLessThan extends com.willfp.eco.core.requirement.Requirement {
@Override
public boolean doesPlayerMeet(@NotNull final Player player,
@NotNull final List<String> args) {
if (args.size() < 2) {
return false;
}
double actual;
try {
actual = Double.parseDouble(PlaceholderManager.translatePlaceholders(args.get(0), player));
} catch (NumberFormatException e) {
return false;
}
double expected;
try {
expected = Double.parseDouble(args.get(1));
} catch (NumberFormatException e) {
return false;
}
return actual < expected;
}
static {
Eco.getHandler().getEcoPlugin().getLogger().severe("Loading for-removal Requirements system! This will throw an error once 6.27.0 is released."
+ "Make sure you're running the latest version of all your plugins!");
}
}

View File

@@ -0,0 +1,24 @@
package com.willfp.eco.core.serialization;
import com.willfp.eco.core.config.interfaces.Config;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Deserialize objects from configs.
* <p>
* Deserializers should <b>never</b> throw errors due to invalid configs,
* all edge cases must be covered, and all failures must be encapsulated as null.
*
* @param <T> The type of object to deserialize.
*/
public interface ConfigDeserializer<T> {
/**
* Deserialize a config to an object.
*
* @param config The config.
* @return The object, or null if invalid.
*/
@Nullable
T deserialize(@NotNull Config config);
}

View File

@@ -0,0 +1,20 @@
package com.willfp.eco.core.serialization;
import com.willfp.eco.core.config.interfaces.Config;
import org.jetbrains.annotations.NotNull;
/**
* Serialize objects to configs.
*
* @param <T> The type of object to serialize.
*/
public interface ConfigSerializer<T> {
/**
* Serialize an object to a config.
*
* @param obj The object.
* @return The config.
*/
@NotNull
Config serialize(@NotNull T obj);
}

View File

@@ -0,0 +1,13 @@
package com.willfp.eco.core.serialization;
import org.bukkit.Keyed;
/**
* Deserializer with a key.
*
* @param <T> The type of object to deserialize.
* @see ConfigDeserializer
*/
public interface KeyedDeserializer<T> extends ConfigDeserializer<T>, Keyed {
}

View File

@@ -0,0 +1,13 @@
package com.willfp.eco.core.serialization;
import org.bukkit.Keyed;
/**
* Serializer with a key.
*
* @param <T> The type of object to serialize.
* @see ConfigSerializer
*/
public interface KeyedSerializer<T> extends ConfigSerializer<T>, Keyed {
}

View File

@@ -1,6 +1,5 @@
package com.willfp.eco.util;
import org.apache.commons.lang.Validate;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
@@ -9,28 +8,16 @@ import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
/**
* Utilities / API methods for blocks.
*/
public final class BlockUtils {
/**
* If the meta set function has been set.
*/
private static boolean initialized = false;
/**
* The block break function.
*/
private static BiConsumer<Player, Block> blockBreakConsumer = null;
private static Set<Block> getNearbyBlocks(@NotNull final Block start,
@NotNull final List<Material> allowedMaterials,
@NotNull final Set<Block> blocks,
@@ -75,12 +62,11 @@ public final class BlockUtils {
*
* @param player The player to break the block as.
* @param block The block to break.
* @deprecated Added into spigot API in 1.17.1
*/
@Deprecated(since = "6.26.2", forRemoval = true)
public static void breakBlock(@NotNull final Player player,
@NotNull final Block block) {
Validate.isTrue(initialized, "Must be initialized!");
Validate.notNull(blockBreakConsumer, "Must be initialized!");
Location location = block.getLocation();
World world = location.getWorld();
assert world != null;
@@ -89,7 +75,7 @@ public final class BlockUtils {
return;
}
blockBreakConsumer.accept(player, block);
player.breakBlock(block);
}
/**
@@ -107,20 +93,6 @@ public final class BlockUtils {
);
}
/**
* Initialize the block break function.
*
* @param function The function.
*/
@ApiStatus.Internal
public static void initialize(@NotNull final BiConsumer<Player, Block> function) {
Validate.isTrue(!initialized, "Already initialized!");
blockBreakConsumer = function;
initialized = true;
}
private BlockUtils() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}

View File

@@ -5,11 +5,10 @@ import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonSyntaxException;
import com.willfp.eco.core.Prerequisite;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.md_5.bungee.api.ChatColor;
@@ -324,17 +323,16 @@ public final class StringUtils {
private static String processFormatting(@NotNull final String message) {
String processedMessage = message;
// Run MiniMessage first so it doesn't complain
processedMessage = translateMiniMessage(processedMessage);
processedMessage = ChatColor.translateAlternateColorCodes('&', processedMessage);
processedMessage = translateGradients(processedMessage);
processedMessage = translateHexColorCodes(processedMessage);
if (Prerequisite.HAS_PAPER.isMet()) {
processedMessage = translateMiniMessage(processedMessage);
}
return processedMessage;
}
private static String translateMiniMessage(@NotNull final String message) {
return LEGACY_COMPONENT_SERIALIZER.serialize(MiniMessage.get().parse(message));
return Eco.getHandler().formatMiniMessage(message);
}
private static String translateHexColorCodes(@NotNull final String message) {

View File

@@ -0,0 +1,12 @@
@file:JvmName("EntityExtensions")
package com.willfp.eco.core.entities
import com.willfp.eco.core.entities.ai.EntityController
import org.bukkit.entity.Mob
/**
* @see EntityController.getFor
*/
val <T : Mob> T.controller: EntityController<T>
get() = EntityController.getFor(this)

View File

@@ -5,7 +5,7 @@ dependencies {
compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT'
compileOnly 'me.clip:placeholderapi:2.10.10'
compileOnly 'org.reflections:reflections:0.9.12'
compileOnly 'net.kyori:adventure-text-minimessage:4.1.0-SNAPSHOT'
compileOnly 'net.kyori:adventure-platform-bukkit:4.0.0'
compileOnly 'net.kyori:adventure-text-minimessage:4.10.0'
compileOnly 'net.kyori:adventure-platform-bukkit:4.1.0'
compileOnly 'org.objenesis:objenesis:3.2'
}

View File

@@ -1,18 +0,0 @@
package com.willfp.eco.internal.fast
import com.willfp.eco.core.fast.FastItemStack
import org.bukkit.inventory.ItemFlag
import org.bukkit.inventory.ItemStack
abstract class EcoFastItemStack<T: Any>(
val handle: T,
val bukkit: ItemStack
) : FastItemStack {
override fun unwrap(): ItemStack {
return bukkit
}
fun getBitModifier(hideFlag: ItemFlag): Int {
return 1 shl hideFlag.ordinal
}
}

View File

@@ -0,0 +1,10 @@
plugins {
id("io.papermc.paperweight.userdev") version "1.3.4"
}
group = "com.willfp"
version = rootProject.version
dependencies {
paperDevBundle("1.17.1-R0.1-SNAPSHOT")
}

View File

@@ -0,0 +1,74 @@
package com.willfp.eco.internal.spigot.proxy.common
import com.willfp.eco.core.entities.ai.EntityGoal
import com.willfp.eco.core.entities.ai.TargetGoal
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.TargetGoalFactory
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import org.bukkit.NamespacedKey
import org.bukkit.entity.Mob
import org.bukkit.inventory.ItemStack
private lateinit var impl: CommonsProvider
val NBT_TAG_STRING by lazy { impl.nbtTagString }
fun Mob.toPathfinderMob(): PathfinderMob? =
impl.toPathfinderMob(this)
fun NamespacedKey.toResourceLocation(): ResourceLocation =
impl.toResourceLocation(this)
fun ItemStack.asNMSStack(): net.minecraft.world.item.ItemStack =
impl.asNMSStack(this)
fun net.minecraft.world.item.ItemStack.asBukkitStack(): ItemStack =
impl.asBukkitStack(this)
fun ItemStack.mergeIfNeeded(nmsStack: net.minecraft.world.item.ItemStack) =
impl.mergeIfNeeded(this, nmsStack)
fun LivingEntity.toBukkitEntity(): org.bukkit.entity.LivingEntity? =
impl.toBukkitEntity(this)
fun <T : EntityGoal<*>> T.getVersionSpecificEntityGoalFactory(): EntityGoalFactory<T>? =
impl.getVersionSpecificEntityGoalFactory(this)
fun <T : TargetGoal<*>> T.getVersionSpecificEntityGoalFactory(): TargetGoalFactory<T>? =
impl.getVersionSpecificTargetGoalFactory(this)
interface CommonsProvider {
val nbtTagString: Int
fun toPathfinderMob(mob: Mob): PathfinderMob?
fun toResourceLocation(namespacedKey: NamespacedKey): ResourceLocation
fun asNMSStack(itemStack: ItemStack): net.minecraft.world.item.ItemStack
fun asBukkitStack(itemStack: net.minecraft.world.item.ItemStack): ItemStack
fun mergeIfNeeded(itemStack: ItemStack, nmsStack: net.minecraft.world.item.ItemStack)
fun toBukkitEntity(entity: LivingEntity): org.bukkit.entity.LivingEntity?
fun <T : EntityGoal<*>> getVersionSpecificEntityGoalFactory(goal: T): EntityGoalFactory<T>? {
return null
}
fun <T : TargetGoal<*>> getVersionSpecificTargetGoalFactory(goal: T): TargetGoalFactory<T>? {
return null
}
companion object {
fun setIfNeeded(provider: CommonsProvider) {
if (::impl.isInitialized) {
return
}
impl = provider
}
}
}

View File

@@ -0,0 +1,86 @@
package com.willfp.eco.internal.spigot.proxy.common.ai
import com.willfp.eco.core.entities.ai.CustomGoal
import com.willfp.eco.core.entities.ai.GoalFlag
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
import java.util.EnumSet
object CustomGoalFactory : EntityGoalFactory<CustomGoal<*>>, TargetGoalFactory<CustomGoal<*>> {
override fun create(apiGoal: CustomGoal<*>, entity: PathfinderMob): Goal {
return NMSCustomGoal(apiGoal, entity)
}
override fun isGoalOfType(goal: Goal): Boolean = goal is NMSCustomGoal<*>
fun isGoalOfType(goal: Goal, apiGoal: CustomGoal<*>): Boolean {
if (goal !is NMSCustomGoal<*>) return false
// ew
return goal.customEntityGoal::class.java.name == apiGoal::class.java.name
}
}
private fun Collection<Goal.Flag>.toEcoFlags(): Collection<GoalFlag> {
return this.mapNotNull {
when (it) {
Goal.Flag.JUMP -> GoalFlag.JUMP
Goal.Flag.LOOK -> GoalFlag.LOOK
Goal.Flag.MOVE -> GoalFlag.MOVE
Goal.Flag.TARGET -> GoalFlag.TARGET
else -> null
}
}
}
private fun Collection<GoalFlag>.toNMSFlags(): Collection<Goal.Flag> {
return this.mapNotNull {
when (it) {
GoalFlag.JUMP -> Goal.Flag.JUMP
GoalFlag.LOOK -> Goal.Flag.LOOK
GoalFlag.MOVE -> Goal.Flag.MOVE
GoalFlag.TARGET -> Goal.Flag.TARGET
else -> null
}
}
}
private class NMSCustomGoal<T : org.bukkit.entity.Mob>(
val customEntityGoal: CustomGoal<T>,
entity: PathfinderMob
) : Goal() {
init {
@Suppress("UNCHECKED_CAST")
customEntityGoal.initialize(entity.bukkitMob as T)
this.setFlags(EnumSet.copyOf(customEntityGoal.flags.toNMSFlags()))
}
override fun canUse(): Boolean {
return customEntityGoal.canUse()
}
override fun tick() {
customEntityGoal.tick()
}
override fun start() {
customEntityGoal.start()
}
override fun stop() {
customEntityGoal.stop()
}
override fun canContinueToUse(): Boolean {
return customEntityGoal.canContinueToUse()
}
override fun isInterruptable(): Boolean {
return customEntityGoal.isInterruptable
}
override fun setFlags(controls: EnumSet<Flag>) {
super.setFlags(controls)
customEntityGoal.flags = EnumSet.copyOf(controls.toEcoFlags())
}
}

View File

@@ -0,0 +1,93 @@
package com.willfp.eco.internal.spigot.proxy.common.ai
import com.willfp.eco.core.entities.ai.CustomGoal
import com.willfp.eco.core.entities.ai.EntityController
import com.willfp.eco.core.entities.ai.EntityGoal
import com.willfp.eco.core.entities.ai.TargetGoal
import com.willfp.eco.internal.spigot.proxy.common.toPathfinderMob
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
import org.bukkit.entity.Mob
class EcoEntityController<T : Mob>(
private val handle: T
) : EntityController<T> {
override fun addEntityGoal(priority: Int, goal: EntityGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
nms.goalSelector.addGoal(
priority,
goal.getGoalFactory()?.create(goal, nms) ?: return this
)
return this
}
override fun removeEntityGoal(goal: EntityGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
val predicate: (Goal) -> Boolean = if (goal is CustomGoal<*>) {
{ CustomGoalFactory.isGoalOfType(it, goal) }
} else {
{ goal.getGoalFactory()?.isGoalOfType(it) == true }
}
for (wrapped in nms.goalSelector.availableGoals.toSet()) {
if (predicate(wrapped.goal)) {
nms.goalSelector.removeGoal(wrapped.goal)
}
}
return this
}
override fun clearEntityGoals(): EntityController<T> {
val nms = getNms() ?: return this
nms.goalSelector.availableGoals.clear()
return this
}
override fun addTargetGoal(priority: Int, goal: TargetGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
nms.targetSelector.addGoal(
priority, goal.getGoalFactory()?.create(goal, nms) ?: return this
)
nms.targetSelector
return this
}
override fun removeTargetGoal(goal: TargetGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
val predicate: (Goal) -> Boolean = if (goal is CustomGoal<*>) {
{ CustomGoalFactory.isGoalOfType(it, goal) }
} else {
{ goal.getGoalFactory()?.isGoalOfType(it) == true }
}
for (wrapped in nms.targetSelector.availableGoals.toSet()) {
if (predicate(wrapped.goal)) {
nms.targetSelector.removeGoal(wrapped.goal)
}
}
return this
}
override fun clearTargetGoals(): EntityController<T> {
val nms = getNms() ?: return this
nms.targetSelector.availableGoals.clear()
return this
}
private fun getNms(): PathfinderMob? {
return handle.toPathfinderMob()
}
override fun getEntity(): T {
return handle
}
}

View File

@@ -0,0 +1,139 @@
package com.willfp.eco.internal.spigot.proxy.common.ai
import com.willfp.eco.core.entities.ai.CustomGoal
import com.willfp.eco.core.entities.ai.EntityGoal
import com.willfp.eco.core.entities.ai.entity.EntityGoalAvoidEntity
import com.willfp.eco.core.entities.ai.entity.EntityGoalBreakDoors
import com.willfp.eco.core.entities.ai.entity.EntityGoalBreatheAir
import com.willfp.eco.core.entities.ai.entity.EntityGoalBreed
import com.willfp.eco.core.entities.ai.entity.EntityGoalCatLieOnBed
import com.willfp.eco.core.entities.ai.entity.EntityGoalCatSitOnBed
import com.willfp.eco.core.entities.ai.entity.EntityGoalEatGrass
import com.willfp.eco.core.entities.ai.entity.EntityGoalFleeSun
import com.willfp.eco.core.entities.ai.entity.EntityGoalFloat
import com.willfp.eco.core.entities.ai.entity.EntityGoalFollowBoats
import com.willfp.eco.core.entities.ai.entity.EntityGoalFollowMobs
import com.willfp.eco.core.entities.ai.entity.EntityGoalIllusionerBlindnessSpell
import com.willfp.eco.core.entities.ai.entity.EntityGoalIllusionerMirrorSpell
import com.willfp.eco.core.entities.ai.entity.EntityGoalInteract
import com.willfp.eco.core.entities.ai.entity.EntityGoalLeapAtTarget
import com.willfp.eco.core.entities.ai.entity.EntityGoalLookAtPlayer
import com.willfp.eco.core.entities.ai.entity.EntityGoalMeleeAttack
import com.willfp.eco.core.entities.ai.entity.EntityGoalMoveBackToVillage
import com.willfp.eco.core.entities.ai.entity.EntityGoalMoveThroughVillage
import com.willfp.eco.core.entities.ai.entity.EntityGoalMoveTowardsRestriction
import com.willfp.eco.core.entities.ai.entity.EntityGoalMoveTowardsTarget
import com.willfp.eco.core.entities.ai.entity.EntityGoalOcelotAttack
import com.willfp.eco.core.entities.ai.entity.EntityGoalOpenDoors
import com.willfp.eco.core.entities.ai.entity.EntityGoalPanic
import com.willfp.eco.core.entities.ai.entity.EntityGoalRandomLookAround
import com.willfp.eco.core.entities.ai.entity.EntityGoalRandomStroll
import com.willfp.eco.core.entities.ai.entity.EntityGoalRandomSwimming
import com.willfp.eco.core.entities.ai.entity.EntityGoalRangedAttack
import com.willfp.eco.core.entities.ai.entity.EntityGoalRangedBowAttack
import com.willfp.eco.core.entities.ai.entity.EntityGoalRangedCrossbowAttack
import com.willfp.eco.core.entities.ai.entity.EntityGoalRestrictSun
import com.willfp.eco.core.entities.ai.entity.EntityGoalStrollThroughVillage
import com.willfp.eco.core.entities.ai.entity.EntityGoalTempt
import com.willfp.eco.core.entities.ai.entity.EntityGoalTryFindWater
import com.willfp.eco.core.entities.ai.entity.EntityGoalUseItem
import com.willfp.eco.core.entities.ai.entity.EntityGoalWaterAvoidingRandomFlying
import com.willfp.eco.core.entities.ai.entity.EntityGoalWaterAvoidingRandomStroll
import com.willfp.eco.core.entities.ai.entity.EntityGoalWolfBeg
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.AvoidEntityGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.BreakDoorsGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.BreatheAirGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.BreedGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.CatLieOnBedGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.CatSitOnBedGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.EatGrassGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.FleeSunGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.FloatGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.FollowBoatsGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.FollowMobsGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.IllusionerBlindnessSpellGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.IllusionerMirrorSpellGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.InteractGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.LeapAtTargetGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.LookAtPlayerGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.MeleeAttackGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.MoveBackToVillageGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.MoveThroughVillageGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.MoveTowardsRestrictionGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.MoveTowardsTargetGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.OcelotAttackGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.OpenDoorsGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.PanicGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.RandomLookAroundGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.RandomStrollGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.RandomSwimmingGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.RangedAttackGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.RangedBowAttackGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.RangedCrossbowAttackGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.RestrictSunGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.StrollThroughVillageGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.TemptGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.TryFindWaterGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.UseItemGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.WaterAvoidingRandomFlyingGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.WaterAvoidingRandomStrollGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.entity.WolfBegGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.getVersionSpecificEntityGoalFactory
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
fun <T : EntityGoal<*>> T.getGoalFactory(): EntityGoalFactory<T>? {
val versionSpecific = this.getVersionSpecificEntityGoalFactory()
if (versionSpecific != null) {
return versionSpecific
}
@Suppress("UNCHECKED_CAST")
return when (this) {
is EntityGoalAvoidEntity -> AvoidEntityGoalFactory
is EntityGoalBreakDoors -> BreakDoorsGoalFactory
is EntityGoalBreatheAir -> BreatheAirGoalFactory
is EntityGoalEatGrass -> EatGrassGoalFactory
is EntityGoalFleeSun -> FleeSunGoalFactory
is EntityGoalFloat -> FloatGoalFactory
is EntityGoalFollowBoats -> FollowBoatsGoalFactory
is EntityGoalFollowMobs -> FollowMobsGoalFactory
is EntityGoalInteract -> InteractGoalFactory
is EntityGoalLeapAtTarget -> LeapAtTargetGoalFactory
is EntityGoalLookAtPlayer -> LookAtPlayerGoalFactory
is EntityGoalMeleeAttack -> MeleeAttackGoalFactory
is EntityGoalMoveBackToVillage -> MoveBackToVillageGoalFactory
is EntityGoalMoveThroughVillage -> MoveThroughVillageGoalFactory
is EntityGoalMoveTowardsRestriction -> MoveTowardsRestrictionGoalFactory
is EntityGoalMoveTowardsTarget -> MoveTowardsTargetGoalFactory
is EntityGoalOcelotAttack -> OcelotAttackGoalFactory
is EntityGoalOpenDoors -> OpenDoorsGoalFactory
is EntityGoalPanic -> PanicGoalFactory
is EntityGoalRandomLookAround -> RandomLookAroundGoalFactory
is EntityGoalRandomStroll -> RandomStrollGoalFactory
is EntityGoalRandomSwimming -> RandomSwimmingGoalFactory
is EntityGoalRangedAttack -> RangedAttackGoalFactory
is EntityGoalRangedBowAttack -> RangedBowAttackGoalFactory
is EntityGoalRangedCrossbowAttack -> RangedCrossbowAttackGoalFactory
is EntityGoalRestrictSun -> RestrictSunGoalFactory
is EntityGoalStrollThroughVillage -> StrollThroughVillageGoalFactory
is EntityGoalTempt -> TemptGoalFactory
is EntityGoalTryFindWater -> TryFindWaterGoalFactory
is EntityGoalUseItem -> UseItemGoalFactory
is EntityGoalWaterAvoidingRandomFlying -> WaterAvoidingRandomFlyingGoalFactory
is EntityGoalWaterAvoidingRandomStroll -> WaterAvoidingRandomStrollGoalFactory
is EntityGoalWolfBeg -> WolfBegGoalFactory
is EntityGoalBreed -> BreedGoalFactory
is EntityGoalCatSitOnBed -> CatSitOnBedGoalFactory
is EntityGoalCatLieOnBed -> CatLieOnBedGoalFactory
is EntityGoalIllusionerBlindnessSpell -> IllusionerBlindnessSpellGoalFactory
is EntityGoalIllusionerMirrorSpell -> IllusionerMirrorSpellGoalFactory
is CustomGoal<*> -> CustomGoalFactory
else -> null
} as EntityGoalFactory<T>?
}
interface EntityGoalFactory<T : EntityGoal<*>> {
fun create(apiGoal: T, entity: PathfinderMob): Goal?
fun isGoalOfType(goal: Goal): Boolean
}

View File

@@ -0,0 +1,46 @@
package com.willfp.eco.internal.spigot.proxy.common.ai
import com.willfp.eco.core.entities.ai.CustomGoal
import com.willfp.eco.core.entities.ai.TargetGoal
import com.willfp.eco.core.entities.ai.target.TargetGoalHurtBy
import com.willfp.eco.core.entities.ai.target.TargetGoalNearestAttackable
import com.willfp.eco.core.entities.ai.target.TargetGoalNearestAttackableWitch
import com.willfp.eco.core.entities.ai.target.TargetGoalNearestHealableRaider
import com.willfp.eco.core.entities.ai.target.TargetGoalNonTameRandom
import com.willfp.eco.core.entities.ai.target.TargetGoalOwnerHurtBy
import com.willfp.eco.core.entities.ai.target.TargetGoalOwnerTarget
import com.willfp.eco.internal.spigot.proxy.common.ai.target.HurtByGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.target.NearestAttackableGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.target.NearestAttackableWitchGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.target.NearestHealableRaiderGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.target.NonTameRandomGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.target.OwnerHurtByGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.target.OwnerTargetGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.getVersionSpecificEntityGoalFactory
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
fun <T : TargetGoal<*>> T.getGoalFactory(): TargetGoalFactory<T>? {
val versionSpecific = this.getVersionSpecificEntityGoalFactory()
if (versionSpecific != null) {
return versionSpecific
}
@Suppress("UNCHECKED_CAST")
return when (this) {
is TargetGoalHurtBy -> HurtByGoalFactory
is TargetGoalNearestAttackable -> NearestAttackableGoalFactory
is TargetGoalNearestAttackableWitch -> NearestAttackableWitchGoalFactory
is TargetGoalNearestHealableRaider -> NearestHealableRaiderGoalFactory
is TargetGoalNonTameRandom -> NonTameRandomGoalFactory
is TargetGoalOwnerHurtBy -> OwnerHurtByGoalFactory
is TargetGoalOwnerTarget -> OwnerTargetGoalFactory
is CustomGoal<*> -> CustomGoalFactory
else -> null
} as TargetGoalFactory<T>?
}
interface TargetGoalFactory<T : TargetGoal<*>> {
fun create(apiGoal: T, entity: PathfinderMob): Goal?
fun isGoalOfType(goal: Goal): Boolean
}

View File

@@ -0,0 +1,23 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalAvoidEntity
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.toBukkitEntity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.AvoidEntityGoal
import net.minecraft.world.entity.ai.goal.Goal
object AvoidEntityGoalFactory : EntityGoalFactory<EntityGoalAvoidEntity> {
override fun create(apiGoal: EntityGoalAvoidEntity, entity: PathfinderMob): Goal {
return AvoidEntityGoal(
entity,
LivingEntity::class.java,
apiGoal.distance.toFloat(),
apiGoal.slowSpeed,
apiGoal.fastSpeed
) { apiGoal.entity.test(it.toBukkitEntity()) }
}
override fun isGoalOfType(goal: Goal) = goal is AvoidEntityGoal<*>
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalBreakDoors
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.BreakDoorGoal
import net.minecraft.world.entity.ai.goal.Goal
object BreakDoorsGoalFactory : EntityGoalFactory<EntityGoalBreakDoors> {
override fun create(apiGoal: EntityGoalBreakDoors, entity: PathfinderMob): Goal {
return BreakDoorGoal(
entity,
apiGoal.ticks
) { true }
}
override fun isGoalOfType(goal: Goal) = goal is BreakDoorGoal
}

View File

@@ -0,0 +1,17 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalBreatheAir
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.BreathAirGoal
import net.minecraft.world.entity.ai.goal.Goal
object BreatheAirGoalFactory : EntityGoalFactory<EntityGoalBreatheAir> {
override fun create(apiGoal: EntityGoalBreatheAir, entity: PathfinderMob): Goal {
return BreathAirGoal(
entity
)
}
override fun isGoalOfType(goal: Goal) = goal is BreathAirGoal
}

View File

@@ -0,0 +1,19 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalBreed
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.BreedGoal
import net.minecraft.world.entity.ai.goal.Goal
import net.minecraft.world.entity.animal.Animal
object BreedGoalFactory : EntityGoalFactory<EntityGoalBreed> {
override fun create(apiGoal: EntityGoalBreed, entity: PathfinderMob): Goal? {
return BreedGoal(
entity as? Animal ?: return null,
apiGoal.speed
)
}
override fun isGoalOfType(goal: Goal) = goal is BreedGoal
}

View File

@@ -0,0 +1,20 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalCatLieOnBed
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.CatLieOnBedGoal
import net.minecraft.world.entity.ai.goal.Goal
import net.minecraft.world.entity.animal.Cat
object CatLieOnBedGoalFactory : EntityGoalFactory<EntityGoalCatLieOnBed> {
override fun create(apiGoal: EntityGoalCatLieOnBed, entity: PathfinderMob): Goal? {
return CatLieOnBedGoal(
entity as? Cat ?: return null,
apiGoal.speed,
apiGoal.range
)
}
override fun isGoalOfType(goal: Goal) = goal is CatLieOnBedGoal
}

View File

@@ -0,0 +1,19 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalCatSitOnBed
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.CatSitOnBlockGoal
import net.minecraft.world.entity.ai.goal.Goal
import net.minecraft.world.entity.animal.Cat
object CatSitOnBedGoalFactory : EntityGoalFactory<EntityGoalCatSitOnBed> {
override fun create(apiGoal: EntityGoalCatSitOnBed, entity: PathfinderMob): Goal? {
return CatSitOnBlockGoal(
entity as? Cat ?: return null,
apiGoal.speed
)
}
override fun isGoalOfType(goal: Goal) = goal is CatSitOnBlockGoal
}

View File

@@ -0,0 +1,17 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalEatGrass
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.EatBlockGoal
import net.minecraft.world.entity.ai.goal.Goal
object EatGrassGoalFactory : EntityGoalFactory<EntityGoalEatGrass> {
override fun create(apiGoal: EntityGoalEatGrass, entity: PathfinderMob): Goal {
return EatBlockGoal(
entity
)
}
override fun isGoalOfType(goal: Goal) = goal is EatBlockGoal
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalFleeSun
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.FleeSunGoal
import net.minecraft.world.entity.ai.goal.Goal
object FleeSunGoalFactory : EntityGoalFactory<EntityGoalFleeSun> {
override fun create(apiGoal: EntityGoalFleeSun, entity: PathfinderMob): Goal {
return FleeSunGoal(
entity,
apiGoal.speed
)
}
override fun isGoalOfType(goal: Goal) = goal is FleeSunGoal
}

View File

@@ -0,0 +1,17 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalFloat
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.FloatGoal
import net.minecraft.world.entity.ai.goal.Goal
object FloatGoalFactory : EntityGoalFactory<EntityGoalFloat> {
override fun create(apiGoal: EntityGoalFloat, entity: PathfinderMob): Goal {
return FloatGoal(
entity
)
}
override fun isGoalOfType(goal: Goal) = goal is FloatGoal
}

View File

@@ -0,0 +1,17 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalFollowBoats
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.FollowBoatGoal
import net.minecraft.world.entity.ai.goal.Goal
object FollowBoatsGoalFactory : EntityGoalFactory<EntityGoalFollowBoats> {
override fun create(apiGoal: EntityGoalFollowBoats, entity: PathfinderMob): Goal {
return FollowBoatGoal(
entity
)
}
override fun isGoalOfType(goal: Goal) = goal is FollowBoatGoal
}

View File

@@ -0,0 +1,20 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalFollowMobs
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.FollowMobGoal
import net.minecraft.world.entity.ai.goal.Goal
object FollowMobsGoalFactory : EntityGoalFactory<EntityGoalFollowMobs> {
override fun create(apiGoal: EntityGoalFollowMobs, entity: PathfinderMob): Goal {
return FollowMobGoal(
entity,
apiGoal.speed,
apiGoal.minDistance.toFloat(),
apiGoal.maxDistance.toFloat(),
)
}
override fun isGoalOfType(goal: Goal) = goal is FollowMobGoal
}

View File

@@ -0,0 +1,21 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalLeapAtTarget
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.opengoals.IllusionerBlindnessSpellGoal
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
import net.minecraft.world.entity.monster.Illusioner
object IllusionerBlindnessSpellGoalFactory : EntityGoalFactory<EntityGoalLeapAtTarget> {
override fun create(apiGoal: EntityGoalLeapAtTarget, entity: PathfinderMob): Goal? {
if (entity !is Illusioner) return null
return IllusionerBlindnessSpellGoal(
entity
)
}
override fun isGoalOfType(goal: Goal) = goal is IllusionerBlindnessSpellGoal
|| goal::class.java.name.contains("IllusionerBlindnessSpellGoal")
}

View File

@@ -0,0 +1,21 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalLeapAtTarget
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.opengoals.IllusionerMirrorSpellGoal
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
import net.minecraft.world.entity.monster.Illusioner
object IllusionerMirrorSpellGoalFactory : EntityGoalFactory<EntityGoalLeapAtTarget> {
override fun create(apiGoal: EntityGoalLeapAtTarget, entity: PathfinderMob): Goal? {
if (entity !is Illusioner) return null
return IllusionerMirrorSpellGoal(
entity
)
}
override fun isGoalOfType(goal: Goal) = goal is IllusionerMirrorSpellGoal
|| goal::class.java.name.contains("IllusionerMirrorSpellGoal")
}

View File

@@ -0,0 +1,61 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.TestableEntity
import com.willfp.eco.core.entities.ai.entity.EntityGoalInteract
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.toBukkitEntity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
import net.minecraft.world.entity.ai.goal.InteractGoal
import net.minecraft.world.entity.player.Player
object InteractGoalFactory : EntityGoalFactory<EntityGoalInteract> {
override fun create(apiGoal: EntityGoalInteract, entity: PathfinderMob): Goal {
return EnhancedInteractGoal(
entity,
apiGoal.target,
apiGoal.range.toFloat(),
apiGoal.chance.toFloat(),
)
}
override fun isGoalOfType(goal: Goal) = goal is InteractGoal
}
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
private class EnhancedInteractGoal(
mob: PathfinderMob,
private val target: TestableEntity,
range: Float,
chance: Float
) : InteractGoal(
mob,
LivingEntity::class.java,
range,
chance
) {
override fun canUse(): Boolean {
return if (mob.random.nextFloat() >= probability) {
false
} else {
@Suppress("SENSELESS_COMPARISON")
if (mob.target != null) {
lookAt = mob.target
}
val lookAt = if (lookAtType == Player::class.java) {
mob.level.getNearestPlayer(lookAtContext, mob, mob.x, mob.eyeY, mob.z)
} else {
mob.level.getNearestEntity(
mob.level.getEntitiesOfClass(
lookAtType, mob.boundingBox.inflate(
lookDistance.toDouble(), 3.0, lookDistance.toDouble()
)
) { target.matches(it.toBukkitEntity()) }, // Change this line to check with TestableEntity.
lookAtContext, mob, mob.x, mob.eyeY, mob.z
)
}
lookAt != null
}
}
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalLeapAtTarget
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
import net.minecraft.world.entity.ai.goal.LeapAtTargetGoal
object LeapAtTargetGoalFactory : EntityGoalFactory<EntityGoalLeapAtTarget> {
override fun create(apiGoal: EntityGoalLeapAtTarget, entity: PathfinderMob): Goal {
return LeapAtTargetGoal(
entity,
apiGoal.velocity.toFloat()
)
}
override fun isGoalOfType(goal: Goal) = goal is LeapAtTargetGoal
}

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