Compare commits
194 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97fba3e243 | ||
|
|
e47c6387a2 | ||
|
|
00df39097c | ||
|
|
efcb406e9a | ||
|
|
9e92ea6062 | ||
|
|
9dbc25df6f | ||
|
|
bc85c5232e | ||
|
|
2f4783f13e | ||
|
|
614241a058 | ||
|
|
c541adb557 | ||
|
|
a484eecc8f | ||
|
|
3933e38891 | ||
|
|
1d5345b367 | ||
|
|
f8513ff1e9 | ||
|
|
ca9c940b2b | ||
|
|
af198d30f7 | ||
|
|
637b239c66 | ||
|
|
124c059294 | ||
|
|
f1d0bd901d | ||
|
|
20f4bf4e78 | ||
|
|
595bc76294 | ||
|
|
9a66e78dcd | ||
|
|
0fcf229bfb | ||
|
|
d2ffc43b17 | ||
|
|
c50f69b372 | ||
|
|
f5eafafc4c | ||
|
|
39372c9b1a | ||
|
|
4c64f03aa1 | ||
|
|
6bb4e301bc | ||
|
|
827e9fd98a | ||
|
|
c3e9bbbc50 | ||
|
|
7328881977 | ||
|
|
00531d0a04 | ||
|
|
5d4a2b4633 | ||
|
|
28c86460fc | ||
|
|
4a1d714b71 | ||
|
|
4ef16530d3 | ||
|
|
a22ad1b22d | ||
|
|
623c3589a3 | ||
|
|
44924cdf83 | ||
|
|
75363181e4 | ||
|
|
1fc9d66457 | ||
|
|
2fdbe0da28 | ||
|
|
a1922cb195 | ||
|
|
725e950092 | ||
|
|
bf123d3105 | ||
|
|
83b6721484 | ||
|
|
dca952c10a | ||
|
|
b2e5896885 | ||
|
|
38542dad5e | ||
|
|
852a9c5fb1 | ||
|
|
12aa0e349e | ||
|
|
351f5d8c16 | ||
|
|
7e4b3075ed | ||
|
|
cf0d725189 | ||
|
|
a9f33fb846 | ||
|
|
4a9ab7a0eb | ||
|
|
3982ab99a6 | ||
|
|
1fb39e55d2 | ||
|
|
56d32b532d | ||
|
|
0d0800dfb5 | ||
|
|
f32110687f | ||
|
|
dbf39f3621 | ||
|
|
9a2f0e3df8 | ||
|
|
56fb9b40b9 | ||
|
|
fbc7bb6f07 | ||
|
|
2294c3758d | ||
|
|
c573da6e31 | ||
|
|
6362931e6b | ||
|
|
0be1f45edc | ||
|
|
9700aa7eac | ||
|
|
75eaf58aa2 | ||
|
|
63352989b1 | ||
|
|
dd5e0e3847 | ||
|
|
4f009c6d57 | ||
|
|
09d4db816e | ||
|
|
fb7799e931 | ||
|
|
13a1965f03 | ||
|
|
3abefb73f9 | ||
|
|
a35f5bb405 | ||
|
|
88a7fb53a3 | ||
|
|
866ba8440d | ||
|
|
61896bbddd | ||
|
|
7a4891d4e3 | ||
|
|
dbd5cd341e | ||
|
|
7df707a59d | ||
|
|
92dfa32d07 | ||
|
|
5cad1d31e3 | ||
|
|
e4c0418fd4 | ||
|
|
54c74d0138 | ||
|
|
6e21bdeb5b | ||
|
|
00439a1b04 | ||
|
|
87684abbbb | ||
|
|
0cbf78733e | ||
|
|
ecf0e7c356 | ||
|
|
f20d9884b5 | ||
|
|
61d713ad4d | ||
|
|
ca5cce3f40 | ||
|
|
1c8edac807 | ||
|
|
98b465f8f3 | ||
|
|
3c3e34eda3 | ||
|
|
02ad6f7810 | ||
|
|
5369ee7d42 | ||
|
|
c441e0761f | ||
|
|
a02dd06c20 | ||
|
|
957af5c5bf | ||
|
|
2c51a6900f | ||
|
|
e09e9b4e8d | ||
|
|
02497d485b | ||
|
|
bd32f3bc8d | ||
|
|
7176342e15 | ||
|
|
32ef8d8c5c | ||
|
|
92f8787eb9 | ||
|
|
5403f7a9ed | ||
|
|
2924d6f560 | ||
|
|
36c0708c17 | ||
|
|
b9fe54e883 | ||
|
|
5cc2f23547 | ||
|
|
30514ba780 | ||
|
|
02e6c5e8f3 | ||
|
|
fac97329aa | ||
|
|
cdf5cc3abe | ||
|
|
4cb630d201 | ||
|
|
79d6277d94 | ||
|
|
1a90fa6707 | ||
|
|
6ecbb2d901 | ||
|
|
3373901b95 | ||
|
|
7eb1b917dc | ||
|
|
fdba12082c | ||
|
|
516ecc1c3d | ||
|
|
cfd545c735 | ||
|
|
0ded1fe68b | ||
|
|
4fce11b149 | ||
|
|
2374e8fe03 | ||
|
|
36ccf346d7 | ||
|
|
cb480bd88d | ||
|
|
8cc96cb30e | ||
|
|
736ba0f8b0 | ||
|
|
5b5fefee8e | ||
|
|
8941e3179b | ||
|
|
19ce4c56a9 | ||
|
|
337c5d2b84 | ||
|
|
631c17aedc | ||
|
|
0ff4ac92be | ||
|
|
aa94094078 | ||
|
|
135ddcf331 | ||
|
|
e5d31e0b49 | ||
|
|
448b2e4b3f | ||
|
|
ebb30e3a70 | ||
|
|
fb89415636 | ||
|
|
d0d64b3e58 | ||
|
|
49273abbfc | ||
|
|
36c77d81eb | ||
|
|
b6cd56ad15 | ||
|
|
1a524aea94 | ||
|
|
2ebab99a06 | ||
|
|
46663f7edb | ||
|
|
4fad66cd90 | ||
|
|
a9fafeec00 | ||
|
|
0d8308d6cd | ||
|
|
13772002e8 | ||
|
|
c9b84889e7 | ||
|
|
1513578266 | ||
|
|
89d6f2f867 | ||
|
|
8e6d98c997 | ||
|
|
5bc2cb31a4 | ||
|
|
041575a103 | ||
|
|
c88dac56b7 | ||
|
|
00da717f6d | ||
|
|
cc557378cc | ||
|
|
b5c49c79b8 | ||
|
|
28018430e7 | ||
|
|
7b6c8d68a8 | ||
|
|
ad3cb3a620 | ||
|
|
511a7e4830 | ||
|
|
5aeeafc041 | ||
|
|
9a51fb8358 | ||
|
|
eeefb9f40e | ||
|
|
aa1ce34cbc | ||
|
|
47772c3ff7 | ||
|
|
dfcac5d527 | ||
|
|
40f88f0b59 | ||
|
|
105355d967 | ||
|
|
3cddff22b8 | ||
|
|
f8e930d29e | ||
|
|
9dbe088e66 | ||
|
|
6361c0e8e9 | ||
|
|
1135672241 | ||
|
|
80891e4a81 | ||
|
|
3e0be3a629 | ||
|
|
17819de2ea | ||
|
|
209cdd6b0d | ||
|
|
fbf6b2edd9 | ||
|
|
8b9e8589a0 |
@@ -29,7 +29,7 @@ and many more.
|
||||
|
||||
# For server owners
|
||||
- Requires ProtocolLib to be installed: get the latest version [here](https://www.spigotmc.org/resources/protocollib.1997/)
|
||||
- Supports 1.16.5+
|
||||
- Supports 1.17+
|
||||
|
||||
## Downloads
|
||||
|
||||
@@ -39,7 +39,7 @@ and many more.
|
||||
# For developers
|
||||
|
||||
## Javadoc
|
||||
The 6.13.0 Javadoc can be found [here](https://javadoc.jitpack.io/com/willfp/eco/6.13.0/javadoc/)
|
||||
The 6.27.2 Javadoc can be found [here](https://javadoc.jitpack.io/com/willfp/eco/6.27.2/javadoc/)
|
||||
|
||||
## Plugin Information
|
||||
|
||||
@@ -68,7 +68,7 @@ dependencies {
|
||||
}
|
||||
```
|
||||
|
||||
Replace `Tag` with a release tag for eco, eg `6.13.0`.
|
||||
Replace `Tag` with a release tag for eco, eg `6.27.2`.
|
||||
|
||||
Maven:
|
||||
|
||||
@@ -88,7 +88,7 @@ Maven:
|
||||
</dependency>
|
||||
```
|
||||
|
||||
Replace `Tag` with a release tag for eco, eg `6.13.0`.
|
||||
Replace `Tag` with a release tag for eco, eg `6.27.2`.
|
||||
|
||||
## Build locally:
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ buildscript {
|
||||
|
||||
plugins {
|
||||
id("java-library")
|
||||
id("com.github.johnrengelman.shadow") version "7.1.0"
|
||||
id("com.github.johnrengelman.shadow") version "7.1.2"
|
||||
id("maven-publish")
|
||||
id("java")
|
||||
kotlin("jvm") version "1.6.10"
|
||||
@@ -21,9 +21,9 @@ dependencies {
|
||||
implementation(project(":eco-core:core-plugin"))
|
||||
implementation(project(":eco-core:core-proxy"))
|
||||
implementation(project(":eco-core:core-backend"))
|
||||
implementation(project(":eco-core:core-nms:v1_16_R3"))
|
||||
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 {
|
||||
@@ -93,12 +93,12 @@ 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")
|
||||
compileOnly("com.google.guava:guava:31.1-jre")
|
||||
compileOnly("com.github.ben-manes.caffeine:caffeine:3.0.5")
|
||||
}
|
||||
|
||||
@@ -114,6 +114,11 @@ 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 {
|
||||
setExtendsFrom(listOf(configurations.compileOnly.get()))
|
||||
}
|
||||
|
||||
tasks {
|
||||
@@ -126,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 {
|
||||
|
||||
@@ -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'
|
||||
@@ -13,6 +12,10 @@ dependencies {
|
||||
group 'com.willfp'
|
||||
version rootProject.version
|
||||
|
||||
java {
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
build.dependsOn publishToMavenLocal
|
||||
|
||||
publishing {
|
||||
@@ -33,4 +36,4 @@ publishing {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -204,15 +205,6 @@ public interface Handler {
|
||||
*/
|
||||
void registerBStats(@NotNull EcoPlugin plugin);
|
||||
|
||||
/**
|
||||
* Get the requirement factory.
|
||||
*
|
||||
* @return The factory.
|
||||
*/
|
||||
@NotNull
|
||||
@Deprecated(forRemoval = true)
|
||||
com.willfp.eco.core.requirement.RequirementFactory getRequirementFactory();
|
||||
|
||||
/**
|
||||
* Get Adventure audiences.
|
||||
*
|
||||
@@ -271,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);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,10 @@ public class Prerequisite {
|
||||
|
||||
/**
|
||||
* Requires the server to be running 1.17.
|
||||
*
|
||||
* @deprecated eco no longer supports versions before 1.17.
|
||||
*/
|
||||
@Deprecated(since = "6.25.2")
|
||||
public static final Prerequisite HAS_1_17 = new Prerequisite(
|
||||
() -> ProxyConstants.NMS_VERSION.contains("17") || HAS_1_18.isMet(),
|
||||
"Requires server to be running 1.17+"
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.willfp.eco.core.config.interfaces;
|
||||
|
||||
import com.willfp.eco.core.config.ConfigType;
|
||||
import com.willfp.eco.core.config.TransientConfig;
|
||||
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
|
||||
import com.willfp.eco.core.placeholder.StaticPlaceholder;
|
||||
import com.willfp.eco.util.NumberUtils;
|
||||
import com.willfp.eco.util.StringUtils;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -9,6 +11,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -18,7 +21,7 @@ import java.util.Objects;
|
||||
* Contains all methods that must exist in yaml and json configurations.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public interface Config extends Cloneable {
|
||||
public interface Config extends Cloneable, PlaceholderInjectable {
|
||||
/**
|
||||
* Clears cache.
|
||||
*/
|
||||
@@ -563,7 +566,7 @@ public interface Config extends Cloneable {
|
||||
*/
|
||||
default double getDoubleFromExpression(@NotNull String path,
|
||||
@Nullable Player player) {
|
||||
return NumberUtils.evaluateExpression(this.getString(path), player);
|
||||
return NumberUtils.evaluateExpression(this.getString(path), player, this.getInjectedPlaceholders());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -629,4 +632,19 @@ public interface Config extends Cloneable {
|
||||
* @return The clone.
|
||||
*/
|
||||
Config clone();
|
||||
|
||||
@Override
|
||||
default void injectPlaceholders(@NotNull Iterable<StaticPlaceholder> placeholders) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
default List<StaticPlaceholder> getInjectedPlaceholders() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
default void clearInjectedPlaceholders() {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.willfp.eco.core.config.wrapper;
|
||||
|
||||
import com.willfp.eco.core.config.ConfigType;
|
||||
import com.willfp.eco.core.config.interfaces.Config;
|
||||
import com.willfp.eco.core.placeholder.StaticPlaceholder;
|
||||
import com.willfp.eco.util.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -134,6 +135,26 @@ public abstract class ConfigWrapper<T extends Config> implements Config {
|
||||
return handle.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectPlaceholders(@NotNull final StaticPlaceholder... placeholders) {
|
||||
handle.injectPlaceholders(placeholders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectPlaceholders(@NotNull final Iterable<StaticPlaceholder> placeholders) {
|
||||
handle.injectPlaceholders(placeholders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StaticPlaceholder> getInjectedPlaceholders() {
|
||||
return handle.getInjectedPlaceholders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearInjectedPlaceholders() {
|
||||
handle.clearInjectedPlaceholders();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the handle.
|
||||
*
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package com.willfp.eco.core.data.keys;
|
||||
|
||||
import com.willfp.eco.core.Eco;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* API to register persistent data keys.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@Eco.HandlerComponent
|
||||
public interface KeyRegistry {
|
||||
/**
|
||||
* Register a persistent data key to be stored.
|
||||
@@ -21,4 +27,37 @@ public interface KeyRegistry {
|
||||
* @return The keys.
|
||||
*/
|
||||
Set<PersistentDataKey<?>> getRegisteredKeys();
|
||||
|
||||
/**
|
||||
* Mark key as category.
|
||||
*
|
||||
* @param key The key.
|
||||
* @param category The category.
|
||||
*/
|
||||
void markKeyAs(@NotNull PersistentDataKey<?> key,
|
||||
@NotNull KeyRegistry.KeyCategory category);
|
||||
|
||||
/**
|
||||
* Get persistent data key from namespaced key.
|
||||
*
|
||||
* @param namespacedKey The key.
|
||||
* @return The key, or null if not found.
|
||||
*/
|
||||
@Nullable
|
||||
PersistentDataKey<?> getKeyFrom(@NotNull NamespacedKey namespacedKey);
|
||||
|
||||
/**
|
||||
* Locations for key categorization.
|
||||
*/
|
||||
enum KeyCategory {
|
||||
/**
|
||||
* Player keys.
|
||||
*/
|
||||
PLAYER,
|
||||
|
||||
/**
|
||||
* Server keys.
|
||||
*/
|
||||
SERVER
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package com.willfp.eco.core.data.keys;
|
||||
import com.willfp.eco.core.Eco;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -11,7 +13,7 @@ import java.util.Set;
|
||||
*
|
||||
* @param <T> The type of the data.
|
||||
*/
|
||||
public class PersistentDataKey<T> {
|
||||
public final class PersistentDataKey<T> {
|
||||
/**
|
||||
* The key of the persistent data value.
|
||||
*/
|
||||
@@ -80,6 +82,32 @@ public class PersistentDataKey<T> {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Categorize key as a server key, will register new column to MySQL
|
||||
* database immediately rather than waiting for auto-categorization.
|
||||
* <p>
|
||||
* This will improve performance.
|
||||
*
|
||||
* @return The key.
|
||||
*/
|
||||
public PersistentDataKey<T> server() {
|
||||
Eco.getHandler().getKeyRegistry().markKeyAs(this, KeyRegistry.KeyCategory.SERVER);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Categorize key as a player key, will register new column to MySQL
|
||||
* database immediately rather than waiting for auto-categorization.
|
||||
* <p>
|
||||
* This will improve performance.
|
||||
*
|
||||
* @return The key.
|
||||
*/
|
||||
public PersistentDataKey<T> player() {
|
||||
Eco.getHandler().getKeyRegistry().markKeyAs(this, KeyRegistry.KeyCategory.PLAYER);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all persistent data keys.
|
||||
*
|
||||
@@ -88,4 +116,20 @@ public class PersistentDataKey<T> {
|
||||
public static Set<PersistentDataKey<?>> values() {
|
||||
return Eco.getHandler().getKeyRegistry().getRegisteredKeys();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof PersistentDataKey that)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(this.getKey(), that.getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.willfp.eco.core.entities;
|
||||
|
||||
import org.bukkit.entity.Entity;
|
||||
|
||||
/**
|
||||
* Interface for Dummy Entities in order to filter them using instanceof.
|
||||
*/
|
||||
public interface DummyEntity extends Entity {
|
||||
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import com.willfp.eco.core.entities.impl.EmptyTestableEntity;
|
||||
import com.willfp.eco.core.entities.impl.ModifiedTestableEntity;
|
||||
import com.willfp.eco.core.entities.impl.SimpleTestableEntity;
|
||||
import com.willfp.eco.util.NamespacedKeyUtils;
|
||||
import com.willfp.eco.util.StringUtils;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.entity.Entity;
|
||||
@@ -37,6 +36,11 @@ public final class Entities {
|
||||
*/
|
||||
private static final List<EntityArgParser> ARG_PARSERS = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The lookup handler.
|
||||
*/
|
||||
private static final EntitiesLookupHandler ENTITIES_LOOKUP_HANDLER = new EntitiesLookupHandler(Entities::doParse);
|
||||
|
||||
/**
|
||||
* Register a new custom item.
|
||||
*
|
||||
@@ -87,20 +91,11 @@ public final class Entities {
|
||||
*/
|
||||
@NotNull
|
||||
public static TestableEntity lookup(@NotNull final String key) {
|
||||
if (key.contains("?")) {
|
||||
String[] options = key.split("\\?");
|
||||
for (String option : options) {
|
||||
TestableEntity lookup = lookup(option);
|
||||
if (!(lookup instanceof EmptyTestableEntity)) {
|
||||
return lookup;
|
||||
}
|
||||
}
|
||||
|
||||
return new EmptyTestableEntity();
|
||||
}
|
||||
|
||||
String[] args = StringUtils.parseTokens(key);
|
||||
return ENTITIES_LOOKUP_HANDLER.parseKey(key);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static TestableEntity doParse(@NotNull final String[] args) {
|
||||
if (args.length == 0) {
|
||||
return new EmptyTestableEntity();
|
||||
}
|
||||
@@ -131,7 +126,6 @@ public final class Entities {
|
||||
entity = part;
|
||||
}
|
||||
|
||||
|
||||
String[] modifierArgs = Arrays.copyOfRange(args, 1, args.length);
|
||||
|
||||
List<EntityArgParseResult> parseResults = new ArrayList<>();
|
||||
@@ -172,7 +166,6 @@ public final class Entities {
|
||||
return entity;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a Testable Entity from an ItemStack.
|
||||
* <p>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.willfp.eco.core.entities;
|
||||
|
||||
import com.willfp.eco.core.entities.impl.EmptyTestableEntity;
|
||||
import com.willfp.eco.core.entities.impl.GroupedTestableEntities;
|
||||
import com.willfp.eco.core.lookup.LookupHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Handle item lookup strings.
|
||||
*/
|
||||
public class EntitiesLookupHandler implements LookupHandler<TestableEntity> {
|
||||
/**
|
||||
* The parser.
|
||||
*/
|
||||
private final Function<String[], @NotNull TestableEntity> parser;
|
||||
|
||||
/**
|
||||
* Create new lookup handler.
|
||||
*
|
||||
* @param parser The parser.
|
||||
*/
|
||||
public EntitiesLookupHandler(@NotNull final Function<String[], @NotNull TestableEntity> parser) {
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TestableEntity parse(@NotNull final String[] args) {
|
||||
return parser.apply(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(@NotNull final TestableEntity object) {
|
||||
return !(object instanceof EmptyTestableEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TestableEntity getFailsafe() {
|
||||
return new EmptyTestableEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TestableEntity join(@NotNull final Collection<TestableEntity> options) {
|
||||
return new GroupedTestableEntities(options);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.willfp.eco.core.entities;
|
||||
|
||||
import com.willfp.eco.core.lookup.Testable;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -8,13 +9,14 @@ import org.jetbrains.annotations.Nullable;
|
||||
/**
|
||||
* An item with a test to see if any item is that item.
|
||||
*/
|
||||
public interface TestableEntity {
|
||||
public interface TestableEntity extends Testable<Entity> {
|
||||
/**
|
||||
* If an Entity matches the test.
|
||||
*
|
||||
* @param entity The entity to test.
|
||||
* @return If the entity matches.
|
||||
*/
|
||||
@Override
|
||||
boolean matches(@Nullable Entity entity);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 canUse returns true.
|
||||
* <p>
|
||||
* Runs after start().
|
||||
*/
|
||||
public void tick() {
|
||||
// Override when needed.
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the goal.
|
||||
* <p>
|
||||
* Runs once canUse() returns true.
|
||||
*/
|
||||
public void start() {
|
||||
// Override when needed.
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the goal.
|
||||
* <p>
|
||||
* Runs once 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."
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.willfp.eco.core.entities.impl;
|
||||
|
||||
import com.willfp.eco.core.entities.TestableEntity;
|
||||
import com.willfp.eco.util.NumberUtils;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A group of testable entities.
|
||||
*
|
||||
* @see com.willfp.eco.core.entities.CustomEntity
|
||||
*/
|
||||
public class GroupedTestableEntities implements TestableEntity {
|
||||
/**
|
||||
* The children.
|
||||
*/
|
||||
private final Collection<TestableEntity> children;
|
||||
|
||||
/**
|
||||
* Create a new group of testable entities.
|
||||
*
|
||||
* @param children The children.
|
||||
*/
|
||||
public GroupedTestableEntities(@NotNull final Collection<TestableEntity> children) {
|
||||
Validate.isTrue(!children.isEmpty(), "Group must have at least one child!");
|
||||
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the item matches any children.
|
||||
*
|
||||
* @param entity The entity to test.
|
||||
* @return If the entity matches the test of any children.
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(@Nullable final Entity entity) {
|
||||
for (TestableEntity child : children) {
|
||||
if (child.matches(entity)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity spawn(@NotNull final Location location) {
|
||||
return new ArrayList<>(children)
|
||||
.get(NumberUtils.randInt(0, children.size() - 1))
|
||||
.spawn(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the children.
|
||||
*
|
||||
* @return The children.
|
||||
*/
|
||||
public Collection<TestableEntity> getChildren() {
|
||||
return new ArrayList<>(children);
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,8 @@ public final class AntigriefManager {
|
||||
* @param location The location.
|
||||
* @return If player can pick up item.
|
||||
*/
|
||||
public static boolean canPickupItem(@NotNull final Player player, @NotNull final Location location) {
|
||||
public static boolean canPickupItem(@NotNull final Player player,
|
||||
@NotNull final Location location) {
|
||||
return REGISTERED.stream().allMatch(antigriefWrapper -> antigriefWrapper.canPickupItem(player, location));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package com.willfp.eco.core.integrations.placeholder;
|
||||
|
||||
import com.willfp.eco.core.Eco;
|
||||
import com.willfp.eco.core.EcoPlugin;
|
||||
import com.willfp.eco.core.placeholder.Placeholder;
|
||||
import com.willfp.eco.core.placeholder.PlayerPlaceholder;
|
||||
import com.willfp.eco.core.placeholder.PlayerlessPlaceholder;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -10,10 +14,13 @@ import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* A {@link PlaceholderEntry} is a placeholder in and of itself.
|
||||
* A placeholder entry is a placeholder in and of itself.
|
||||
* <p>
|
||||
* It should be fairly straightforward.
|
||||
*
|
||||
* @deprecated Confusing functionality with inconsistent nullability and poor naming.
|
||||
*/
|
||||
@Deprecated(since = "6.28.0", forRemoval = true)
|
||||
public class PlaceholderEntry {
|
||||
/**
|
||||
* The name of the placeholder, used in lookups.
|
||||
@@ -140,7 +147,28 @@ public class PlaceholderEntry {
|
||||
* Register the placeholder.
|
||||
*/
|
||||
public void register() {
|
||||
PlaceholderManager.registerPlaceholder(this);
|
||||
PlaceholderManager.registerPlaceholder(this.toModernPlaceholder());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the placeholder to a modern placeholder.
|
||||
*
|
||||
* @return The placeholder.
|
||||
*/
|
||||
Placeholder toModernPlaceholder() {
|
||||
if (this.requiresPlayer) {
|
||||
return new PlayerPlaceholder(
|
||||
Objects.requireNonNullElse(plugin, Eco.getHandler().getEcoPlugin()),
|
||||
identifier,
|
||||
function
|
||||
);
|
||||
} else {
|
||||
return new PlayerlessPlaceholder(
|
||||
Objects.requireNonNullElse(plugin, Eco.getHandler().getEcoPlugin()),
|
||||
identifier,
|
||||
() -> function.apply(null)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,11 +4,16 @@ import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.willfp.eco.core.Eco;
|
||||
import com.willfp.eco.core.EcoPlugin;
|
||||
import com.willfp.eco.core.placeholder.Placeholder;
|
||||
import com.willfp.eco.core.placeholder.PlayerPlaceholder;
|
||||
import com.willfp.eco.core.placeholder.PlayerlessPlaceholder;
|
||||
import com.willfp.eco.core.placeholder.StaticPlaceholder;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -19,11 +24,12 @@ import java.util.concurrent.TimeUnit;
|
||||
/**
|
||||
* Class to handle placeholder integrations.
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
public final class PlaceholderManager {
|
||||
/**
|
||||
* All registered placeholders.
|
||||
*/
|
||||
private static final Map<EcoPlugin, Map<String, PlaceholderEntry>> REGISTERED_PLACEHOLDERS = new HashMap<>();
|
||||
private static final Map<EcoPlugin, Map<String, Placeholder>> REGISTERED_PLACEHOLDERS = new HashMap<>();
|
||||
|
||||
/**
|
||||
* All registered placeholder integrations.
|
||||
@@ -35,7 +41,7 @@ public final class PlaceholderManager {
|
||||
*/
|
||||
private static final LoadingCache<EntryWithPlayer, String> PLACEHOLDER_CACHE = Caffeine.newBuilder()
|
||||
.expireAfterWrite(50, TimeUnit.MILLISECONDS)
|
||||
.build(key -> key.entry.getResult(key.player));
|
||||
.build(key -> key.entry.getValue(key.player));
|
||||
|
||||
/**
|
||||
* Register a new placeholder integration.
|
||||
@@ -50,16 +56,31 @@ public final class PlaceholderManager {
|
||||
/**
|
||||
* Register a placeholder.
|
||||
*
|
||||
* @param expansion The {@link com.willfp.eco.core.integrations.placeholder.PlaceholderEntry} to register.
|
||||
* @param placeholder The placeholder to register.
|
||||
*/
|
||||
public static void registerPlaceholder(@NotNull final PlaceholderEntry expansion) {
|
||||
EcoPlugin plugin = expansion.getPlugin() == null ? Eco.getHandler().getEcoPlugin() : expansion.getPlugin();
|
||||
Map<String, PlaceholderEntry> pluginPlaceholders = REGISTERED_PLACEHOLDERS
|
||||
public static void registerPlaceholder(@NotNull final Placeholder placeholder) {
|
||||
if (placeholder instanceof StaticPlaceholder) {
|
||||
throw new IllegalArgumentException("Static placeholders cannot be registered!");
|
||||
}
|
||||
|
||||
EcoPlugin plugin = placeholder.getPlugin() == null ? Eco.getHandler().getEcoPlugin() : placeholder.getPlugin();
|
||||
Map<String, Placeholder> pluginPlaceholders = REGISTERED_PLACEHOLDERS
|
||||
.getOrDefault(plugin, new HashMap<>());
|
||||
pluginPlaceholders.put(expansion.getIdentifier(), expansion);
|
||||
pluginPlaceholders.put(placeholder.getIdentifier(), placeholder);
|
||||
REGISTERED_PLACEHOLDERS.put(plugin, pluginPlaceholders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a placeholder.
|
||||
*
|
||||
* @param placeholder The placeholder to register.
|
||||
* @deprecated Uses old placeholder system.
|
||||
*/
|
||||
@Deprecated(since = "6.28.0", forRemoval = true)
|
||||
public static void registerPlaceholder(@NotNull final PlaceholderEntry placeholder) {
|
||||
registerPlaceholder(placeholder.toModernPlaceholder());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result of a placeholder with respect to a player.
|
||||
*
|
||||
@@ -82,29 +103,36 @@ public final class PlaceholderManager {
|
||||
* @param plugin The plugin for the placeholder.
|
||||
* @return The value of the placeholder.
|
||||
*/
|
||||
@NotNull
|
||||
public static String getResult(@Nullable final Player player,
|
||||
@NotNull final String identifier,
|
||||
@Nullable final EcoPlugin plugin) {
|
||||
EcoPlugin owner = plugin == null ? Eco.getHandler().getEcoPlugin() : plugin;
|
||||
PlaceholderEntry entry = REGISTERED_PLACEHOLDERS.getOrDefault(owner, new HashMap<>()).get(identifier.toLowerCase());
|
||||
Placeholder placeholder = REGISTERED_PLACEHOLDERS.getOrDefault(owner, new HashMap<>()).get(identifier.toLowerCase());
|
||||
|
||||
if (entry == null && plugin != null) {
|
||||
PlaceholderEntry alternate = REGISTERED_PLACEHOLDERS.getOrDefault(Eco.getHandler().getEcoPlugin(), new HashMap<>())
|
||||
if (placeholder == null && plugin != null) {
|
||||
Placeholder alternate = REGISTERED_PLACEHOLDERS.getOrDefault(Eco.getHandler().getEcoPlugin(), new HashMap<>())
|
||||
.get(identifier.toLowerCase());
|
||||
if (alternate != null) {
|
||||
entry = alternate;
|
||||
placeholder = alternate;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry == null) {
|
||||
if (placeholder == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (player == null && entry.requiresPlayer()) {
|
||||
if (placeholder instanceof PlayerPlaceholder playerPlaceholder) {
|
||||
if (player == null) {
|
||||
return "";
|
||||
} else {
|
||||
return PLACEHOLDER_CACHE.get(new EntryWithPlayer(playerPlaceholder, player));
|
||||
}
|
||||
} else if (placeholder instanceof PlayerlessPlaceholder playerlessPlaceholder) {
|
||||
return playerlessPlaceholder.getValue();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
||||
return PLACEHOLDER_CACHE.get(new EntryWithPlayer(entry, player));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,10 +144,28 @@ public final class PlaceholderManager {
|
||||
*/
|
||||
public static String translatePlaceholders(@NotNull final String text,
|
||||
@Nullable final Player player) {
|
||||
return translatePlaceholders(text, player, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate all placeholders with respect to a player.
|
||||
*
|
||||
* @param text The text that may contain placeholders to translate.
|
||||
* @param player The player to translate the placeholders with respect to.
|
||||
* @param statics Extra static placeholders.
|
||||
* @return The text, translated.
|
||||
*/
|
||||
public static String translatePlaceholders(@NotNull final String text,
|
||||
@Nullable final Player player,
|
||||
@NotNull final List<StaticPlaceholder> statics) {
|
||||
String processed = text;
|
||||
for (PlaceholderIntegration integration : REGISTERED_INTEGRATIONS) {
|
||||
processed = integration.translate(processed, player);
|
||||
}
|
||||
for (StaticPlaceholder placeholder : statics) {
|
||||
// Do I know this is a bad way of doing this? Yes.
|
||||
processed = processed.replace("%" + placeholder.getIdentifier() + "%", placeholder.getValue());
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
|
||||
@@ -138,8 +184,8 @@ public final class PlaceholderManager {
|
||||
return found;
|
||||
}
|
||||
|
||||
private static record EntryWithPlayer(@NotNull PlaceholderEntry entry,
|
||||
@Nullable Player player) {
|
||||
private record EntryWithPlayer(@NotNull PlayerPlaceholder entry,
|
||||
@NotNull Player player) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import com.willfp.eco.core.recipe.parts.ModifiedTestableItem;
|
||||
import com.willfp.eco.core.recipe.parts.TestableStack;
|
||||
import com.willfp.eco.util.NamespacedKeyUtils;
|
||||
import com.willfp.eco.util.NumberUtils;
|
||||
import com.willfp.eco.util.StringUtils;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@@ -43,9 +42,13 @@ public final class Items {
|
||||
* Cached custom item lookups, using {@link HashedItem}.
|
||||
*/
|
||||
private static final LoadingCache<HashedItem, Optional<TestableItem>> CACHE = Caffeine.newBuilder()
|
||||
.expireAfterAccess(5, TimeUnit.MINUTES)
|
||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||
.build(
|
||||
key -> {
|
||||
if (!key.getItem().hasItemMeta()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
TestableItem match = null;
|
||||
for (TestableItem item : REGISTRY.values()) {
|
||||
if (item.matches(key.getItem())) {
|
||||
@@ -68,6 +71,11 @@ public final class Items {
|
||||
*/
|
||||
private static final List<LookupArgParser> ARG_PARSERS = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The handler.
|
||||
*/
|
||||
private static final ItemsLookupHandler ITEMS_LOOKUP_HANDLER = new ItemsLookupHandler(Items::doParse);
|
||||
|
||||
/**
|
||||
* Register a new custom item.
|
||||
*
|
||||
@@ -131,20 +139,11 @@ public final class Items {
|
||||
*/
|
||||
@NotNull
|
||||
public static TestableItem lookup(@NotNull final String key) {
|
||||
if (key.contains("?")) {
|
||||
String[] options = key.split("\\?");
|
||||
for (String option : options) {
|
||||
TestableItem lookup = lookup(option);
|
||||
if (!(lookup instanceof EmptyTestableItem)) {
|
||||
return lookup;
|
||||
}
|
||||
}
|
||||
|
||||
return new EmptyTestableItem();
|
||||
}
|
||||
|
||||
String[] args = StringUtils.parseTokens(key);
|
||||
return ITEMS_LOOKUP_HANDLER.parseKey(key);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static TestableItem doParse(@NotNull final String[] args) {
|
||||
if (args.length == 0) {
|
||||
return new EmptyTestableItem();
|
||||
}
|
||||
@@ -299,7 +298,7 @@ public final class Items {
|
||||
* @param itemStack The itemStack to check.
|
||||
* @return If is recipe.
|
||||
*/
|
||||
public static boolean isCustomItem(@NotNull final ItemStack itemStack) {
|
||||
public static boolean isCustomItem(@Nullable final ItemStack itemStack) {
|
||||
return getCustomItem(itemStack) != null;
|
||||
}
|
||||
|
||||
@@ -310,7 +309,11 @@ public final class Items {
|
||||
* @return The custom item, or null if not exists.
|
||||
*/
|
||||
@Nullable
|
||||
public static CustomItem getCustomItem(@NotNull final ItemStack itemStack) {
|
||||
public static CustomItem getCustomItem(@Nullable final ItemStack itemStack) {
|
||||
if (itemStack == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return CACHE.get(HashedItem.of(itemStack)).map(Items::getOrWrap).orElse(null);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.willfp.eco.core.items;
|
||||
|
||||
import com.willfp.eco.core.lookup.LookupHandler;
|
||||
import com.willfp.eco.core.recipe.parts.EmptyTestableItem;
|
||||
import com.willfp.eco.core.recipe.parts.GroupedTestableItems;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Handle item lookup strings.
|
||||
*/
|
||||
public class ItemsLookupHandler implements LookupHandler<TestableItem> {
|
||||
/**
|
||||
* The parser.
|
||||
*/
|
||||
private final Function<String[], @NotNull TestableItem> parser;
|
||||
|
||||
/**
|
||||
* Create new lookup handler.
|
||||
*
|
||||
* @param parser The parser.
|
||||
*/
|
||||
public ItemsLookupHandler(@NotNull final Function<String[], @NotNull TestableItem> parser) {
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TestableItem parse(@NotNull final String[] args) {
|
||||
return parser.apply(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(@NotNull final TestableItem object) {
|
||||
return !(object instanceof EmptyTestableItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TestableItem getFailsafe() {
|
||||
return new EmptyTestableItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TestableItem join(@NotNull final Collection<TestableItem> options) {
|
||||
return new GroupedTestableItems(options);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,20 @@
|
||||
package com.willfp.eco.core.items;
|
||||
|
||||
import com.willfp.eco.core.lookup.Testable;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* An item with a test to see if any item is that item.
|
||||
*/
|
||||
public interface TestableItem {
|
||||
public interface TestableItem extends Testable<ItemStack> {
|
||||
/**
|
||||
* If an ItemStack matches the recipe part.
|
||||
*
|
||||
* @param itemStack The item to test.
|
||||
* @return If the item matches.
|
||||
*/
|
||||
@Override
|
||||
boolean matches(@Nullable ItemStack itemStack);
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.willfp.eco.core.lookup;
|
||||
|
||||
import com.willfp.eco.util.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Handle lookups, used in {@link com.willfp.eco.core.entities.Entities} and {@link com.willfp.eco.core.items.Items}.
|
||||
*
|
||||
* @param <T> The type of testable object, eg {@link com.willfp.eco.core.items.TestableItem}.
|
||||
*/
|
||||
public interface LookupHandler<T extends Testable<?>> {
|
||||
/**
|
||||
* Parse lookup string completely.
|
||||
* <p>
|
||||
* You shouldn't override this method unless you're doing something
|
||||
* technically interesting or weird. This is the entry point for all
|
||||
* lookup parsers, parse() is to specify implementation-specific
|
||||
* parsing.
|
||||
*
|
||||
* @param key The key.
|
||||
* @return The object.
|
||||
*/
|
||||
default T parseKey(@NotNull String key) {
|
||||
for (SegmentParser parser : SegmentParser.values()) {
|
||||
T generated = parser.parse(key, this);
|
||||
|
||||
if (generated != null) {
|
||||
return generated;
|
||||
}
|
||||
}
|
||||
|
||||
String[] args = StringUtils.parseTokens(key);
|
||||
|
||||
return this.parse(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse arguments to an object.
|
||||
*
|
||||
* @param args The arguments.
|
||||
* @return The object.
|
||||
*/
|
||||
@NotNull
|
||||
T parse(@NotNull String[] args);
|
||||
|
||||
/**
|
||||
* Validate an object.
|
||||
*
|
||||
* @param object The object.
|
||||
* @return If validated.
|
||||
*/
|
||||
boolean validate(@NotNull T object);
|
||||
|
||||
/**
|
||||
* Get the failsafe object.
|
||||
* <p>
|
||||
* A failsafe object should never pass validate(), as this will
|
||||
* cause issues with segment parsers. See {@link com.willfp.eco.core.items.ItemsLookupHandler} and
|
||||
* {@link com.willfp.eco.core.recipe.parts.EmptyTestableItem} for examples.
|
||||
*
|
||||
* @return The failsafe.
|
||||
*/
|
||||
@NotNull
|
||||
T getFailsafe();
|
||||
|
||||
/**
|
||||
* Join several options together.
|
||||
*
|
||||
* @param options The options.
|
||||
* @return The joined object.
|
||||
*/
|
||||
@NotNull
|
||||
T join(@NotNull Collection<T> options);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.willfp.eco.core.lookup;
|
||||
|
||||
import com.willfp.eco.util.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Parse a key into segments.
|
||||
*/
|
||||
public abstract class SegmentParser {
|
||||
/**
|
||||
* All segment parsers.
|
||||
*/
|
||||
private static final List<SegmentParser> REGISTERED = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The pattern to split keys on.
|
||||
*/
|
||||
private final String pattern;
|
||||
|
||||
/**
|
||||
* Create new lookup segment parser.
|
||||
*
|
||||
* @param pattern The pattern.
|
||||
*/
|
||||
protected SegmentParser(@NotNull final String pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the parser.
|
||||
*
|
||||
* @return The parser.
|
||||
*/
|
||||
public SegmentParser register() {
|
||||
REGISTERED.add(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try parse segments from key.
|
||||
*
|
||||
* @param key The key.
|
||||
* @param handler The handler.
|
||||
* @param <T> The object type.
|
||||
* @return Null if no segments were found, or the object generated from the segments.
|
||||
*/
|
||||
@Nullable
|
||||
public <T extends Testable<?>> T parse(@NotNull final String key,
|
||||
@NotNull final LookupHandler<T> handler) {
|
||||
if (!key.contains(" " + pattern + " ")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] segments = StringUtils.splitAround(key, pattern);
|
||||
|
||||
return handleSegments(segments, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle segments from key.
|
||||
*
|
||||
* @param segments The key segments.
|
||||
* @param handler The handler.
|
||||
* @param <T> The object type.
|
||||
* @return The returned object.
|
||||
*/
|
||||
protected abstract <T extends Testable<?>> T handleSegments(@NotNull String[] segments,
|
||||
@NotNull LookupHandler<T> handler);
|
||||
|
||||
/**
|
||||
* Get all segment parsers.
|
||||
*
|
||||
* @return All parsers.
|
||||
*/
|
||||
public static Collection<SegmentParser> values() {
|
||||
return new ArrayList<>(REGISTERED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
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> extends Predicate<T> {
|
||||
/**
|
||||
* If object matches the test.
|
||||
*
|
||||
* @param other The other object.
|
||||
* @return If matches.
|
||||
*/
|
||||
boolean matches(@Nullable T other);
|
||||
|
||||
@Override
|
||||
default boolean test(@Nullable T other) {
|
||||
return this.matches(other);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.willfp.eco.core.placeholder;
|
||||
|
||||
import com.willfp.eco.core.EcoPlugin;
|
||||
|
||||
/**
|
||||
* A placeholder represents a string that can hold a value.
|
||||
*/
|
||||
public sealed interface Placeholder permits PlayerPlaceholder, PlayerlessPlaceholder, StaticPlaceholder {
|
||||
/**
|
||||
* Get the plugin that holds the placeholder.
|
||||
*
|
||||
* @return The plugin.
|
||||
*/
|
||||
EcoPlugin getPlugin();
|
||||
|
||||
/**
|
||||
* Get the identifier for the placeholder.
|
||||
*
|
||||
* @return The identifier.
|
||||
*/
|
||||
String getIdentifier();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.willfp.eco.core.placeholder;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a class that can have placeholders injected into it.
|
||||
*/
|
||||
public interface PlaceholderInjectable {
|
||||
/**
|
||||
* Inject placeholder.
|
||||
*
|
||||
* @param placeholders The placeholders.
|
||||
*/
|
||||
default void injectPlaceholders(@NotNull StaticPlaceholder... placeholders) {
|
||||
this.injectPlaceholders(List.of(placeholders));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject placeholder.
|
||||
*
|
||||
* @param placeholders The placeholders.
|
||||
*/
|
||||
void injectPlaceholders(@NotNull Iterable<StaticPlaceholder> placeholders);
|
||||
|
||||
/**
|
||||
* Clear injected placeholders.
|
||||
*/
|
||||
void clearInjectedPlaceholders();
|
||||
|
||||
/**
|
||||
* Get injected placeholders.
|
||||
*
|
||||
* @return Injected placeholders.
|
||||
*/
|
||||
List<StaticPlaceholder> getInjectedPlaceholders();
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.willfp.eco.core.placeholder;
|
||||
|
||||
import com.willfp.eco.core.EcoPlugin;
|
||||
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* A placeholder that requires a player.
|
||||
*/
|
||||
public final class PlayerPlaceholder implements Placeholder {
|
||||
/**
|
||||
* The name of the placeholder.
|
||||
*/
|
||||
private final String identifier;
|
||||
|
||||
/**
|
||||
* The function to retrieve the output of the placeholder given a player.
|
||||
*/
|
||||
private final Function<Player, String> function;
|
||||
|
||||
/**
|
||||
* The plugin for the placeholder.
|
||||
*/
|
||||
private final EcoPlugin plugin;
|
||||
|
||||
/**
|
||||
* Create a new player placeholder.
|
||||
*
|
||||
* @param plugin The plugin.
|
||||
* @param identifier The identifier.
|
||||
* @param function The function to retrieve the value.
|
||||
*/
|
||||
public PlayerPlaceholder(@NotNull final EcoPlugin plugin,
|
||||
@NotNull final String identifier,
|
||||
@NotNull final Function<Player, String> function) {
|
||||
this.plugin = plugin;
|
||||
this.identifier = identifier;
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the placeholder for a given player.
|
||||
*
|
||||
* @param player The player.
|
||||
* @return The value.
|
||||
*/
|
||||
public String getValue(@NotNull final Player player) {
|
||||
return function.apply(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the placeholder.
|
||||
*
|
||||
* @return The placeholder.
|
||||
*/
|
||||
public PlayerPlaceholder register() {
|
||||
PlaceholderManager.registerPlaceholder(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EcoPlugin getPlugin() {
|
||||
return this.plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return this.identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof StaticPlaceholder that)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(this.getIdentifier(), that.getIdentifier())
|
||||
&& Objects.equals(this.getPlugin(), that.getPlugin());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.getIdentifier(), this.getPlugin());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.willfp.eco.core.placeholder;
|
||||
|
||||
import com.willfp.eco.core.EcoPlugin;
|
||||
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* A placeholder that does not require a player.
|
||||
*/
|
||||
public final class PlayerlessPlaceholder implements Placeholder {
|
||||
/**
|
||||
* The name of the placeholder.
|
||||
*/
|
||||
private final String identifier;
|
||||
|
||||
/**
|
||||
* The function to retrieve the output of the placeholder.
|
||||
*/
|
||||
private final Supplier<String> function;
|
||||
|
||||
/**
|
||||
* The plugin for the placeholder.
|
||||
*/
|
||||
private final EcoPlugin plugin;
|
||||
|
||||
/**
|
||||
* Create a new player placeholder.
|
||||
*
|
||||
* @param plugin The plugin.
|
||||
* @param identifier The identifier.
|
||||
* @param function The function to retrieve the value.
|
||||
*/
|
||||
public PlayerlessPlaceholder(@NotNull final EcoPlugin plugin,
|
||||
@NotNull final String identifier,
|
||||
@NotNull final Supplier<String> function) {
|
||||
this.plugin = plugin;
|
||||
this.identifier = identifier;
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the placeholder.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
public String getValue() {
|
||||
return function.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the placeholder.
|
||||
*
|
||||
* @return The placeholder.
|
||||
*/
|
||||
public PlayerlessPlaceholder register() {
|
||||
PlaceholderManager.registerPlaceholder(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EcoPlugin getPlugin() {
|
||||
return this.plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return this.identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof StaticPlaceholder that)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(this.getIdentifier(), that.getIdentifier())
|
||||
&& Objects.equals(this.getPlugin(), that.getPlugin());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.getIdentifier(), this.getPlugin());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.willfp.eco.core.placeholder;
|
||||
|
||||
import com.willfp.eco.core.Eco;
|
||||
import com.willfp.eco.core.EcoPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* A placeholder that cannot be registered, and exists purely in injection.
|
||||
*/
|
||||
public final class StaticPlaceholder implements Placeholder {
|
||||
/**
|
||||
* The name of the placeholder.
|
||||
*/
|
||||
private final String identifier;
|
||||
|
||||
/**
|
||||
* The function to retrieve the output of the placeholder.
|
||||
*/
|
||||
private final Supplier<String> function;
|
||||
|
||||
/**
|
||||
* Create a new player placeholder.
|
||||
*
|
||||
* @param identifier The identifier.
|
||||
* @param function The function to retrieve the value.
|
||||
*/
|
||||
public StaticPlaceholder(@NotNull final String identifier,
|
||||
@NotNull final Supplier<String> function) {
|
||||
this.identifier = identifier;
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the placeholder.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
public String getValue() {
|
||||
return function.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EcoPlugin getPlugin() {
|
||||
return Eco.getHandler().getEcoPlugin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return this.identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof StaticPlaceholder that)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(this.getIdentifier(), that.getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.getIdentifier());
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,9 @@ package com.willfp.eco.core.proxy;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Proxy / NMS constants.
|
||||
*/
|
||||
@@ -11,6 +14,15 @@ public final class ProxyConstants {
|
||||
*/
|
||||
public static final String NMS_VERSION = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
|
||||
/**
|
||||
* All supported NMS versions.
|
||||
*/
|
||||
public static final List<String> SUPPORTED_VERSIONS = Arrays.asList(
|
||||
"v1_17_R1",
|
||||
"v1_18_R1",
|
||||
"v1_18_R2"
|
||||
);
|
||||
|
||||
private ProxyConstants() {
|
||||
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.willfp.eco.core.recipe;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.willfp.eco.core.EcoPlugin;
|
||||
@@ -14,6 +16,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Utility class to manage and register crafting recipes.
|
||||
@@ -24,6 +27,14 @@ public final class Recipes {
|
||||
*/
|
||||
private static final BiMap<NamespacedKey, CraftingRecipe> RECIPES = HashBiMap.create();
|
||||
|
||||
/**
|
||||
* 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()
|
||||
);
|
||||
|
||||
/**
|
||||
* Register a recipe.
|
||||
@@ -32,6 +43,7 @@ public final class Recipes {
|
||||
*/
|
||||
public static void register(@NotNull final CraftingRecipe recipe) {
|
||||
RECIPES.forcePut(recipe.getKey(), recipe);
|
||||
RECIPES_FROM_MATRIX.invalidateAll();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,7 +54,7 @@ public final class Recipes {
|
||||
*/
|
||||
@Nullable
|
||||
public static CraftingRecipe getMatch(@NotNull final ItemStack[] matrix) {
|
||||
return RECIPES.values().stream().filter(recipe -> recipe.test(matrix)).findFirst().orElse(null);
|
||||
return RECIPES_FROM_MATRIX.get(matrix).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.willfp.eco.core.recipe.parts;
|
||||
|
||||
import com.willfp.eco.core.items.TestableItem;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A group of testable items.
|
||||
*
|
||||
* @see com.willfp.eco.core.items.CustomItem
|
||||
*/
|
||||
public class GroupedTestableItems implements TestableItem {
|
||||
/**
|
||||
* The children.
|
||||
*/
|
||||
private final Collection<TestableItem> children;
|
||||
|
||||
/**
|
||||
* Create a new group of testable items.
|
||||
*
|
||||
* @param children The children.
|
||||
*/
|
||||
public GroupedTestableItems(@NotNull final Collection<TestableItem> children) {
|
||||
Validate.isTrue(!children.isEmpty(), "Group must have at least one child!");
|
||||
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the item matches any children.
|
||||
*
|
||||
* @param itemStack The item to test.
|
||||
* @return If the item matches the test of any children..
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(@Nullable final ItemStack itemStack) {
|
||||
for (TestableItem child : children) {
|
||||
if (child.matches(itemStack)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem() {
|
||||
for (TestableItem child : children) {
|
||||
return child.getItem();
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @return The children.
|
||||
*/
|
||||
public Collection<TestableItem> getChildren() {
|
||||
return new ArrayList<>(children);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -3,11 +3,12 @@ package com.willfp.eco.core.recipe.recipes;
|
||||
import com.willfp.eco.core.Eco;
|
||||
import com.willfp.eco.core.EcoPlugin;
|
||||
import com.willfp.eco.core.PluginDependent;
|
||||
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 +67,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,29 +104,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<>();
|
||||
|
||||
if (Prerequisite.HAS_1_18.isMet() && !Prerequisite.HAS_PAPER.isMet()) {
|
||||
if (Bukkit.getServer().getRecipe(this.getKey()) != null) {
|
||||
return;
|
||||
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));
|
||||
}
|
||||
|
||||
Bukkit.getServer().addRecipe(shapedRecipe);
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
package com.willfp.eco.core.recipe.recipes;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.willfp.eco.core.Eco;
|
||||
import com.willfp.eco.core.EcoPlugin;
|
||||
import com.willfp.eco.core.PluginDependent;
|
||||
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 org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.RecipeChoice;
|
||||
import org.bukkit.inventory.ShapelessRecipe;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Shapeless crafting recipe.
|
||||
*/
|
||||
@Beta
|
||||
public final class ShapelessCraftingRecipe extends PluginDependent<EcoPlugin> implements CraftingRecipe {
|
||||
/**
|
||||
* Recipe parts.
|
||||
*/
|
||||
private final List<TestableItem> parts;
|
||||
|
||||
/**
|
||||
* The key of the recipe.
|
||||
*/
|
||||
private final NamespacedKey key;
|
||||
|
||||
/**
|
||||
* The key of the displayed recipe.
|
||||
*/
|
||||
private final NamespacedKey displayedKey;
|
||||
|
||||
/**
|
||||
* The recipe's output.
|
||||
*/
|
||||
private final ItemStack output;
|
||||
|
||||
/**
|
||||
* The permission.
|
||||
*/
|
||||
private final String permission;
|
||||
|
||||
private ShapelessCraftingRecipe(@NotNull final EcoPlugin plugin,
|
||||
@NotNull final String key,
|
||||
@NotNull final List<TestableItem> parts,
|
||||
@NotNull final ItemStack output,
|
||||
@Nullable final String permission) {
|
||||
super(plugin);
|
||||
|
||||
this.parts = parts;
|
||||
this.key = plugin.getNamespacedKeyFactory().create(key);
|
||||
this.displayedKey = plugin.getNamespacedKeyFactory().create(key + "_displayed");
|
||||
this.output = output;
|
||||
this.permission = permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a new test.
|
||||
*
|
||||
* @return The test.
|
||||
*/
|
||||
@NotNull
|
||||
public RecipeTest newTest() {
|
||||
return new RecipeTest(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(@NotNull final ItemStack[] matrix) {
|
||||
RecipeTest test = newTest();
|
||||
|
||||
for (ItemStack stack : matrix) {
|
||||
if (test.matchAndRemove(stack) == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register() {
|
||||
Recipes.register(this);
|
||||
|
||||
Bukkit.getServer().removeRecipe(this.getKey());
|
||||
Bukkit.getServer().removeRecipe(this.getDisplayedKey());
|
||||
|
||||
ShapelessRecipe shapelessRecipe = new ShapelessRecipe(this.getKey(), this.getOutput());
|
||||
for (TestableItem part : parts) {
|
||||
shapelessRecipe.addIngredient(part.getItem().getType());
|
||||
}
|
||||
|
||||
ShapelessRecipe displayedRecipe = new ShapelessRecipe(this.getDisplayedKey(), this.getOutput());
|
||||
for (TestableItem part : parts) {
|
||||
List<TestableItem> items = new ArrayList<>();
|
||||
if (part instanceof GroupedTestableItems group) {
|
||||
items.addAll(group.getChildren());
|
||||
} else {
|
||||
items.add(part);
|
||||
}
|
||||
|
||||
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.addIngredient(new RecipeChoice.ExactChoice(displayedItems));
|
||||
}
|
||||
|
||||
Bukkit.getServer().addRecipe(shapelessRecipe);
|
||||
Bukkit.getServer().addRecipe(displayedRecipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new recipe builder.
|
||||
*
|
||||
* @param plugin The plugin that owns the recipe.
|
||||
* @param key The recipe key.
|
||||
* @return A new builder.
|
||||
*/
|
||||
public static Builder builder(@NotNull final EcoPlugin plugin,
|
||||
@NotNull final String key) {
|
||||
return new Builder(plugin, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parts.
|
||||
*
|
||||
* @return The parts.
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public List<TestableItem> getParts() {
|
||||
return this.parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the key.
|
||||
*
|
||||
* @return The key.
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public NamespacedKey getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the displayed key.
|
||||
*
|
||||
* @return The displayed key.
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public NamespacedKey getDisplayedKey() {
|
||||
return this.displayedKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output.
|
||||
*
|
||||
* @return The output.
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemStack getOutput() {
|
||||
return this.output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the permission.
|
||||
*
|
||||
* @return The permission.
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public String getPermission() {
|
||||
return permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for recipes.
|
||||
*/
|
||||
public static final class Builder {
|
||||
/**
|
||||
* The recipe parts.
|
||||
*/
|
||||
private final List<TestableItem> recipeParts = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The output of the recipe.
|
||||
*/
|
||||
private ItemStack output = null;
|
||||
|
||||
/**
|
||||
* The permission for the recipe.
|
||||
*/
|
||||
private String permission = null;
|
||||
|
||||
/**
|
||||
* The key of the recipe.
|
||||
*/
|
||||
private final String key;
|
||||
|
||||
/**
|
||||
* The plugin that created the recipe.
|
||||
*/
|
||||
private final EcoPlugin plugin;
|
||||
|
||||
/**
|
||||
* Create a new recipe builder.
|
||||
*
|
||||
* @param plugin The plugin that owns the recipe.
|
||||
* @param key The recipe key.
|
||||
*/
|
||||
private Builder(@NotNull final EcoPlugin plugin,
|
||||
@NotNull final String key) {
|
||||
this.key = key;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a recipe part.
|
||||
*
|
||||
* @param part The part of the recipe.
|
||||
* @return The builder.
|
||||
*/
|
||||
public Builder addRecipePart(@NotNull final TestableItem part) {
|
||||
recipeParts.add(part);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the output of the recipe.
|
||||
*
|
||||
* @param output The output.
|
||||
* @return The builder.
|
||||
*/
|
||||
public Builder setOutput(@NotNull final ItemStack output) {
|
||||
this.output = output;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the permission required to craft the recipe.
|
||||
*
|
||||
* @param permission The permission.
|
||||
* @return The builder.
|
||||
*/
|
||||
public Builder setPermission(@Nullable final String permission) {
|
||||
this.permission = permission;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if recipe parts are all air.
|
||||
*
|
||||
* @return If recipe parts are all air.
|
||||
*/
|
||||
public boolean isAir() {
|
||||
for (TestableItem recipePart : this.recipeParts) {
|
||||
if (recipePart != null && !(recipePart instanceof EmptyTestableItem)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the recipe.
|
||||
*
|
||||
* @return The built recipe.
|
||||
*/
|
||||
public ShapelessCraftingRecipe build() {
|
||||
return new ShapelessCraftingRecipe(plugin, key.toLowerCase(), recipeParts, output, permission);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for shapeless recipes.
|
||||
*/
|
||||
public static final class RecipeTest {
|
||||
/**
|
||||
* The remaining items left to be found.
|
||||
*/
|
||||
private final List<TestableItem> remaining;
|
||||
|
||||
private RecipeTest(@NotNull final ShapelessCraftingRecipe recipe) {
|
||||
this.remaining = new ArrayList<>(recipe.getParts());
|
||||
}
|
||||
|
||||
/**
|
||||
* If the item is in the recipe, remove it from the remaining items to test and
|
||||
* return the matching item.
|
||||
*
|
||||
* @param itemStack The item.
|
||||
* @return The matching item, or null if no match was found.
|
||||
*/
|
||||
@Nullable
|
||||
public TestableItem matchAndRemove(@NotNull final ItemStack itemStack) {
|
||||
if (remaining.isEmpty() && !(new EmptyTestableItem().matches(itemStack))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Optional<TestableItem> match = remaining.stream()
|
||||
.filter(item -> item.matches(itemStack))
|
||||
.findFirst();
|
||||
|
||||
match.ifPresent(remaining::remove);
|
||||
|
||||
return match.orElse(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.willfp.eco.core.requirement;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
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.
|
||||
*/
|
||||
@Deprecated(since = "6.24.0", forRemoval = true)
|
||||
@SuppressWarnings("DeprecatedIsStillUsed")
|
||||
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);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.willfp.eco.core.requirement;
|
||||
|
||||
import com.willfp.eco.core.Eco;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Interface for internal requirement factory implementations.
|
||||
*
|
||||
* @deprecated See {@link Requirement}.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@Eco.HandlerComponent
|
||||
@Deprecated(since = "6.24.0", forRemoval = true)
|
||||
@SuppressWarnings({"removal", "DeprecatedIsStillUsed"})
|
||||
public interface RequirementFactory {
|
||||
/**
|
||||
* Create a requirement.
|
||||
*
|
||||
* @param name The name.
|
||||
* @return The requirement returned for the name.
|
||||
* Will return a requirement that is always true if not found.
|
||||
*/
|
||||
Requirement create(@NotNull String name);
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package com.willfp.eco.core.requirement;
|
||||
|
||||
import com.willfp.eco.core.Eco;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Contains methods and fields pertaining to requirements.
|
||||
*
|
||||
* @deprecated See {@link Requirement}.
|
||||
*/
|
||||
@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 = Eco.getHandler().getRequirementFactory().create("has-permission");
|
||||
|
||||
/**
|
||||
* Placeholder equals value.
|
||||
*/
|
||||
public static final Requirement PLACEHOLDER_EQUALS = Eco.getHandler().getRequirementFactory().create("placeholder-equals");
|
||||
|
||||
/**
|
||||
* Numeric placeholder greater than value.
|
||||
*/
|
||||
public static final Requirement PLACEHOLDER_GREATER_THAN = Eco.getHandler().getRequirementFactory().create("placeholder-greater-than");
|
||||
|
||||
/**
|
||||
* Numeric placeholder less than value.
|
||||
*/
|
||||
public static final Requirement PLACEHOLDER_LESS_THAN = Eco.getHandler().getRequirementFactory().create("placeholder-less-than");
|
||||
|
||||
/**
|
||||
* Get Requirements matching ID.
|
||||
*
|
||||
* @param name The ID to search for.
|
||||
* @return The matching Requirements.
|
||||
*/
|
||||
public static Requirement getByID(@NotNull final String name) {
|
||||
return Eco.getHandler().getRequirementFactory().create(name);
|
||||
}
|
||||
|
||||
private Requirements() {
|
||||
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.willfp.eco.util;
|
||||
|
||||
import com.willfp.eco.core.placeholder.StaticPlaceholder;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@@ -7,10 +8,10 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* Utilities / API methods for numbers.
|
||||
@@ -24,7 +25,7 @@ public final class NumberUtils {
|
||||
/**
|
||||
* Crunch handler.
|
||||
*/
|
||||
private static BiFunction<String, Player, Double> crunch = null;
|
||||
private static CrunchHandler crunch = null;
|
||||
|
||||
/**
|
||||
* Set of roman numerals to look up.
|
||||
@@ -251,7 +252,21 @@ public final class NumberUtils {
|
||||
*/
|
||||
public static double evaluateExpression(@NotNull final String expression,
|
||||
@Nullable final Player player) {
|
||||
return crunch.apply(expression, player);
|
||||
return evaluateExpression(expression, player, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate an expression with respect to a player (for placeholders).
|
||||
*
|
||||
* @param expression The expression.
|
||||
* @param player The player.
|
||||
* @param statics The static placeholders.
|
||||
* @return The value of the expression, or zero if invalid.
|
||||
*/
|
||||
public static double evaluateExpression(@NotNull final String expression,
|
||||
@Nullable final Player player,
|
||||
@NotNull final Iterable<StaticPlaceholder> statics) {
|
||||
return crunch.evaluate(expression, player, statics);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,11 +275,29 @@ public final class NumberUtils {
|
||||
* @param handler The handler.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public static void initCrunch(@NotNull final BiFunction<String, Player, Double> handler) {
|
||||
public static void initCrunch(@NotNull final CrunchHandler handler) {
|
||||
Validate.isTrue(crunch == null, "Already initialized!");
|
||||
crunch = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bridge component for crunch.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public interface CrunchHandler {
|
||||
/**
|
||||
* Evaluate an expression.
|
||||
*
|
||||
* @param expression The expression.
|
||||
* @param player The player.
|
||||
* @param statics The statics.
|
||||
* @return The value of the expression, or zero if invalid.
|
||||
*/
|
||||
double evaluate(@NotNull String expression,
|
||||
@Nullable Player player,
|
||||
@NotNull Iterable<StaticPlaceholder> statics);
|
||||
}
|
||||
|
||||
private NumberUtils() {
|
||||
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -121,6 +120,14 @@ public final class StringUtils {
|
||||
.put("§k", ChatColor.MAGIC)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Regex map for splitting values.
|
||||
*/
|
||||
private static final LoadingCache<String, Pattern> SPACE_AROUND_CHARACTER = Caffeine.newBuilder()
|
||||
.build(
|
||||
character -> Pattern.compile("( " + Pattern.quote(character) + " )")
|
||||
);
|
||||
|
||||
/**
|
||||
* Format a list of strings.
|
||||
* <p>
|
||||
@@ -316,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) {
|
||||
@@ -419,9 +425,23 @@ public final class StringUtils {
|
||||
*
|
||||
* @param object The object to convert to string.
|
||||
* @return The object stringified.
|
||||
* @deprecated Poorly named method. Use {@link StringUtils#toNiceString(Object)} instead.
|
||||
*/
|
||||
@NotNull
|
||||
@Deprecated(since = "6.26.0", forRemoval = true)
|
||||
public static String internalToString(@Nullable final Object object) {
|
||||
return toNiceString(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation of {@link String#valueOf}.
|
||||
* Formats collections and doubles better.
|
||||
*
|
||||
* @param object The object to convert to string.
|
||||
* @return The object stringified.
|
||||
*/
|
||||
@NotNull
|
||||
public static String toNiceString(@Nullable final Object object) {
|
||||
if (object == null) {
|
||||
return "null";
|
||||
}
|
||||
@@ -433,7 +453,7 @@ public final class StringUtils {
|
||||
} else if (object instanceof Double) {
|
||||
return NumberUtils.format((Double) object);
|
||||
} else if (object instanceof Collection<?> c) {
|
||||
return c.stream().map(StringUtils::internalToString).collect(Collectors.joining(", "));
|
||||
return c.stream().map(StringUtils::toNiceString).collect(Collectors.joining(", "));
|
||||
} else {
|
||||
return String.valueOf(object);
|
||||
}
|
||||
@@ -560,6 +580,22 @@ public final class StringUtils {
|
||||
return tokens.toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split input string around separator surrounded by spaces.
|
||||
* <p>
|
||||
* e.g. {@code splitAround("hello ? how are you", "?")} will split, but
|
||||
* {@code splitAround("hello? how are you", "?")} will not.
|
||||
*
|
||||
* @param input Input string.
|
||||
* @param separator Separator.
|
||||
* @return The split string.
|
||||
*/
|
||||
@NotNull
|
||||
public static String[] splitAround(@NotNull final String input,
|
||||
@NotNull final String separator) {
|
||||
return SPACE_AROUND_CHARACTER.get(separator).split(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for formatting.
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.willfp.eco.util;
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.willfp.eco.core.Prerequisite;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
@@ -91,17 +90,15 @@ public final class TeamUtils {
|
||||
MATERIAL_COLORS.put(Material.EMERALD_ORE, ChatColor.GREEN);
|
||||
MATERIAL_COLORS.put(Material.ANCIENT_DEBRIS, ChatColor.DARK_RED);
|
||||
|
||||
if (Prerequisite.HAS_1_17.isMet()) {
|
||||
MATERIAL_COLORS.put(Material.COPPER_ORE, ChatColor.GOLD);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_COPPER_ORE, ChatColor.GOLD);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_COAL_ORE, ChatColor.BLACK);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_IRON_ORE, ChatColor.GRAY);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_GOLD_ORE, ChatColor.YELLOW);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_LAPIS_ORE, ChatColor.BLUE);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_REDSTONE_ORE, ChatColor.RED);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_DIAMOND_ORE, ChatColor.AQUA);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_EMERALD_ORE, ChatColor.GREEN);
|
||||
}
|
||||
MATERIAL_COLORS.put(Material.COPPER_ORE, ChatColor.GOLD);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_COPPER_ORE, ChatColor.GOLD);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_COAL_ORE, ChatColor.BLACK);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_IRON_ORE, ChatColor.GRAY);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_GOLD_ORE, ChatColor.YELLOW);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_LAPIS_ORE, ChatColor.BLUE);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_REDSTONE_ORE, ChatColor.RED);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_DIAMOND_ORE, ChatColor.AQUA);
|
||||
MATERIAL_COLORS.put(Material.DEEPSLATE_EMERALD_ORE, ChatColor.GREEN);
|
||||
}
|
||||
|
||||
private TeamUtils() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user