diff --git a/plugins/fancynpcs/Jenkinsfile b/plugins/fancynpcs/Jenkinsfile new file mode 100644 index 00000000..a2b906ee --- /dev/null +++ b/plugins/fancynpcs/Jenkinsfile @@ -0,0 +1,70 @@ +/* + Required env: java 21, git + Required plugins: discord notifier + Required credentials: MODRINTH_PUBLISH_API_TOKEN, HANGAR_PUBLISH_API_TOKEN +*/ + +pipeline { + agent any + + environment { + GRADLE_OPTS = '-Dorg.gradle.daemon=false' + } + + stages { + stage('Checkout') { + steps { + git url: 'https://github.com/FancyMcPlugins/FancyNpcs', branch: 'main' + } + } + + stage('Build') { + steps { + sh 'chmod +x gradlew' + sh './gradlew clean shadowJar' + echo 'Built the plugin!' + } + } + + stage('Deploy') { + steps { + // Load the secrets and make them available as environment variables + withCredentials([ + string(credentialsId: 'MODRINTH_PUBLISH_API_TOKEN', variable: 'MODRINTH_PUBLISH_API_TOKEN'), + string(credentialsId: 'HANGAR_PUBLISH_API_TOKEN', variable: 'HANGAR_PUBLISH_API_TOKEN') + ]) { + sh 'export MODRINTH_PUBLISH_API_TOKEN=${MODRINTH_PUBLISH_API_TOKEN} && ./gradlew modrinth' + echo 'Published to Modrinth!' + + sh 'export HANGAR_PUBLISH_API_TOKEN=${HANGAR_PUBLISH_API_TOKEN} && ./gradlew publishAllPublicationsToHangar' + echo 'Published to Hangar!' + } + } + } + } + + post { + always { + archiveArtifacts artifacts: '**/build/libs/FancyNpcs-*.jar', allowEmptyArchive: true + } + success { + withCredentials([ + string(credentialsId: 'DISC_WEBHOOK_URL', variable: 'DISC_WEBHOOK_URL') + ]) { + discordSend description: "**Build:** ${env.BUILD_NUMBER} \n**Status:** ${currentBuild.currentResult} \n**Download:** https://modrinth.com/plugin/fancynpcs/versions", + footer: "Jenkins Pipeline", link: env.BUILD_URL, result: 'SUCCESS', title: "FancyNpcs #${env.BUILD_NUMBER}", webhookURL: "${DISC_WEBHOOK_URL}" + } + echo 'Build was successful!' + } + failure { + script { + withCredentials([ + string(credentialsId: 'DISC_WEBHOOK_URL', variable: 'DISC_WEBHOOK_URL') + ]) { + discordSend description: "**Build:** ${env.BUILD_NUMBER} \n**Status:** ${currentBuild.currentResult}", footer: "Jenkins Pipeline", link: env.BUILD_URL, result: 'FAILURE', title: "FancyNpcs #${env.BUILD_NUMBER}", "${DISC_WEBHOOK_URL}" + } + } + echo 'Build failed!' + } + } +} diff --git a/plugins/fancynpcs/README.md b/plugins/fancynpcs/README.md new file mode 100644 index 00000000..2a48390f --- /dev/null +++ b/plugins/fancynpcs/README.md @@ -0,0 +1,146 @@ +
+ +![Banner](https://github.com/FancyMcPlugins/FancyNpcs/blob/main/images/banner.png?raw=true) + +[![GitHub Release](https://img.shields.io/github/v/release/FancyMcPlugins/FancyNpcs?logo=github&labelColor=%2324292F&color=%23454F5A)](https://github.com/FancyMcPlugins/FancyNpcs/releases/latest) +[![Supports Folia](https://img.shields.io/badge/folia-supported-%23F9D879?labelColor=%2313154E&color=%234A44A6)](https://papermc.io/software/folia) +[![Discord](https://img.shields.io/discord/899740810956910683?cacheSeconds=3600&logo=discord&logoColor=white&label=%20&labelColor=%235865F2&color=%23707BF4)](https://discord.gg/ZUgYCEJUEx) +[![GitHub Downloads](https://img.shields.io/github/downloads/FancyMcPlugins/FancyNpcs/total?logo=github&labelColor=%2324292F&color=%23454F5A)](https://github.com/FancyMcPlugins/FancyNpcs/releases/latest) +[![Modrinth Downloads](https://img.shields.io/modrinth/dt/fancynpcs?logo=modrinth&logoColor=white&label=downloads&labelColor=%23139549&color=%2318c25f)](https://modrinth.com/plugin/fancynpcs) +[![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/FancyMcPlugins/FancyNpcs?logo=codefactor&logoColor=white&label=%20)](https://www.codefactor.io/repository/github/fancymcplugins/fancynpcs/issues/main) + +[![Modrinth](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/available/modrinth_vector.svg)](https://modrinth.com/plugin/fancynpcs) +[![Hangar](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/available/hangar_vector.svg)](https://hangar.papermc.io/Oliver/FancyNpcs) + +
+ +Simple, lightweight and feature-rich NPC plugin for **[Paper](https://papermc.io/software/paper)** (and **[Folia](https://papermc.io/software/folia)**) servers using packets. + +
+ +## Features + +With this plugin you can create NPCs with customizable properties like: + +- **Type** (Cow, Pig, Player, etc.) +- **Skin** (from username, texture URL or placeholder) +- **Glowing** (in all colors) +- **Attributes** (pose, visibility, variant, etc.) +- **Equipment** (eg. holding a diamond sword and wearing leather armor) +- **Interactions** (execute commands, send messages etc.) +- ...and much more! + +Check out **[images section](#images)** down below. + +
+ +## Installation + +Paper **1.19.4** - **1.21.5** with **Java 21** (or higher) is required. Plugin should also work on **Paper** forks. + +**Spigot** is **not** supported. + +### Download (Stable) + +- **[Hangar](https://hangar.papermc.io/Oliver/FancyNpcs)** +- **[Modrinth](https://modrinth.com/plugin/fancynpcs)** +- **[GitHub Releases](https://github.com/FancyMcPlugins/FancyNpcs/releases)** + +### Download (Development Builds) + +- **[Jenkins CI](https://jenkins.fancyplugins.de/job/FancyNpcs/)** +- **[FancyPlugins Website](https://fancyplugins.de/FancyNpcs/download)** + +
+ +## Documentation + +Official documentation is hosted **[here](https://fancyplugins.de/docs/fancynpcs.html)**. Quick reference: + +- **[Getting Started](https://fancyplugins.de/docs/fn-getting-started.html)** +- **[Command Reference](https://fancyplugins.de/docs/fn-commands.html)** +- **[Using API](https://fancyplugins.de/docs/fn-api.html)** + +**Have more questions?** Feel free to ask them on our **[Discord](https://discord.gg/ZUgYCEJUEx)** server. + +
+ +## Developer API + +More information can be found in **[Documentation](https://fancyplugins.de/docs/fn-api.html)** and **[Javadocs](https://repo.fancyplugins.de/javadoc/releases/de/oliver/FancyNpcs/latest)**. + +### Maven + +```xml + + + fancyplugins-releases + FancyPlugins Repository + https://repo.fancyplugins.de/releases + +``` + +```xml + + + de.oliver + FancyNpcs + [VERSION] + provided + +``` + +### Gradle + +```groovy +repositories { + maven("https://repo.fancyplugins.de/releases") +} + +dependencies { + compileOnly("de.oliver:FancyNpcs:[VERSION]") +} +``` + +
+ +## Building + +Follow these steps to build the plugin locally: + +```shell +# Cloning repository. +$ git clone https://github.com/FancyMcPlugins/FancyNpcs.git +# Entering cloned repository. +$ cd FancyNpcs +# Compiling and building artifacts. +$ gradlew shadowJar +# Once successfully built, plugin .jar can be found in /build/libs directory. +``` + +
+ +## Images + +Images showcasing the plugin, sent to us by our community. + +![Screenshot 1](https://github.com/FancyMcPlugins/FancyNpcs/blob/main/images/screenshots/niceron1.jpeg?raw=true) +Provided by [Explorer's Eden](https://explorerseden.eu/) + +![Screenshot 2](https://github.com/FancyMcPlugins/FancyNpcs/blob/main/images/screenshots/niceron2.jpeg?raw=true) +Provided by [Explorer's Eden](https://explorerseden.eu/) + +![Screenshot 3](https://github.com/FancyMcPlugins/FancyNpcs/blob/main/images/screenshots/niceron3.jpeg?raw=true) +Provided by [Explorer's Eden](https://explorerseden.eu/) + +![Screenshot 4](https://github.com/FancyMcPlugins/FancyNpcs/blob/main/images/screenshots/dave1.jpeg?raw=true) +Provided by [Beacon's Quest](https://www.beaconsquest.net/) + +![Screenshot 5](https://github.com/FancyMcPlugins/FancyNpcs/blob/main/images/screenshots/oliver1.jpeg?raw=true) +Provided by [@OliverSchlueter](https://github.com/OliverSchlueter) + +![Screenshot 6](https://github.com/FancyMcPlugins/FancyNpcs/blob/main/images/screenshots/oliver2.jpeg?raw=true) +Provided by [@OliverSchlueter](https://github.com/OliverSchlueter) + +![Screenshot 7](https://github.com/FancyMcPlugins/FancyNpcs/blob/main/images/screenshots/grabsky1.jpeg?raw=true) +Provided by [@Grabsky](https://github.com/Grabsky) diff --git a/plugins/fancynpcs/api/build.gradle.kts b/plugins/fancynpcs/api/build.gradle.kts new file mode 100644 index 00000000..b656c8c2 --- /dev/null +++ b/plugins/fancynpcs/api/build.gradle.kts @@ -0,0 +1,71 @@ +plugins { + id("java-library") + id("maven-publish") + id("com.gradleup.shadow") +} + +val minecraftVersion = "1.19.4" + +dependencies { + compileOnly("io.papermc.paper:paper-api:$minecraftVersion-R0.1-SNAPSHOT") + + compileOnly("de.oliver:FancyLib:37") + compileOnly("de.oliver.FancyAnalytics:logger:0.0.6") + + implementation("org.lushplugins:ChatColorHandler:5.1.3") +} + +tasks { + shadowJar { + archiveClassifier.set("") + + relocate("org.lushplugins.chatcolorhandler", "de.oliver.fancynpcs.libs.chatcolorhandler") + } + + publishing { + repositories { + maven { + name = "fancypluginsReleases" + url = uri("https://repo.fancyplugins.de/releases") + credentials(PasswordCredentials::class) + authentication { + isAllowInsecureProtocol = true + create("basic") + } + } + + maven { + name = "fancypluginsSnapshots" + url = uri("https://repo.fancyplugins.de/snapshots") + credentials(PasswordCredentials::class) + authentication { + isAllowInsecureProtocol = true + create("basic") + } + } + } + publications { + create("maven") { + groupId = rootProject.group.toString() + artifactId = rootProject.name + version = rootProject.version.toString() + from(project.components["java"]) + } + } + } + + java { + withSourcesJar() + withJavadocJar() + } + + javadoc { + options.encoding = Charsets.UTF_8.name() + } + + compileJava { + options.encoding = Charsets.UTF_8.name() + options.release = 17 + + } +} \ No newline at end of file diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/AttributeManager.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/AttributeManager.java new file mode 100644 index 00000000..6a8af52f --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/AttributeManager.java @@ -0,0 +1,16 @@ +package de.oliver.fancynpcs.api; + +import org.bukkit.entity.EntityType; + +import java.util.List; + +public interface AttributeManager { + + NpcAttribute getAttributeByName(EntityType type, String name); + + List getAllAttributes(); + + List getAllAttributesForEntityType(EntityType type); + + void registerAttribute(NpcAttribute attribute); +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/FancyNpcsConfig.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/FancyNpcsConfig.java new file mode 100644 index 00000000..930b1518 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/FancyNpcsConfig.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.api; + +import java.util.List; +import java.util.Map; + +public interface FancyNpcsConfig { + + boolean isSkipInvisibleNpcs(); + + boolean isInteractionCooldownMessageDisabled(); + + boolean isMuteVersionNotification(); + + boolean isEnableAutoSave(); + + int getAutoSaveInterval(); + + int getNpcUpdateInterval(); + + int getNpcUpdateVisibilityInterval(); + + int getTurnToPlayerDistance(); + + boolean isTurnToPlayerResetToInitialDirection(); + + int getVisibilityDistance(); + + int getRemoveNpcsFromPlayerlistDelay(); + + String getMineSkinApiKey(); + + List getBlockedCommands(); + + Map getMaxNpcsPerPermission(); + +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/FancyNpcsPlugin.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/FancyNpcsPlugin.java new file mode 100644 index 00000000..a582a1a9 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/FancyNpcsPlugin.java @@ -0,0 +1,56 @@ +package de.oliver.fancynpcs.api; + +import de.oliver.fancyanalytics.logger.ExtendedFancyLogger; +import de.oliver.fancylib.serverSoftware.schedulers.FancyScheduler; +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.api.actions.ActionManager; +import de.oliver.fancynpcs.api.skins.SkinManager; +import org.bukkit.Bukkit; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.ApiStatus; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Function; + +public interface FancyNpcsPlugin { + + static FancyNpcsPlugin get() { + PluginManager pluginManager = Bukkit.getPluginManager(); + + if (pluginManager.isPluginEnabled("FancyNpcs")) { + return (FancyNpcsPlugin) pluginManager.getPlugin("FancyNpcs"); + } + + throw new NullPointerException("Plugin is not enabled"); + } + + JavaPlugin getPlugin(); + + ExtendedFancyLogger getFancyLogger(); + + ScheduledExecutorService getNpcThread(); + + /** + * Creates a new thread with the given name and runnable. + * Warning: Do not use this method, it is for internal use only. + */ + @ApiStatus.Internal + Thread newThread(String name, Runnable runnable); + + FancyScheduler getScheduler(); + + Function getNpcAdapter(); + + FancyNpcsConfig getFancyNpcConfig(); + + NpcManager getNpcManager(); + + AttributeManager getAttributeManager(); + + ActionManager getActionManager(); + + SkinManager getSkinManager(); + + Translator getTranslator(); +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/Npc.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/Npc.java new file mode 100644 index 00000000..62ce4a5d --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/Npc.java @@ -0,0 +1,227 @@ +package de.oliver.fancynpcs.api; + +import de.oliver.fancylib.RandomUtils; +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.api.actions.ActionTrigger; +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.api.actions.executor.ActionExecutor; +import de.oliver.fancynpcs.api.events.NpcInteractEvent; +import de.oliver.fancynpcs.api.utils.Interval; +import de.oliver.fancynpcs.api.utils.Interval.Unit; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public abstract class Npc { + + private static final NpcAttribute INVISIBLE_ATTRIBUTE = FancyNpcsPlugin.get().getAttributeManager().getAttributeByName(EntityType.PLAYER, "invisible"); + private static final char[] localNameChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'k', 'l', 'm', 'n', 'o', 'r'}; + protected final Map isTeamCreated = new ConcurrentHashMap<>(); + protected final Map isVisibleForPlayer = new ConcurrentHashMap<>(); + protected final Map isLookingAtPlayer = new ConcurrentHashMap<>(); + protected final Map lastPlayerInteraction = new ConcurrentHashMap<>(); + private final Translator translator = FancyNpcsPlugin.get().getTranslator(); + protected NpcData data; + protected boolean saveToFile; + + public Npc(NpcData data) { + this.data = data; + this.saveToFile = true; + } + + protected String generateLocalName() { + String localName = ""; + for (int i = 0; i < 8; i++) { + localName += "&" + localNameChars[(int) RandomUtils.randomInRange(0, localNameChars.length)]; + } + + localName = ChatColor.translateAlternateColorCodes('&', localName); + + return localName; + } + + public abstract void create(); + + public abstract void spawn(Player player); + + public void spawnForAll() { + FancyNpcsPlugin.get().getNpcThread().submit(() -> { + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + spawn(onlinePlayer); + } + }); + } + + public abstract void remove(Player player); + + public void removeForAll() { + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + remove(onlinePlayer); + } + } + + /** + * Checks if the NPC should be visible for the player. + * + * @param player The player to check for. + * @return True if the NPC should be visible for the player, otherwise false. + */ + protected boolean shouldBeVisible(Player player) { + int visibilityDistance = (data.getVisibilityDistance() > -1) ? data.getVisibilityDistance() : FancyNpcsPlugin.get().getFancyNpcConfig().getVisibilityDistance(); + + if (visibilityDistance == 0) { + return false; + } + + if (!data.isSpawnEntity()) { + return false; + } + + if (data.getLocation() == null) { + return false; + } + + if (player.getLocation().getWorld() != data.getLocation().getWorld()) { + return false; + } + + if (visibilityDistance != Integer.MAX_VALUE && data.getLocation().distanceSquared(player.getLocation()) > visibilityDistance * visibilityDistance) { + return false; + } + + if (FancyNpcsPlugin.get().getFancyNpcConfig().isSkipInvisibleNpcs() && data.getAttributes().getOrDefault(INVISIBLE_ATTRIBUTE, "false").equalsIgnoreCase("true") && !data.isGlowing() && data.getEquipment().isEmpty()) { + return false; + } + + return true; + } + + public void checkAndUpdateVisibility(Player player) { + FancyNpcsPlugin.get().getNpcThread().submit(() -> { + boolean shouldBeVisible = shouldBeVisible(player); + boolean wasVisible = isVisibleForPlayer.getOrDefault(player.getUniqueId(), false); + + if (shouldBeVisible && !wasVisible) { + spawn(player); + } else if (!shouldBeVisible && wasVisible) { + remove(player); + } + }); + } + + public abstract void lookAt(Player player, Location location); + + public abstract void update(Player player); + + public void updateForAll() { + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + update(onlinePlayer); + } + } + + public abstract void move(Player player, boolean swingArm); + + public void move(Player player) { + move(player, true); + } + + public void moveForAll(boolean swingArm) { + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + move(onlinePlayer, swingArm); + } + } + + public void moveForAll() { + moveForAll(true); + } + + public void interact(Player player) { + interact(player, ActionTrigger.CUSTOM); + } + + public void interact(Player player, ActionTrigger actionTrigger) { + if (data.getInteractionCooldown() > 0) { + final long interactionCooldownMillis = (long) (data.getInteractionCooldown() * 1000); + final long lastInteractionMillis = lastPlayerInteraction.getOrDefault(player.getUniqueId(), 0L); + final Interval interactionCooldownLeft = Interval.between(lastInteractionMillis + interactionCooldownMillis, System.currentTimeMillis(), Unit.MILLISECONDS); + if (interactionCooldownLeft.as(Unit.MILLISECONDS) > 0) { + + if (!FancyNpcsPlugin.get().getFancyNpcConfig().isInteractionCooldownMessageDisabled()) { + translator.translate("interaction_on_cooldown").replace("time", interactionCooldownLeft.toString()).send(player); + } + + return; + } + lastPlayerInteraction.put(player.getUniqueId(), System.currentTimeMillis()); + } + + List actions = data.getActions(actionTrigger); + NpcInteractEvent npcInteractEvent = new NpcInteractEvent(this, data.getOnClick(), actions, player, actionTrigger); + npcInteractEvent.callEvent(); + + if (npcInteractEvent.isCancelled()) { + return; + } + + // onClick + if (data.getOnClick() != null) { + data.getOnClick().accept(player); + } + + // actions + ActionExecutor.execute(actionTrigger, this, player); + + if (actionTrigger == ActionTrigger.LEFT_CLICK || actionTrigger == ActionTrigger.RIGHT_CLICK) { + ActionExecutor.execute(ActionTrigger.ANY_CLICK, this, player); + } + } + + protected abstract void refreshEntityData(Player serverPlayer); + + public abstract int getEntityId(); + + public NpcData getData() { + return data; + } + + public abstract float getEyeHeight(); + + public Map getIsTeamCreated() { + return isTeamCreated; + } + + public Map getIsVisibleForPlayer() { + return isVisibleForPlayer; + } + + public Map getIsLookingAtPlayer() { + return isLookingAtPlayer; + } + + public Map getLastPlayerInteraction() { + return lastPlayerInteraction; + } + + public boolean isDirty() { + return data.isDirty(); + } + + public void setDirty(boolean dirty) { + data.setDirty(dirty); + } + + public boolean isSaveToFile() { + return saveToFile; + } + + public void setSaveToFile(boolean saveToFile) { + this.saveToFile = saveToFile; + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/NpcAttribute.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/NpcAttribute.java new file mode 100644 index 00000000..5981ecc3 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/NpcAttribute.java @@ -0,0 +1,52 @@ +package de.oliver.fancynpcs.api; + +import org.bukkit.entity.EntityType; + +import java.util.List; +import java.util.function.BiConsumer; + +public class NpcAttribute { + + private final String name; + private final List possibleValues; + private final List types; + private final BiConsumer applyFunc; // npc, value + + public NpcAttribute(String name, List possibleValues, List types, BiConsumer applyFunc) { + this.name = name; + this.possibleValues = possibleValues; + this.types = types; + this.applyFunc = applyFunc; + } + + public boolean isValidValue(String value) { + if (possibleValues.isEmpty()) { + return true; + } + + for (String pv : possibleValues) { + if (pv.equalsIgnoreCase(value)) { + return true; + } + } + + return false; + } + + public void apply(Npc npc, String value) { + applyFunc.accept(npc, value); + } + + public String getName() { + return name; + } + + public List getPossibleValues() { + return possibleValues; + } + + public List getTypes() { + return types; + } + +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/NpcData.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/NpcData.java new file mode 100644 index 00000000..786358f3 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/NpcData.java @@ -0,0 +1,384 @@ +package de.oliver.fancynpcs.api; + +import de.oliver.fancynpcs.api.actions.ActionTrigger; +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.api.skins.SkinData; +import de.oliver.fancynpcs.api.utils.NpcEquipmentSlot; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Location; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; + +public class NpcData { + + private final String id; + private final String name; + private final UUID creator; + private String displayName; + private SkinData skin; + private boolean mirrorSkin; + private Location location; + private boolean showInTab; + private boolean spawnEntity; + private boolean collidable; + private boolean glowing; + private NamedTextColor glowingColor; + private EntityType type; + private Map equipment; + private Consumer onClick; + private Map> actions; + private boolean turnToPlayer; + private float interactionCooldown; + private float scale; + private int visibilityDistance; + private Map attributes; + private boolean isDirty; + + public NpcData( + String id, + String name, + UUID creator, + String displayName, + SkinData skin, + Location location, + boolean showInTab, + boolean spawnEntity, + boolean collidable, + boolean glowing, + NamedTextColor glowingColor, + EntityType type, + Map equipment, + boolean turnToPlayer, + Consumer onClick, + Map> actions, + float interactionCooldown, + float scale, + int visibilityDistance, + Map attributes, + boolean mirrorSkin + ) { + this.id = id; + this.name = name; + this.creator = creator; + this.displayName = displayName; + this.skin = skin; + this.location = location; + this.showInTab = showInTab; + this.spawnEntity = spawnEntity; + this.collidable = collidable; + this.glowing = glowing; + this.glowingColor = glowingColor; + this.type = type; + this.equipment = equipment; + this.onClick = onClick; + this.actions = actions; + this.turnToPlayer = turnToPlayer; + this.interactionCooldown = interactionCooldown; + this.scale = scale; + this.visibilityDistance = visibilityDistance; + this.attributes = attributes; + this.mirrorSkin = mirrorSkin; + this.isDirty = true; + } + + /** + * Creates a default npc with random id + */ + public NpcData(String name, UUID creator, Location location) { + this.id = UUID.randomUUID().toString(); + this.name = name; + this.creator = creator; + this.location = location; + this.displayName = name; + this.type = EntityType.PLAYER; + this.showInTab = false; + this.spawnEntity = true; + this.collidable = true; + this.glowing = false; + this.glowingColor = NamedTextColor.WHITE; + this.onClick = p -> { + }; + this.actions = new ConcurrentHashMap<>(); + this.turnToPlayer = false; + this.interactionCooldown = 0; + this.scale = 1; + this.visibilityDistance = -1; + this.equipment = new ConcurrentHashMap<>(); + this.attributes = new ConcurrentHashMap<>(); + this.mirrorSkin = false; + this.isDirty = true; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public UUID getCreator() { + return creator == null ? UUID.fromString("00000000-0000-0000-0000-000000000000") : creator; + } + + public String getDisplayName() { + return displayName; + } + + public NpcData setDisplayName(String displayName) { + this.displayName = displayName; + isDirty = true; + return this; + } + + public SkinData getSkinData() { + return skin; + } + + /** + * Sets the skin data of the npc + * Use this method, if you have a loaded skin data object (with texture and signature), otherwise use {@link #setSkin(String, SkinData.SkinVariant)} + * + * @param skinData the skin data + */ + public NpcData setSkinData(SkinData skinData) { + this.skin = skinData; + isDirty = true; + return this; + } + + /** + * Loads the skin data and sets it as the skin of the npc + * + * @param skin a valid UUID, username, URL or file path + * @param variant the skin variant + */ + public NpcData setSkin(String skin, SkinData.SkinVariant variant) { + SkinData data = FancyNpcsPlugin.get().getSkinManager().getByIdentifier(skin, variant); + return setSkinData(data); + } + + /** + * Loads the skin data and sets it as the skin of the npc + * + * @param skin a valid UUID, username, URL or file path + */ + public NpcData setSkin(String skin) { + return setSkin(skin, SkinData.SkinVariant.AUTO); + } + + public Location getLocation() { + return location; + } + + public NpcData setLocation(Location location) { + this.location = location; + isDirty = true; + return this; + } + + public boolean isShowInTab() { + return showInTab; + } + + public NpcData setShowInTab(boolean showInTab) { + this.showInTab = showInTab; + isDirty = true; + return this; + } + + public boolean isSpawnEntity() { + return spawnEntity; + } + + public NpcData setSpawnEntity(boolean spawnEntity) { + this.spawnEntity = spawnEntity; + isDirty = true; + return this; + } + + public boolean isCollidable() { + return collidable; + } + + public NpcData setCollidable(boolean collidable) { + this.collidable = collidable; + isDirty = true; + return this; + } + + public boolean isGlowing() { + return glowing; + } + + public NpcData setGlowing(boolean glowing) { + this.glowing = glowing; + isDirty = true; + return this; + } + + public NamedTextColor getGlowingColor() { + return glowingColor; + } + + public NpcData setGlowingColor(NamedTextColor glowingColor) { + this.glowingColor = glowingColor; + isDirty = true; + return this; + } + + public EntityType getType() { + return type; + } + + public NpcData setType(EntityType type) { + this.type = type; + attributes.clear(); + isDirty = true; + return this; + } + + public Map getEquipment() { + return equipment; + } + + public NpcData setEquipment(Map equipment) { + this.equipment = equipment; + isDirty = true; + return this; + } + + public NpcData addEquipment(NpcEquipmentSlot slot, ItemStack item) { + equipment.put(slot, item); + isDirty = true; + return this; + } + + public Consumer getOnClick() { + return onClick; + } + + public NpcData setOnClick(Consumer onClick) { + this.onClick = onClick; + isDirty = true; + return this; + } + + public Map> getActions() { + return actions; + } + + public NpcData setActions(Map> actions) { + this.actions = actions; + isDirty = true; + return this; + } + + public List getActions(ActionTrigger trigger) { + return actions.getOrDefault(trigger, new ArrayList<>()); + } + + public NpcData setActions(ActionTrigger trigger, List actions) { + this.actions.put(trigger, actions); + isDirty = true; + return this; + } + + public NpcData addAction(ActionTrigger trigger, int order, NpcAction action, String value) { + List a = actions.getOrDefault(trigger, new ArrayList<>()); + a.add(new NpcAction.NpcActionData(order, action, value)); + actions.put(trigger, a); + + isDirty = true; + return this; + } + + public NpcData removeAction(ActionTrigger trigger, NpcAction action) { + List a = actions.getOrDefault(trigger, new ArrayList<>()); + a.removeIf(ad -> ad.action().equals(action)); + actions.put(trigger, a); + + isDirty = true; + return this; + } + + public boolean isTurnToPlayer() { + return turnToPlayer; + } + + public NpcData setTurnToPlayer(boolean turnToPlayer) { + this.turnToPlayer = turnToPlayer; + isDirty = true; + return this; + } + + public float getInteractionCooldown() { + return interactionCooldown; + } + + public NpcData setInteractionCooldown(float interactionCooldown) { + this.interactionCooldown = interactionCooldown; + return this; + } + + public float getScale() { + return scale; + } + + public NpcData setScale(float scale) { + this.scale = scale; + isDirty = true; + return this; + } + + public int getVisibilityDistance() { + return visibilityDistance; + } + + public NpcData setVisibilityDistance(int visibilityDistance) { + this.visibilityDistance = visibilityDistance; + isDirty = true; + return this; + } + + public Map getAttributes() { + return attributes; + } + + public void addAttribute(NpcAttribute attribute, String value) { + attributes.put(attribute, value); + isDirty = true; + } + + public void applyAllAttributes(Npc npc) { + for (NpcAttribute attribute : attributes.keySet()) { + attribute.apply(npc, attributes.get(attribute)); + } + } + + public boolean isMirrorSkin() { + return mirrorSkin; + } + + public NpcData setMirrorSkin(boolean mirrorSkin) { + this.mirrorSkin = mirrorSkin; + isDirty = true; + return this; + } + + public boolean isDirty() { + return isDirty; + } + + public void setDirty(boolean dirty) { + isDirty = dirty; + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/NpcManager.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/NpcManager.java new file mode 100644 index 00000000..2cdf433d --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/NpcManager.java @@ -0,0 +1,31 @@ +package de.oliver.fancynpcs.api; + +import org.jetbrains.annotations.ApiStatus; + +import java.util.Collection; +import java.util.UUID; + +public interface NpcManager { + + void registerNpc(Npc npc); + + void removeNpc(Npc npc); + + @ApiStatus.Internal + Npc getNpc(int entityId); + + Npc getNpc(String name); + + Npc getNpcById(String id); + + Npc getNpc(String name, UUID creator); + + Collection getAllNpcs(); + + void saveNpcs(boolean force); + + void loadNpcs(); + + void reloadNpcs(); + +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/ActionManager.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/ActionManager.java new file mode 100644 index 00000000..a6bdcc4a --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/ActionManager.java @@ -0,0 +1,14 @@ +package de.oliver.fancynpcs.api.actions; + +import java.util.List; + +public interface ActionManager { + + void registerAction(NpcAction action); + + NpcAction getActionByName(String name); + + void unregisterAction(NpcAction action); + + List getAllActions(); +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/ActionTrigger.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/ActionTrigger.java new file mode 100644 index 00000000..a699f63c --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/ActionTrigger.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.api.actions; + +public enum ActionTrigger { + /** + * represents any click interaction by a player. + */ + ANY_CLICK, + /** + * represents a left click interaction by a player. + */ + LEFT_CLICK, + /** + * represents a right click interaction by a player. + */ + RIGHT_CLICK, + /** + * represents interactions invoked by the API. + */ + CUSTOM, + ; + + /** + * Gets the ActionTrigger by its name. + * + * @param name the name of the ActionTrigger + * @return the ActionTrigger or null if not found + */ + public static ActionTrigger getByName(final String name) { + for (ActionTrigger trigger : values()) { + if (trigger.name().equalsIgnoreCase(name)) { + return trigger; + } + } + return null; + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/NpcAction.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/NpcAction.java new file mode 100644 index 00000000..e0884d0a --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/NpcAction.java @@ -0,0 +1,48 @@ +package de.oliver.fancynpcs.api.actions; + +import de.oliver.fancynpcs.api.actions.executor.ActionExecutionContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * The NpcAction class is an abstract class that represents an action that can be performed by an NPC. + * Each NpcAction has a name and a flag indicating whether it requires a value. + *

+ * The NpcAction class provides an abstract execute method that must be implemented by subclasses + * to specify the behavior of the action when executed. + *

+ * Subclasses of NpcAction can provide additional data using the NpcActionData record, which includes + * an order value to specify the order of execution, the NpcAction itself, and a value associated with + * the action. + *

+ * This class provides getters for the name and the requiresValue flag of the action. + */ +public abstract class NpcAction { + + private final String name; + private final boolean requiresValue; + + public NpcAction(String name, boolean requiresValue) { + this.name = name; + this.requiresValue = requiresValue; + } + + /** + * Executes the action associated with this NpcAction. + * + * @param context The context in which the action is being executed. + * @param value The value associated with the action. Can be null if no value is required. + */ + public abstract void execute(@NotNull ActionExecutionContext context, @Nullable String value); + + public String getName() { + return name; + } + + public boolean requiresValue() { + return requiresValue; + } + + public record NpcActionData(int order, NpcAction action, String value) { + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/executor/ActionExecutionContext.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/executor/ActionExecutionContext.java new file mode 100644 index 00000000..fe380bcc --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/executor/ActionExecutionContext.java @@ -0,0 +1,184 @@ +package de.oliver.fancynpcs.api.actions.executor; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.actions.ActionTrigger; +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.api.actions.types.BlockUntilDoneAction; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Context for executing a sequence of NPC actions initiated by different triggers. + */ +public class ActionExecutionContext { + + /** + * The trigger that initiated the action. + * This is a final variable that represents the specific condition or + * event that caused the action to be created in the context. + */ + private final ActionTrigger trigger; + + /** + * The NPC that the action is being executed on. + */ + private final Npc npc; + + /** + * The player involved in the action, may be null if no player is involved. + */ + private final @Nullable UUID player; + + /** + * A list of NpcActionData instances representing the sequence of actions + * to be executed for the NPC in the given context. + */ + private final List actions; + + /** + * The index of the currently executing action in the list of actions. + *

+ * This variable keeps track of which action within the action sequence + * is currently being executed. It is incremented as actions are executed + * sequentially using the {@link #runNext()} method. + *

+ *

+ * The default initial value is 0, indicating the start of the sequence. + * When the index is set to -1, it signifies that the sequence has been + * terminated and no further actions should be executed. + *

+ */ + private int actionIndex; + + /** + * Constructs an ActionExecutionContext with the specified ActionTrigger, Npc, and an optional Player. + * + * @param trigger the trigger that initiated the action + * @param npc the NPC that the action is being executed on + * @param player the player involved in the action, may be null if no player is involved + */ + public ActionExecutionContext(ActionTrigger trigger, Npc npc, @Nullable UUID player) { + this.trigger = trigger; + this.npc = npc; + this.player = player; + + this.actions = new ArrayList<>(npc.getData().getActions(trigger)); + this.actionIndex = 0; + } + + /** + * Constructs an ActionExecutionContext with the specified ActionTrigger and Npc, without a Player. + * + * @param trigger the trigger that initiated the action + * @param npc the NPC that the action is being executed on + */ + public ActionExecutionContext(ActionTrigger trigger, Npc npc) { + this(trigger, npc, null); + } + + /** + * Executes the action at the specified index within the list of actions. + * + * @param index the index of the action to be executed. If the index is out of bounds, the method returns immediately. + */ + public void run(int index) { + if (index < 0 || index >= actions.size()) { + return; + } + + NpcAction.NpcActionData actionData = actions.get(index); + actionData.action().execute(this, actionData.value()); + } + + /** + * Executes the next action in the list of actions. + *

+ * If the current action index is out of bounds, the method returns immediately. + * The action index is incremented after the action is executed. + *

+ */ + public void runNext() { + if (actionIndex < 0 || actionIndex >= actions.size()) { + return; + } + + run(actionIndex++); + } + + /** + * Checks if there are more actions to be executed. + * + * @return true if there are more actions to be executed, false otherwise + */ + public boolean hasNext() { + return actionIndex >= 0 && actionIndex < actions.size(); + } + + /** + * Resets the current action index to its initial state. + * This is useful for re-running the sequence of actions from the beginning. + */ + public void reset() { + actionIndex = 0; + } + + /** + * Terminates the current action sequence by setting the action index to -1. + * This effectively marks the context as finished and prevents any further actions from being executed. + */ + public void terminate() { + actionIndex = -1; + } + + /** + * Checks if the action sequence has been terminated. + * + * @return true if the action index is -1, indicating the sequence is terminated; false otherwise + */ + public boolean isTerminated() { + return actionIndex == -1; + } + + public boolean shouldBlockUntilDone() { + for (NpcAction.NpcActionData action : actions) { + if (action.action() instanceof BlockUntilDoneAction) { + return true; + } + } + + return false; + } + + public ActionTrigger getTrigger() { + return trigger; + } + + public Npc getNpc() { + return npc; + } + + public List getActions() { + return actions; + } + + public UUID getPlayerUUID() { + return player; + } + + public @Nullable Player getPlayer() { + if (player == null) { + return null; + } + + return Bukkit.getPlayer(player); + } + + public int getActionIndex() { + return actionIndex; + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/executor/ActionExecutor.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/executor/ActionExecutor.java new file mode 100644 index 00000000..f5b97c83 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/executor/ActionExecutor.java @@ -0,0 +1,42 @@ +package de.oliver.fancynpcs.api.actions.executor; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.actions.ActionTrigger; +import org.bukkit.entity.Player; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class ActionExecutor { + + private static final Map runningContexts = new ConcurrentHashMap<>(); + + public static void execute(ActionTrigger trigger, Npc npc, Player player) { + String key = getKey(trigger, npc, player); + ActionExecutionContext runningContext = runningContexts.get(key); + if (runningContext != null) { + if (runningContext.shouldBlockUntilDone() && !runningContext.isTerminated()) { + return; + } + } + + ActionExecutionContext context = new ActionExecutionContext(trigger, npc, player.getUniqueId()); + runningContexts.put(key, context); + + FancyNpcsPlugin.get().newThread("FancyNpcs-ActionExecutor", () -> { + while (context.hasNext()) { + context.runNext(); + } + context.terminate(); + + runningContexts.remove(key); + }).start(); + + } + + private static String getKey(ActionTrigger trigger, Npc npc, Player player) { + return trigger.name() + "_" + npc.getData().getId() + "_" + player.getUniqueId(); + } + +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/BlockUntilDoneAction.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/BlockUntilDoneAction.java new file mode 100644 index 00000000..f539ad0a --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/BlockUntilDoneAction.java @@ -0,0 +1,23 @@ +package de.oliver.fancynpcs.api.actions.types; + +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.api.actions.executor.ActionExecutionContext; + +/** + * The BlockUntilDoneAction class is a specific implementation of the + * NpcAction class that represents an action requiring the NPC (Non-Player + * Character) to block its subsequent actions until the current interaction is + * completed. + *

+ */ +public class BlockUntilDoneAction extends NpcAction { + + public BlockUntilDoneAction() { + super("block_until_done", false); + } + + @Override + public void execute(ActionExecutionContext context, String value) { + + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/ConsoleCommandAction.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/ConsoleCommandAction.java new file mode 100644 index 00000000..47768f7b --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/ConsoleCommandAction.java @@ -0,0 +1,46 @@ +package de.oliver.fancynpcs.api.actions.types; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.api.actions.executor.ActionExecutionContext; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; +import org.lushplugins.chatcolorhandler.ChatColorHandler; +import org.lushplugins.chatcolorhandler.parsers.ParserTypes; + +/** + * Represents a console command action that can be executed for an NPC. + */ +public class ConsoleCommandAction extends NpcAction { + + public ConsoleCommandAction() { + super("console_command", true); + } + + /** + * Executes the console command action for an NPC. + * + * @param value The command string to be executed. The value can contain the placeholder "{player}" which will be replaced with the player's name. + */ + @Override + public void execute(@NotNull ActionExecutionContext context, String value) { + if (value == null || value.isEmpty()) { + return; + } + + String command = value; + if (context.getPlayer() != null) { + command = value.replace("{player}", context.getPlayer().getName()); + } + + String finalCommand = ChatColorHandler.translate(command, context.getPlayer(), ParserTypes.placeholder()); + + FancyNpcsPlugin.get().getScheduler().runTask(null, () -> { + try { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), finalCommand); + } catch (Exception e) { + FancyNpcsPlugin.get().getFancyLogger().warn("Failed to execute command: " + finalCommand); + } + }); + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/ExecuteRandomActionAction.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/ExecuteRandomActionAction.java new file mode 100644 index 00000000..48d3696d --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/ExecuteRandomActionAction.java @@ -0,0 +1,41 @@ +package de.oliver.fancynpcs.api.actions.types; + +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.api.actions.executor.ActionExecutionContext; +import org.jetbrains.annotations.NotNull; + +import java.util.Random; + +/** + * The ExecuteRandomActionAction class represents an action that can be executed randomly by an NPC. + *

+ * The ExecuteRandomActionAction class provides an implementation for the execute method, + * which executes a random action triggered by the given action trigger on the specified NPC and player. + * The execution of the action is based on the actions associated with the NPC's data for the given trigger. + */ +public class ExecuteRandomActionAction extends NpcAction { + + public ExecuteRandomActionAction() { + super("execute_random_action", false); + } + + /** + * Executes a random action triggered by the given action trigger on the specified NPC and player. + */ + @Override + public void execute(@NotNull ActionExecutionContext context, String value) { + int currentIndex = context.getActionIndex(); + int actionCount = context.getActions().size(); + + int randomIndex = getRandomIndex(currentIndex, actionCount); + + NpcActionData action = context.getActions().get(randomIndex); + action.action().execute(context, action.value()); + + context.terminate(); + } + + private int getRandomIndex(int from, int to) { + return new Random().nextInt(to - from) + from; + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/MessageAction.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/MessageAction.java new file mode 100644 index 00000000..41200a57 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/MessageAction.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.api.actions.types; + +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.api.actions.executor.ActionExecutionContext; +import org.lushplugins.chatcolorhandler.ModernChatColorHandler; +import org.jetbrains.annotations.NotNull; + +/** + * The MessageAction class represents an action that sends a message to the player when executed by an NPC. + */ +public class MessageAction extends NpcAction { + + public MessageAction() { + super("message", true); + } + + /** + * Executes the action associated with this NpcAction. + * + * @param value The value passed to the action. + */ + @Override + public void execute(@NotNull ActionExecutionContext context, String value) { + if (value == null || value.isEmpty()) { + return; + } + + if (context.getPlayer() == null) { + return; + } + + context.getPlayer().sendMessage(ModernChatColorHandler.translate(value, context.getPlayer())); + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/NeedPermissionAction.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/NeedPermissionAction.java new file mode 100644 index 00000000..1f57ffcb --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/NeedPermissionAction.java @@ -0,0 +1,28 @@ +package de.oliver.fancynpcs.api.actions.types; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.api.actions.executor.ActionExecutionContext; + +public class NeedPermissionAction extends NpcAction { + + public NeedPermissionAction() { + super("need_permission", true); + } + + @Override + public void execute(ActionExecutionContext context, String value) { + if (value == null || value.isEmpty()) { + return; + } + + if (context.getPlayer() == null) { + return; + } + + if (!context.getPlayer().hasPermission(value)) { + FancyNpcsPlugin.get().getTranslator().translate("action_missing_permissions").send(context.getPlayer()); + context.terminate(); + } + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/PlaySoundAction.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/PlaySoundAction.java new file mode 100644 index 00000000..d9cba82a --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/PlaySoundAction.java @@ -0,0 +1,38 @@ +package de.oliver.fancynpcs.api.actions.types; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.api.actions.executor.ActionExecutionContext; +import org.jetbrains.annotations.NotNull; +import org.lushplugins.chatcolorhandler.ChatColorHandler; +import org.lushplugins.chatcolorhandler.parsers.ParserTypes; + +public class PlaySoundAction extends NpcAction { + + public PlaySoundAction() { + super("play_sound", true); + } + + @Override + public void execute(@NotNull ActionExecutionContext context, String value) { + if (value == null || value.isEmpty()) { + return; + } + + if (context.getPlayer() == null) { + return; + } + + String sound = ChatColorHandler.translate(value, context.getPlayer(), ParserTypes.placeholder()); + + FancyNpcsPlugin.get().getScheduler().runTask( + context.getPlayer().getLocation(), + () -> { + try { + context.getPlayer().playSound(context.getPlayer().getLocation(), value, 1.0F, 1.0F); + } catch (Exception e) { + FancyNpcsPlugin.get().getFancyLogger().warn("Failed to play sound: " + sound); + } + }); + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/PlayerCommandAction.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/PlayerCommandAction.java new file mode 100644 index 00000000..a4bf6e87 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/PlayerCommandAction.java @@ -0,0 +1,60 @@ +package de.oliver.fancynpcs.api.actions.types; + +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.api.actions.executor.ActionExecutionContext; +import org.jetbrains.annotations.NotNull; +import org.lushplugins.chatcolorhandler.ChatColorHandler; +import org.lushplugins.chatcolorhandler.parsers.ParserTypes; + +/** + * Represents a player command action that can be executed when triggered by an NPC interaction. + */ +public class PlayerCommandAction extends NpcAction { + + public PlayerCommandAction() { + super("player_command", true); + } + + /** + * Executes a player command action when triggered by an NPC interaction. + */ + @Override + public void execute(@NotNull ActionExecutionContext context, String value) { + if (value == null || value.isEmpty()) { + return; + } + + if (context.getPlayer() == null) { + return; + } + + String command = ChatColorHandler.translate(value, context.getPlayer(), ParserTypes.placeholder()); + + if (command.toLowerCase().startsWith("server")) { + String[] args = value.split(" "); + if (args.length < 2) { + return; + } + String server = args[1]; + + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF("Connect"); + out.writeUTF(server); + context.getPlayer().sendPluginMessage(FancyNpcsPlugin.get().getPlugin(), "BungeeCord", out.toByteArray()); + return; + } + + FancyNpcsPlugin.get().getScheduler().runTask( + context.getPlayer().getLocation(), + () -> { + try { + context.getPlayer().chat("/" + command); + } catch (Exception e) { + FancyNpcsPlugin.get().getFancyLogger().warn("Failed to execute command: " + command); + } + }); + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/PlayerCommandAsOpAction.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/PlayerCommandAsOpAction.java new file mode 100644 index 00000000..e5d09ac2 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/PlayerCommandAsOpAction.java @@ -0,0 +1,67 @@ +package de.oliver.fancynpcs.api.actions.types; + +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.api.actions.executor.ActionExecutionContext; +import org.jetbrains.annotations.NotNull; +import org.lushplugins.chatcolorhandler.ChatColorHandler; +import org.lushplugins.chatcolorhandler.parsers.ParserTypes; + +/** + * PlayerCommandAsOpAction is a npc action that allows a player to execute a command as an operator when triggered by an NPC interaction. + */ +public class PlayerCommandAsOpAction extends NpcAction { + + public PlayerCommandAsOpAction() { + super("player_command_as_op", true); + } + + /** + * Executes a player command as an operator when triggered by an NPC interaction. + */ + @Override + public void execute(@NotNull ActionExecutionContext context, String value) { + if (value == null || value.isEmpty()) { + return; + } + + if (context.getPlayer() == null) { + return; + } + + String command = ChatColorHandler.translate(value, context.getPlayer(), ParserTypes.placeholder()); + + if (command.toLowerCase().startsWith("server")) { + String[] args = value.split(" "); + if (args.length < 2) { + return; + } + String server = args[1]; + + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF("Connect"); + out.writeUTF(server); + context.getPlayer().sendPluginMessage(FancyNpcsPlugin.get().getPlugin(), "BungeeCord", out.toByteArray()); + return; + } + + FancyNpcsPlugin.get().getScheduler().runTask( + context.getPlayer().getLocation(), + () -> { + boolean wasOp = context.getPlayer().isOp(); + + context.getPlayer().setOp(true); + try { + context.getPlayer().chat("/" + command); + } catch (Exception e) { + FancyNpcsPlugin.get().getFancyLogger().warn("Failed to execute command: " + command); + } finally { + context.getPlayer().setOp(wasOp); + } + } + ); + } + +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/SendToServerAction.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/SendToServerAction.java new file mode 100644 index 00000000..bad77c23 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/SendToServerAction.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.api.actions.types; + +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.api.actions.executor.ActionExecutionContext; +import org.jetbrains.annotations.NotNull; + +/** + * The SendToServerAction class is a subclass of NpcAction that represents an action + * to send data to the server using BungeeCord messaging. + */ +public class SendToServerAction extends NpcAction { + + public SendToServerAction() { + super("send_to_server", true); + } + + /** + * Executes the action associated with this NpcAction. + * + * @param value The value associated with the action. + */ + @Override + public void execute(@NotNull ActionExecutionContext context, String value) { + if (value == null || value.isEmpty()) { + return; + } + + if (context.getPlayer() == null) { + return; + } + + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF("Connect"); + out.writeUTF(value); + context.getPlayer().sendPluginMessage(FancyNpcsPlugin.get().getPlugin(), "BungeeCord", out.toByteArray()); + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/WaitAction.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/WaitAction.java new file mode 100644 index 00000000..2caab984 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/actions/types/WaitAction.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.api.actions.types; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.api.actions.executor.ActionExecutionContext; +import org.jetbrains.annotations.NotNull; + +public class WaitAction extends NpcAction { + + public WaitAction() { + super("wait", true); + } + + /** + * Executes the "wait" action for an NPC. + * + * @param value The value representing the time to wait in seconds. + */ + @Override + public void execute(@NotNull ActionExecutionContext context, String value) { + if (value == null || value.isEmpty()) { + return; + } + + int time; + try { + time = Integer.parseInt(value); + } catch (NumberFormatException e) { + FancyNpcsPlugin.get().getFancyLogger().warn("Invalid time value for wait action: " + value); + return; + } + + try { + Thread.sleep(time * 1000L); + } catch (InterruptedException e) { + FancyNpcsPlugin.get().getFancyLogger().warn("Thread was interrupted while waiting"); + } + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcCreateEvent.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcCreateEvent.java new file mode 100644 index 00000000..f9afcffb --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcCreateEvent.java @@ -0,0 +1,60 @@ +package de.oliver.fancynpcs.api.events; + +import de.oliver.fancynpcs.api.Npc; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * Is fired when a new NPC is being created + */ +public class NpcCreateEvent extends Event implements Cancellable { + + private static final HandlerList handlerList = new HandlerList(); + @NotNull + private final Npc npc; + @NotNull + private final CommandSender creator; + private boolean isCancelled; + + public NpcCreateEvent(@NotNull Npc npc, @NotNull CommandSender creator) { + this.npc = npc; + this.creator = creator; + } + + public static HandlerList getHandlerList() { + return handlerList; + } + + /** + * @return the created npc + */ + public @NotNull Npc getNpc() { + return npc; + } + + /** + * @return the player who created the npc + */ + public @NotNull CommandSender getCreator() { + return creator; + } + + @Override + public boolean isCancelled() { + return isCancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.isCancelled = cancel; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlerList; + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcInteractEvent.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcInteractEvent.java new file mode 100644 index 00000000..0382f8db --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcInteractEvent.java @@ -0,0 +1,95 @@ +package de.oliver.fancynpcs.api.events; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.actions.ActionTrigger; +import de.oliver.fancynpcs.api.actions.NpcAction; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Consumer; + +/** + * Is fired when a player interacts with a NPC + */ +public class NpcInteractEvent extends Event implements Cancellable { + + private static final HandlerList handlerList = new HandlerList(); + @NotNull + private final Npc npc; + @Nullable + private final List actions; + @NotNull + private final Consumer onClick; + @NotNull + private final Player player; + private final ActionTrigger actionTrigger; + private boolean isCancelled; + + public NpcInteractEvent(@NotNull Npc npc, @NotNull Consumer onClick, @NotNull List actions, @NotNull Player player, @NotNull ActionTrigger actionTrigger) { + this.npc = npc; + this.onClick = onClick; + this.actions = actions; + this.player = player; + this.actionTrigger = actionTrigger; + } + + public static HandlerList getHandlerList() { + return handlerList; + } + + /** + * @return the modified npc + */ + public @NotNull Npc getNpc() { + return npc; + } + + /** + * @return the custom on click method that will run + */ + public @NotNull Consumer getOnClick() { + return onClick; + } + + /** + * @return the actions that will run + */ + public @Nullable List getActions() { + return actions; + } + + /** + * @return returns interaction type + */ + public @NotNull ActionTrigger getInteractionType() { + return actionTrigger; + } + + /** + * @return the player who interacted with the npc + */ + public @NotNull Player getPlayer() { + return player; + } + + @Override + public boolean isCancelled() { + return isCancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.isCancelled = cancel; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlerList; + } + +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcModifyEvent.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcModifyEvent.java new file mode 100644 index 00000000..c5a29f5e --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcModifyEvent.java @@ -0,0 +1,117 @@ +package de.oliver.fancynpcs.api.events; + +import de.oliver.fancynpcs.api.Npc; +import org.bukkit.command.CommandSender; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * Is fired when a NPC is being modified + */ +public class NpcModifyEvent extends Event implements Cancellable { + + private static final HandlerList handlerList = new HandlerList(); + @NotNull + private final Npc npc; + @NotNull + private final NpcModification modification; + @NotNull + private final Object newValue; + @NotNull + private final CommandSender modifier; + private boolean isCancelled; + + public NpcModifyEvent(@NotNull Npc npc, @NotNull NpcModification modification, Object newValue, @NotNull CommandSender modifier) { + this.npc = npc; + this.modification = modification; + this.newValue = newValue; + this.modifier = modifier; + } + + public static HandlerList getHandlerList() { + return handlerList; + } + + /** + * @return the modified npc + */ + public @NotNull Npc getNpc() { + return npc; + } + + /** + * @return the modification that was being made + */ + public @NotNull NpcModification getModification() { + return modification; + } + + /** + * @return the value that is being set + */ + public @NotNull Object getNewValue() { + return newValue; + } + + /** + * @return the sender who modified the npc + */ + public @NotNull CommandSender getModifier() { + return modifier; + } + + @Override + public boolean isCancelled() { + return isCancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.isCancelled = cancel; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlerList; + } + + public enum NpcModification { + ATTRIBUTE, + COLLIDABLE, + DISPLAY_NAME, + EQUIPMENT, + GLOWING, + GLOWING_COLOR, + INTERACTION_COOLDOWN, + SCALE, + VISIBILITY_DISTANCE, + LOCATION, + MIRROR_SKIN, + PLAYER_COMMAND, + SERVER_COMMAND, + SHOW_IN_TAB, + SKIN, + TURN_TO_PLAYER, + TYPE, + // Messages. + MESSAGE_ADD, + MESSAGE_SET, + MESSAGE_REMOVE, + MESSAGE_CLEAR, + MESSAGE_SEND_RANDOMLY, + // Player commands. + PLAYER_COMMAND_ADD, + PLAYER_COMMAND_SET, + PLAYER_COMMAND_REMOVE, + PLAYER_COMMAND_CLEAR, + PLAYER_COMMAND_SEND_RANDOMLY, + // Server commands. + SERVER_COMMAND_ADD, + SERVER_COMMAND_SET, + SERVER_COMMAND_REMOVE, + SERVER_COMMAND_CLEAR, + SERVER_COMMAND_SEND_RANDOMLY + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcRemoveEvent.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcRemoveEvent.java new file mode 100644 index 00000000..c61de4cc --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcRemoveEvent.java @@ -0,0 +1,59 @@ +package de.oliver.fancynpcs.api.events; + +import de.oliver.fancynpcs.api.Npc; +import org.bukkit.command.CommandSender; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * Is fired when a NPC is being deleted + */ +public class NpcRemoveEvent extends Event implements Cancellable { + + private static final HandlerList handlerList = new HandlerList(); + @NotNull + private final Npc npc; + @NotNull + private final CommandSender receiver; + private boolean isCancelled; + + public NpcRemoveEvent(@NotNull Npc npc, @NotNull CommandSender receiver) { + this.npc = npc; + this.receiver = receiver; + } + + public static HandlerList getHandlerList() { + return handlerList; + } + + /** + * @return the npc that is being removed + */ + public @NotNull Npc getNpc() { + return npc; + } + + /** + * @return the player who removed the npc + */ + public @NotNull CommandSender getSender() { + return receiver; + } + + @Override + public boolean isCancelled() { + return isCancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.isCancelled = cancel; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlerList; + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcSpawnEvent.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcSpawnEvent.java new file mode 100644 index 00000000..3274a356 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcSpawnEvent.java @@ -0,0 +1,59 @@ +package de.oliver.fancynpcs.api.events; + +import de.oliver.fancynpcs.api.Npc; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * Is fired when a NPC is being spawned + */ +public class NpcSpawnEvent extends Event implements Cancellable { + private static final HandlerList handlerList = new HandlerList(); + @NotNull + private final Npc npc; + @NotNull + private final Player player; + private boolean isCancelled; + + public NpcSpawnEvent(@NotNull Npc npc, @NotNull Player player) { + super(true); + this.npc = npc; + this.player = player; + } + + public static HandlerList getHandlerList() { + return handlerList; + } + + /** + * @return the npc that is being spawned + */ + public @NotNull Npc getNpc() { + return npc; + } + + /** + * @return the player to whom the spawn packets are being sent + */ + public @NotNull Player getPlayer() { + return player; + } + + @Override + public boolean isCancelled() { + return isCancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.isCancelled = cancel; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlerList; + } +} \ No newline at end of file diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcStartLookingEvent.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcStartLookingEvent.java new file mode 100644 index 00000000..ea1e0eb7 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcStartLookingEvent.java @@ -0,0 +1,48 @@ +package de.oliver.fancynpcs.api.events; + +import de.oliver.fancynpcs.api.Npc; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * Is fired when NPC starts looking at a player. + */ +public class NpcStartLookingEvent extends Event { + + private static final HandlerList handlerList = new HandlerList(); + @NotNull + private final Npc npc; + @NotNull + private final Player player; + + public NpcStartLookingEvent(@NotNull Npc npc, @NotNull Player player) { + this.npc = npc; + this.player = player; + } + + public static HandlerList getHandlerList() { + return handlerList; + } + + /** + * @return the npc that started looking at a player + */ + public @NotNull Npc getNpc() { + return npc; + } + + /** + * @return the player who npc started looking at + */ + public @NotNull Player getPlayer() { + return player; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlerList; + } + +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcStopLookingEvent.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcStopLookingEvent.java new file mode 100644 index 00000000..00db0407 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcStopLookingEvent.java @@ -0,0 +1,48 @@ +package de.oliver.fancynpcs.api.events; + +import de.oliver.fancynpcs.api.Npc; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * Is fired when NPC stops looking at a player. + */ +public class NpcStopLookingEvent extends Event { + + private static final HandlerList handlerList = new HandlerList(); + @NotNull + private final Npc npc; + @NotNull + private final Player player; + + public NpcStopLookingEvent(@NotNull Npc npc, @NotNull Player player) { + this.npc = npc; + this.player = player; + } + + public static HandlerList getHandlerList() { + return handlerList; + } + + /** + * @return the npc that stopped looking at a player + */ + public @NotNull Npc getNpc() { + return npc; + } + + /** + * @return the player who npc stopped looking at + */ + public @NotNull Player getPlayer() { + return player; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlerList; + } + +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcsLoadedEvent.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcsLoadedEvent.java new file mode 100644 index 00000000..a264b1ca --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/NpcsLoadedEvent.java @@ -0,0 +1,24 @@ +package de.oliver.fancynpcs.api.events; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.ApiStatus; + +/** + * Is fired when all NPCs are loaded. + * + * Will be removed, once the npc loading is coupled with the loading of worlds! Be aware of that! + */ +@ApiStatus.Experimental() +public class NpcsLoadedEvent extends Event { + private static final HandlerList handlerList = new HandlerList(); + + public static HandlerList getHandlerList() { + return handlerList; + } + + @Override + public HandlerList getHandlers() { + return handlerList; + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/PacketReceivedEvent.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/PacketReceivedEvent.java new file mode 100644 index 00000000..5b4df3b4 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/events/PacketReceivedEvent.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.api.events; + +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class PacketReceivedEvent extends Event { + + private static final HandlerList handlerList = new HandlerList(); + + private final Object packet; + private final Player player; + + public PacketReceivedEvent(Object packet, Player player) { + this.packet = packet; + this.player = player; + } + + public static HandlerList getHandlerList() { + return handlerList; + } + + public Object getPacket() { + return packet; + } + + public Player getPlayer() { + return player; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlerList; + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/skins/SkinData.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/skins/SkinData.java new file mode 100644 index 00000000..d7d1b62e --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/skins/SkinData.java @@ -0,0 +1,65 @@ +package de.oliver.fancynpcs.api.skins; + +public class SkinData { + + private String identifier; + private SkinVariant variant; + + private String textureValue; + private String textureSignature; + + public SkinData(String identifier, SkinVariant variant, String textureValue, String textureSignature) { + this.identifier = identifier; + this.variant = variant; + this.textureValue = textureValue; + this.textureSignature = textureSignature; + } + + public SkinData(String identifier, SkinVariant variant) { + this(identifier, variant, null, null); + } + + public boolean hasTexture() { + return textureValue != null && + textureSignature != null && + !textureValue.isEmpty() && + !textureSignature.isEmpty(); + } + + public String getIdentifier() { + return identifier; + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + public SkinVariant getVariant() { + return variant; + } + + public void setVariant(SkinVariant variant) { + this.variant = variant; + } + + public String getTextureValue() { + return textureValue; + } + + public void setTextureValue(String textureValue) { + this.textureValue = textureValue; + } + + public String getTextureSignature() { + return textureSignature; + } + + public void setTextureSignature(String textureSignature) { + this.textureSignature = textureSignature; + } + + public enum SkinVariant { + AUTO, + SLIM, + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/skins/SkinGeneratedEvent.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/skins/SkinGeneratedEvent.java new file mode 100644 index 00000000..65ccd10b --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/skins/SkinGeneratedEvent.java @@ -0,0 +1,49 @@ +package de.oliver.fancynpcs.api.skins; + +import org.bukkit.Bukkit; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Event that is called when a skin is generated + */ +public class SkinGeneratedEvent extends Event { + + private static final HandlerList handlerList = new HandlerList(); + + @NotNull + private final String id; + + @Nullable + private final SkinData skin; + + public SkinGeneratedEvent(@NotNull String id, @Nullable SkinData skin) { + super(!Bukkit.isPrimaryThread()); + this.id = id; + this.skin = skin; + } + + public static HandlerList getHandlerList() { + return handlerList; + } + + public @NotNull String getId() { + return id; + } + + /** + * Get the skin that was generated + * + * @return the skin that was generated or null if the skin could not be generated + */ + public @Nullable SkinData getSkin() { + return skin; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlerList; + } +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/skins/SkinManager.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/skins/SkinManager.java new file mode 100644 index 00000000..1ac2c537 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/skins/SkinManager.java @@ -0,0 +1,43 @@ +package de.oliver.fancynpcs.api.skins; + +import java.util.UUID; + +public interface SkinManager { + + /** + * Fetch a skin by its identifier and variant + * + * @param identifier either a valid UUID, username, URL or file path + * @return the skin data, if the skin was cached. Otherwise, null is returned and the skin is fetched asynchronously. You can listen to the {@link SkinGeneratedEvent} to get the skin data + */ + SkinData getByIdentifier(String identifier, SkinData.SkinVariant variant); + + /** + * Fetch a skin by a UUID of a player + * + * @return the skin data, if the skin was cached. Otherwise, null is returned and the skin is fetched asynchronously. You can listen to the {@link SkinGeneratedEvent} to get the skin data + */ + SkinData getByUUID(UUID uuid, SkinData.SkinVariant variant); + + /** + * Fetch a skin by a username of a player + * + * @return the skin data, if the skin was cached. Otherwise, null is returned and the skin is fetched asynchronously. You can listen to the {@link SkinGeneratedEvent} to get the skin data + */ + SkinData getByUsername(String username, SkinData.SkinVariant variant); + + /** + * Fetch a skin by a URL pointing to a skin image + * + * @return the skin data, if the skin was cached. Otherwise, null is returned and the skin is fetched asynchronously. You can listen to the {@link SkinGeneratedEvent} to get the skin data + */ + SkinData getByURL(String url, SkinData.SkinVariant variant); + + /** + * Fetch a skin by a file path pointing to a skin image (relative to plugins/FancyNPCs/skins) + * + * @return the skin data, if the skin was cached. Otherwise, null is returned and the skin is fetched asynchronously. You can listen to the {@link SkinGeneratedEvent} to get the skin data + */ + SkinData getByFile(String filePath, SkinData.SkinVariant variant); + +} diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/utils/Interval.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/utils/Interval.java new file mode 100644 index 00000000..aed64905 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/utils/Interval.java @@ -0,0 +1,217 @@ +/* + * MIT License + * + * Copyright (c) 2023 Grabsky <44530932+Grabsky@users.noreply.github.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * HORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package de.oliver.fancynpcs.api.utils; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.time.Instant; +import java.util.Date; + +import static de.oliver.fancynpcs.api.utils.Interval.Unit.*; + + +/** + * {@link Interval} is simple (but not very extensible) object that provides methods for + * unit conversion and creation of human-readable 'elapsed time' strings. + *

+ * This API is for internal use only and can change at any time. + */ +@ApiStatus.Internal +public final class Interval { + + private final long value; + + public Interval(final long value) { + this.value = value; + } + + /** + * Returns {@link Interval} object of current time. + */ + public static @NotNull Interval now() { + return new Interval(System.currentTimeMillis()); + } + + /** + * Returns {@link Interval} object constructed from provided {@link Long long} {@code (interval)}. + * It is expected that provided value is already a difference between two timestamps. + */ + public static @NotNull Interval of(final long interval, final @NotNull Unit unit) { + return new Interval(interval * unit.factor); + } + + /** + * Returns {@link Interval} object constructed from provided {@link Double double} {@code (interval)}. + * It is expected that provided value is already a difference between two timestamps. + */ + public static @NotNull Interval of(final double interval, final @NotNull Unit unit) { + return new Interval(Math.round(interval * unit.factor)); + } + + /** + * Returns {@link Interval} of time between {@code n} and {@code m}. + */ + public static @NotNull Interval between(final long n, final long m, final @NotNull Unit unit) { + return new Interval((n - m) * unit.factor); + } + + /** + * Returns {@link Interval} of time between {@code n} and {@code m}. + */ + public static @NotNull Interval between(final double n, final double m, final @NotNull Unit unit) { + return new Interval(Math.round((n - m) * unit.factor)); + } + + /** + * Returns interval converted to specified {@link Unit} {@code (unit)}.
+ *

+     * Interval.of(1500, Interval.Unit.MILLISECONDS).as(Interval.Unit.SECONDS) // 1.5F
+     * Interval.of(300, Interval.Unit.SECONDS).as(Interval.Unit.MINUTES) // 5F
+     * 
+ */ + public double as(final @NotNull Unit unit) { + return (double) (value / unit.factor); + } + + /** + * Returns a copy of (this) {@link Interval} with {@code n} of {@link Unit} added. + */ + public @NotNull Interval add(final @NotNull Interval other) { + return new Interval(this.value + other.value); + } + + /** + * Returns a copy of (this) {@link Interval} with {@code n} of {@link Unit} added. + */ + public @NotNull Interval add(final long n, final @NotNull Unit unit) { + return new Interval(this.value + (n * unit.factor)); + } + + /** + * Returns a copy of (this) {@link Interval} with {@code n} of {@link Unit} removed. + */ + public @NotNull Interval remove(final @NotNull Interval other) { + return new Interval(this.value - other.value); + } + + /** + * Returns a copy of (this) {@link Interval} with {@code n} of {@link Unit} removed. + */ + public @NotNull Interval remove(final long n, final @NotNull Unit unit) { + return new Interval(this.value - (n * unit.factor)); + } + + /** + * Returns new {@link Date} created from (this) {@link Interval}. + */ + public @NotNull Date toDate() { + return new Date(this.value); + } + + /** + * Returns new {@link Instant} created from (this) {@link Interval}. + */ + public @NotNull Instant toInstant() { + return Instant.ofEpochMilli(this.value); + } + + /** + * Returns formatted {@link String} expressing this {@link Interval}. + *
+     * final Interval i = Interval.between(lastJoinedMillis, currentTimeMillis, Interval.Unit.MILLISECONDS);
+     * System.out.println(i.toString()) + " ago"; // eg. '1d 7h 32min 10s ago'
+     * 
+ */ + @Override + public @NotNull String toString() { + // Returning milliseconds for values below 1000. (less than one second) + if (value < 1000) + return value % YEARS.getFactor() % MONTHS.getFactor() % DAYS.getFactor() % HOURS.getFactor() % MINUTES.getFactor() % SECONDS.getFactor() / MILLISECONDS.getFactor() + "ms"; + ; + // Calculation values, the ugly way. + final long years = value / YEARS.getFactor(); + final long months = value % YEARS.getFactor() / MONTHS.getFactor(); + final long days = value % YEARS.getFactor() % MONTHS.getFactor() / DAYS.getFactor(); + final long hours = value % YEARS.getFactor() % MONTHS.getFactor() % DAYS.getFactor() / HOURS.getFactor(); + final long minutes = value % YEARS.getFactor() % MONTHS.getFactor() % DAYS.getFactor() % HOURS.getFactor() / MINUTES.getFactor(); + final long seconds = value % YEARS.getFactor() % MONTHS.getFactor() % DAYS.getFactor() % HOURS.getFactor() % MINUTES.getFactor() / SECONDS.getFactor(); + // Creating a new output StringBuilder object. + final StringBuilder builder = new StringBuilder(); + // Appending to the StringBuilder. + if (years > 0L) builder.append(years).append("y "); + if (months > 0L) builder.append(months).append("mo "); + if (days > 0L) builder.append(days).append("d "); + if (hours > 0L) builder.append(hours).append("h "); + if (minutes > 0L) builder.append(minutes).append("min "); + if (seconds > 0L) builder.append(seconds).append("s"); + // Removing last character if a whitespace. + if (builder.charAt(builder.length() - 1) == ' ') + builder.deleteCharAt(builder.length() - 1); + // Building a String and returning. + return builder.toString(); + } + + public enum Unit { + MILLISECONDS(1L, "ms"), + TICKS(50L, "t"), + SECONDS(1_000L, "s"), + MINUTES(60_000L, "min"), + HOURS(3_600_000L, "h"), + DAYS(86_400_000L, "d"), + MONTHS(2_629_800_000L, "mo"), + YEARS(31_557_600_000L, "y"); + + private final long factor; + private final String shortCode; + + Unit(final long factor, final @NotNull String shortCode) { + this.factor = factor; + this.shortCode = shortCode; + } + + /** + * Returns {@link Unit} or {@code null} from provided short code. + */ + public static @Nullable Unit fromShortCode(final @NotNull String shortCode) { + // Iterating over all units and finding one that matches provided short code. + for (final Unit unit : Unit.values()) + if (unit.shortCode.equalsIgnoreCase(shortCode) == true) + return unit; + // Unit has not been found. Returning null. + return null; + } + + public long getFactor() { + return factor; + } + + public @NotNull String getShortCode() { + return shortCode; + } + + } + +} \ No newline at end of file diff --git a/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/utils/NpcEquipmentSlot.java b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/utils/NpcEquipmentSlot.java new file mode 100644 index 00000000..b62f6c84 --- /dev/null +++ b/plugins/fancynpcs/api/src/main/java/de/oliver/fancynpcs/api/utils/NpcEquipmentSlot.java @@ -0,0 +1,25 @@ +package de.oliver.fancynpcs.api.utils; + +public enum NpcEquipmentSlot { + MAINHAND, + OFFHAND, + FEET, + LEGS, + CHEST, + HEAD; + + public static NpcEquipmentSlot parse(String s) { + for (NpcEquipmentSlot slot : values()) { + if (slot.name().equalsIgnoreCase(s)) { + return slot; + } + } + + return null; + } + + public String toNmsName() { + return name().toLowerCase(); + } + +} diff --git a/plugins/fancynpcs/build.gradle.kts b/plugins/fancynpcs/build.gradle.kts new file mode 100644 index 00000000..7c7bd747 --- /dev/null +++ b/plugins/fancynpcs/build.gradle.kts @@ -0,0 +1,256 @@ +import net.minecrell.pluginyml.paper.PaperPluginDescription +import java.io.BufferedReader +import java.io.InputStreamReader + +plugins { + id("java-library") + id("maven-publish") + id("xyz.jpenilla.run-paper") + id("com.gradleup.shadow") + id("net.minecrell.plugin-yml.paper") + id("io.papermc.hangar-publish-plugin") + id("com.modrinth.minotaur") +} + +runPaper.folia.registerTask() + +val supportedVersions = + listOf( + "1.19.4", + "1.20", + "1.20.1", + "1.20.2", + "1.20.3", + "1.20.4", + "1.20.5", + "1.20.6", + "1.21", + "1.21.1", + "1.21.2", + "1.21.3", + "1.21.4" + ) + +allprojects { + group = "de.oliver" + val buildId = System.getenv("BUILD_ID") + version = "2.4.4" + (if (buildId != null) ".$buildId" else "") + description = "Simple, lightweight and fast NPC plugin using packets" + + repositories { + mavenLocal() + mavenCentral() + maven(url = "https://repo.papermc.io/repository/maven-public/") + maven(url = "https://repo.fancyplugins.de/releases") + maven(url = "https://repo.lushplugins.org/releases") + maven(url = "https://repo.inventivetalent.org/repository/maven-snapshots/") + } +} + +dependencies { + compileOnly("io.papermc.paper:paper-api:1.21.5-R0.1-SNAPSHOT") + + implementation(project(":plugins:fancynpcs:api")) + implementation(project(":plugins:fancynpcs:implementation_1_21_5")) + implementation(project(":plugins:fancynpcs:implementation_1_21_4")) + implementation(project(":plugins:fancynpcs:implementation_1_21_3")) + implementation(project(":plugins:fancynpcs:implementation_1_21_1")) + implementation(project(":plugins:fancynpcs:implementation_1_20_6")) + implementation(project(":plugins:fancynpcs:implementation_1_20_4", configuration = "reobf")) + implementation(project(":plugins:fancynpcs:implementation_1_20_2", configuration = "reobf")) + implementation(project(":plugins:fancynpcs:implementation_1_20_1", configuration = "reobf")) + implementation(project(":plugins:fancynpcs:implementation_1_20", configuration = "reobf")) + implementation(project(":plugins:fancynpcs:implementation_1_19_4", configuration = "reobf")) + + implementation("de.oliver:FancyLib:37") + implementation("de.oliver:plugin-tests:1.0.0") + implementation("de.oliver:JDB:1.0.0") + compileOnly("org.lushplugins:ChatColorHandler:5.1.3") + implementation("de.oliver.FancyAnalytics:api:0.1.6") + implementation("de.oliver.FancyAnalytics:logger:0.0.6") + implementation("org.incendo:cloud-core:2.1.0-SNAPSHOT") + implementation("org.incendo:cloud-paper:2.0.0-SNAPSHOT") + implementation("org.incendo:cloud-annotations:2.1.0-SNAPSHOT") + annotationProcessor("org.incendo:cloud-annotations:2.1.0-SNAPSHOT") + implementation("org.mineskin:java-client-jsoup:3.0.3-SNAPSHOT") + + compileOnly("com.intellectualsites.plotsquared:plotsquared-core:7.5.1") +} + +paper { + main = "de.oliver.fancynpcs.FancyNpcs" + bootstrapper = "de.oliver.fancynpcs.loaders.FancyNpcsBootstrapper" + loader = "de.oliver.fancynpcs.loaders.FancyNpcsLoader" + foliaSupported = true + version = rootProject.version.toString() + description = "Simple, lightweight and fast NPC plugin using packets" + apiVersion = "1.19" + serverDependencies { + register("PlaceholderAPI") { + required = false + load = PaperPluginDescription.RelativeLoadOrder.BEFORE + } + register("MiniPlaceholders") { + required = false + load = PaperPluginDescription.RelativeLoadOrder.BEFORE + } + register("PlotSquared") { + required = false + load = PaperPluginDescription.RelativeLoadOrder.BEFORE + } + } +} + +tasks { + runServer { + minecraftVersion("1.21.5") + + downloadPlugins { + hangar("ViaVersion", "5.2.1") + hangar("ViaBackwards", "5.2.1") + hangar("PlaceholderAPI", "2.11.6") +// modrinth("multiverse-core", "4.3.11") + } + } + + shadowJar { + relocate("org.incendo", "de.oliver") + relocate("org.lushplugins.chatcolorhandler", "de.oliver.fancynpcs.libs.chatcolorhandler") + archiveClassifier.set("") + dependsOn(":api:shadowJar") + } + + publishing { + repositories { + maven { + name = "fancypluginsReleases" + url = uri("https://repo.fancyplugins.de/releases") + credentials(PasswordCredentials::class) + authentication { + isAllowInsecureProtocol = true + create("basic") + } + } + + maven { + name = "fancypluginsSnapshots" + url = uri("https://repo.fancyplugins.de/snapshots") + credentials(PasswordCredentials::class) + authentication { + isAllowInsecureProtocol = true + create("basic") + } + } + } + publications { + create("maven") { + groupId = project.group.toString() + artifactId = project.name + version = project.version.toString() + from(project.components["java"]) + } + } + } + + compileJava { + options.encoding = Charsets.UTF_8.name() // We want UTF-8 for everything + options.release = 21 + // For cloud-annotations, see https://cloud.incendo.org/annotations/#command-components + options.compilerArgs.add("-parameters") + } + + javadoc { + options.encoding = Charsets.UTF_8.name() // We want UTF-8 for everything + } + + processResources { + filteringCharset = Charsets.UTF_8.name() // We want UTF-8 for everything + + val props = mapOf( + "description" to project.description, + "version" to project.version, + "hash" to getCurrentCommitHash(), + "build" to (System.getenv("BUILD_ID") ?: "").ifEmpty { "undefined" } + ) + + inputs.properties(props) + + filesMatching("paper-plugin.yml") { + expand(props) + } + + filesMatching("version.yml") { + expand(props) + } + } +} + +tasks.publishAllPublicationsToHangar { + dependsOn("shadowJar") +} + +tasks.modrinth { + dependsOn("shadowJar") +} + +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) +} + +fun getCurrentCommitHash(): String { + val process = ProcessBuilder("git", "rev-parse", "HEAD").start() + val reader = BufferedReader(InputStreamReader(process.inputStream)) + val commitHash = reader.readLine() + reader.close() + process.waitFor() + if (process.exitValue() == 0) { + return commitHash ?: "" + } else { + throw IllegalStateException("Failed to retrieve the commit hash.") + } +} + +fun getLastCommitMessage(): String { + val process = ProcessBuilder("git", "log", "-1", "--pretty=%B").start() + val reader = BufferedReader(InputStreamReader(process.inputStream)) + val commitMessage = reader.readLine() + reader.close() + process.waitFor() + if (process.exitValue() == 0) { + println("Commit message: $commitMessage") + return commitMessage ?: "" + } else { + throw IllegalStateException("Failed to retrieve the commit message.") + } +} + +hangarPublish { + publications.register("plugin") { + version = project.version as String + id = "FancyNpcs" + channel = "Alpha" + + apiKey.set(System.getenv("HANGAR_PUBLISH_API_TOKEN")) + + platforms { + paper { + jar = tasks.shadowJar.flatMap { it.archiveFile } + platformVersions.set(supportedVersions) + } + } + + changelog = getLastCommitMessage() + } +} + +modrinth { + token.set(System.getenv("MODRINTH_PUBLISH_API_TOKEN")) + projectId.set("fancynpcs") + versionNumber.set(project.version.toString()) + versionType.set("alpha") + uploadFile.set(file("build/libs/${project.name}-${project.version}.jar")) + gameVersions.addAll(supportedVersions) + loaders.add("paper") + loaders.add("folia") + changelog.set(getLastCommitMessage()) +} \ No newline at end of file diff --git a/plugins/fancynpcs/images/banner.png b/plugins/fancynpcs/images/banner.png new file mode 100644 index 00000000..0933b953 Binary files /dev/null and b/plugins/fancynpcs/images/banner.png differ diff --git a/plugins/fancynpcs/images/screenshots/dave1.jpeg b/plugins/fancynpcs/images/screenshots/dave1.jpeg new file mode 100644 index 00000000..043dc21a Binary files /dev/null and b/plugins/fancynpcs/images/screenshots/dave1.jpeg differ diff --git a/plugins/fancynpcs/images/screenshots/grabsky1.jpeg b/plugins/fancynpcs/images/screenshots/grabsky1.jpeg new file mode 100644 index 00000000..335dbd1e Binary files /dev/null and b/plugins/fancynpcs/images/screenshots/grabsky1.jpeg differ diff --git a/plugins/fancynpcs/images/screenshots/niceron1.jpeg b/plugins/fancynpcs/images/screenshots/niceron1.jpeg new file mode 100644 index 00000000..6b9df525 Binary files /dev/null and b/plugins/fancynpcs/images/screenshots/niceron1.jpeg differ diff --git a/plugins/fancynpcs/images/screenshots/niceron2.jpeg b/plugins/fancynpcs/images/screenshots/niceron2.jpeg new file mode 100644 index 00000000..3e3c93a4 Binary files /dev/null and b/plugins/fancynpcs/images/screenshots/niceron2.jpeg differ diff --git a/plugins/fancynpcs/images/screenshots/niceron3.jpeg b/plugins/fancynpcs/images/screenshots/niceron3.jpeg new file mode 100644 index 00000000..ca3b0154 Binary files /dev/null and b/plugins/fancynpcs/images/screenshots/niceron3.jpeg differ diff --git a/plugins/fancynpcs/images/screenshots/oliver1.jpeg b/plugins/fancynpcs/images/screenshots/oliver1.jpeg new file mode 100644 index 00000000..7becd6d2 Binary files /dev/null and b/plugins/fancynpcs/images/screenshots/oliver1.jpeg differ diff --git a/plugins/fancynpcs/images/screenshots/oliver2.jpeg b/plugins/fancynpcs/images/screenshots/oliver2.jpeg new file mode 100644 index 00000000..5df01ad6 Binary files /dev/null and b/plugins/fancynpcs/images/screenshots/oliver2.jpeg differ diff --git a/plugins/fancynpcs/implementation_1_19_4/build.gradle.kts b/plugins/fancynpcs/implementation_1_19_4/build.gradle.kts new file mode 100644 index 00000000..f395d532 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + id("java-library") + id("io.papermc.paperweight.userdev") +} + +val minecraftVersion = "1.19.4" + +dependencies { + paperweight.paperDevBundle("$minecraftVersion-R0.1-SNAPSHOT") + + compileOnly(project(":plugins:fancynpcs:api")) + compileOnly("de.oliver:FancyLib:37") + compileOnly("org.lushplugins:ChatColorHandler:5.1.3") +} + + +tasks { + named("assemble") { + dependsOn(named("reobfJar")) + } + + javadoc { + options.encoding = Charsets.UTF_8.name() + } + + compileJava { + options.encoding = Charsets.UTF_8.name() + options.release = 17 + } +} + +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) +} \ No newline at end of file diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/MappingKeys1_19_4.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/MappingKeys1_19_4.java new file mode 100644 index 00000000..f3f18cfd --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/MappingKeys1_19_4.java @@ -0,0 +1,23 @@ +package de.oliver.fancynpcs.v1_19_4; + +public enum MappingKeys1_19_4 { + + ENTITY_TYPE__FACTORY("bA"), + SYNCHED_ENTITY_DATA__ITEMS_BY_ID("e"), + CLIENTBOUND_TELEPORT_ENTITY_PACKET__X("b"), + CLIENTBOUND_TELEPORT_ENTITY_PACKET__Y("c"), + CLIENTBOUND_TELEPORT_ENTITY_PACKET__Z("d"), + CLIENTBOUND_PLAYER_INFO_UPDATE_PACKET__ENTRIES("b"), + PANDA__DATA_ID_FLAGS("ca"), + ; + + private final String mapping; + + MappingKeys1_19_4(String mapping) { + this.mapping = mapping; + } + + public String getMapping() { + return mapping; + } +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/Npc_1_19_4.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/Npc_1_19_4.java new file mode 100644 index 00000000..bb6144dc --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/Npc_1_19_4.java @@ -0,0 +1,391 @@ +package de.oliver.fancynpcs.v1_19_4; + +import com.google.common.collect.ImmutableList; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.datafixers.util.Pair; +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.events.NpcSpawnEvent; +import de.oliver.fancynpcs.api.utils.NpcEquipmentSlot; +import io.papermc.paper.adventure.PaperAdventure; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; +import net.minecraft.world.entity.Display; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.scores.PlayerTeam; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.scores.Team; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_19_R3.CraftServer; +import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_19_R3.util.CraftNamespacedKey; +import org.bukkit.entity.Player; +import org.lushplugins.chatcolorhandler.ModernChatColorHandler; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class Npc_1_19_4 extends Npc { + + private final String localName; + private final UUID uuid; + private Entity npc; + private Display.TextDisplay sittingVehicle; + + public Npc_1_19_4(NpcData data) { + super(data); + + this.localName = generateLocalName(); + this.uuid = UUID.randomUUID(); + } + + @Override + public void create() { + MinecraftServer minecraftServer = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel serverLevel = ((CraftWorld) data.getLocation().getWorld()).getHandle(); + GameProfile gameProfile = new GameProfile(uuid, localName); + + if (data.getType() == org.bukkit.entity.EntityType.PLAYER) { + npc = new ServerPlayer(minecraftServer, serverLevel, new GameProfile(uuid, "")); + ((ServerPlayer) npc).gameProfile = gameProfile; + } else { + EntityType nmsType = BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(data.getType().getKey())); + EntityType.EntityFactory factory = (EntityType.EntityFactory) ReflectionUtils.getValue(nmsType, MappingKeys1_19_4.ENTITY_TYPE__FACTORY.getMapping()); // EntityType.factory + npc = factory.create(nmsType, serverLevel); + isTeamCreated.clear(); + } + } + + @Override + public void spawn(Player player) { + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc == null) { + return; + } + + if (!data.getLocation().getWorld().getName().equalsIgnoreCase(serverPlayer.getLevel().getWorld().getName())) { + return; + } + + if (data.getSkinData() != null && data.getSkinData().hasTexture()) { + String value = data.getSkinData().getTextureValue(); + String signature = data.getSkinData().getTextureSignature(); + + ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues( + "textures", + ImmutableList.of(new Property("textures", value, signature)) + ); + } + + NpcSpawnEvent spawnEvent = new NpcSpawnEvent(this, player); + spawnEvent.callEvent(); + if (spawnEvent.isCancelled()) { + return; + } + + + if (npc instanceof ServerPlayer npcPlayer) { + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, List.of(npcPlayer)); + if (data.isMirrorSkin()) { + handleMirroredSkin(playerInfoPacket, serverPlayer); + } + serverPlayer.connection.send(playerInfoPacket); + + if (data.isSpawnEntity()) { + npc.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + ClientboundAddPlayerPacket spawnPlayerPacket = new ClientboundAddPlayerPacket(npcPlayer); + serverPlayer.connection.send(spawnPlayerPacket); + } + } + + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket(npc); + serverPlayer.connection.send(addEntityPacket); + + isVisibleForPlayer.put(player.getUniqueId(), true); + + int removeNpcsFromPlayerlistDelay = FancyNpcsPlugin.get().getFancyNpcConfig().getRemoveNpcsFromPlayerlistDelay(); + if (!data.isShowInTab() && removeNpcsFromPlayerlistDelay > 0) { + FancyNpcsPlugin.get().getNpcThread().schedule(() -> { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of(npc.getUUID())); + serverPlayer.connection.send(playerInfoRemovePacket); + }, removeNpcsFromPlayerlistDelay, TimeUnit.MILLISECONDS); + } + + update(player); + } + + @Override + public void remove(Player player) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc instanceof ServerPlayer npcPlayer) { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of((npcPlayer.getUUID()))); + serverPlayer.connection.send(playerInfoRemovePacket); + } + + // remove entity + ClientboundRemoveEntitiesPacket removeEntitiesPacket = new ClientboundRemoveEntitiesPacket(npc.getId()); + serverPlayer.connection.send(removeEntitiesPacket); + + // remove sitting vehicle + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + + isVisibleForPlayer.put(serverPlayer.getUUID(), false); + } + + @Override + public void lookAt(Player player, Location location) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setRot(location.getYaw(), location.getPitch()); + npc.setYHeadRot(location.getYaw()); + npc.setXRot(location.getPitch()); + npc.setYRot(location.getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket(npc); + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (location.getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + } + + @Override + public void update(Player player) { + if (npc == null) { + return; + } + + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + PlayerTeam team = new PlayerTeam(new Scoreboard(), "npc-" + localName); + team.getPlayers().clear(); + team.getPlayers().add(npc instanceof ServerPlayer npcPlayer ? npcPlayer.getGameProfile().getName() : npc.getStringUUID()); + team.setColor(PaperAdventure.asVanilla(data.getGlowingColor())); + if (!data.isCollidable()) { + team.setCollisionRule(Team.CollisionRule.NEVER); + } + + net.kyori.adventure.text.Component displayName = ModernChatColorHandler.translate(data.getDisplayName(), serverPlayer.getBukkitEntity()); + Component vanillaComponent = PaperAdventure.asVanilla(displayName); + if (!(npc instanceof ServerPlayer)) { + npc.setCustomName(vanillaComponent); + npc.setCustomNameVisible(true); + } else { + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } + + if (data.getDisplayName().equalsIgnoreCase("")) { + team.setNameTagVisibility(Team.Visibility.NEVER); + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } else { + team.setNameTagVisibility(Team.Visibility.ALWAYS); + } + + if (npc instanceof ServerPlayer npcPlayer) { + team.setPlayerPrefix(vanillaComponent); + npcPlayer.listName = vanillaComponent; + + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, List.of(npcPlayer)); + if (data.isMirrorSkin()) { + handleMirroredSkin(playerInfoPacket, serverPlayer); + } + serverPlayer.connection.send(playerInfoPacket); + } + + boolean isTeamCreatedForPlayer = this.isTeamCreated.getOrDefault(player.getUniqueId(), false); + serverPlayer.connection.send(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, !isTeamCreatedForPlayer)); + isTeamCreated.put(player.getUniqueId(), true); + + npc.setGlowingTag(data.isGlowing()); + + if (data.getEquipment() != null && data.getEquipment().size() > 0) { + List> equipmentList = new ArrayList<>(); + + for (NpcEquipmentSlot slot : data.getEquipment().keySet()) { + equipmentList.add(new Pair<>(EquipmentSlot.byName(slot.toNmsName()), CraftItemStack.asNMSCopy(data.getEquipment().get(slot)))); + } + + ClientboundSetEquipmentPacket setEquipmentPacket = new ClientboundSetEquipmentPacket(npc.getId(), equipmentList); + serverPlayer.connection.send(setEquipmentPacket); + } + + if (npc instanceof ServerPlayer) { + // Enable second layer of skin (https://wiki.vg/Entity_metadata#Player) + npc.getEntityData().set(net.minecraft.world.entity.player.Player.DATA_PLAYER_MODE_CUSTOMISATION, (byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40)); + } + + data.applyAllAttributes(this); + + refreshEntityData(player); + + if (data.isSpawnEntity() && data.getLocation() != null) { + move(player, true); + } + + NpcAttribute playerPoseAttr = FancyNpcsPlugin.get().getAttributeManager().getAttributeByName(org.bukkit.entity.EntityType.PLAYER, "pose"); + if (data.getAttributes().containsKey(playerPoseAttr)) { + String pose = data.getAttributes().get(playerPoseAttr); + + if (pose.equals("sitting")) { + setSitting(serverPlayer); + } else { + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + } + + } + } + + @Override + protected void refreshEntityData(Player player) { + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + Int2ObjectMap> itemsById = (Int2ObjectMap>) ReflectionUtils.getValue(npc.getEntityData(), MappingKeys1_19_4.SYNCHED_ENTITY_DATA__ITEMS_BY_ID.getMapping()); // itemsById + List> entityData = new ArrayList<>(); + for (SynchedEntityData.DataItem dataItem : itemsById.values()) { + entityData.add(dataItem.value()); + } + ClientboundSetEntityDataPacket setEntityDataPacket = new ClientboundSetEntityDataPacket(npc.getId(), entityData); + serverPlayer.connection.send(setEntityDataPacket); + } + + public void move(Player player, boolean swingArm) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setPosRaw(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + npc.setRot(data.getLocation().getYaw(), data.getLocation().getPitch()); + npc.setYHeadRot(data.getLocation().getYaw()); + npc.setXRot(data.getLocation().getPitch()); + npc.setYRot(data.getLocation().getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket(npc); + ReflectionUtils.setValue(teleportEntityPacket, MappingKeys1_19_4.CLIENTBOUND_TELEPORT_ENTITY_PACKET__X.getMapping(), data.getLocation().x()); // 'x' + ReflectionUtils.setValue(teleportEntityPacket, MappingKeys1_19_4.CLIENTBOUND_TELEPORT_ENTITY_PACKET__Y.getMapping(), data.getLocation().y()); // 'y' + ReflectionUtils.setValue(teleportEntityPacket, MappingKeys1_19_4.CLIENTBOUND_TELEPORT_ENTITY_PACKET__Z.getMapping(), data.getLocation().z()); // 'z' + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (data.getLocation().getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + + if (swingArm && npc instanceof ServerPlayer) { + ClientboundAnimatePacket animatePacket = new ClientboundAnimatePacket(npc, 0); + serverPlayer.connection.send(animatePacket); + } + } + + public void setSitting(ServerPlayer serverPlayer) { + if (npc == null) { + return; + } + + if (sittingVehicle == null) { + sittingVehicle = new Display.TextDisplay(EntityType.TEXT_DISPLAY, ((CraftWorld) data.getLocation().getWorld()).getHandle()); + } + + sittingVehicle.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket(sittingVehicle); + serverPlayer.connection.send(addEntityPacket); + + sittingVehicle.passengers = ImmutableList.of(npc); + + ClientboundSetPassengersPacket packet = new ClientboundSetPassengersPacket(sittingVehicle); + serverPlayer.connection.send(packet); + } + + private void handleMirroredSkin(ClientboundPlayerInfoUpdatePacket playerInfoUpdatePacket, ServerPlayer viewer) { + if (!ServerLoginPacketListenerImpl.isValidUsername(viewer.getGameProfile().getName())) return; + ClientboundPlayerInfoUpdatePacket.Entry entry = playerInfoUpdatePacket.entries().get(0); + GameProfile profile = entry.profile(); + GameProfile newProfile = new GameProfile(profile.getId(), profile.getName()); + newProfile.getProperties().putAll(viewer.getGameProfile().getProperties()); + ClientboundPlayerInfoUpdatePacket.Entry newEntry = new ClientboundPlayerInfoUpdatePacket.Entry( + entry.profileId(), + newProfile, + entry.listed(), + entry.latency(), + entry.gameMode(), + entry.displayName(), + entry.chatSession() + ); + + ReflectionUtils.setValue(playerInfoUpdatePacket, "b", List.of(newEntry)); // 'entries' + } + + @Override + public float getEyeHeight() { + return npc.getEyeHeight(); + } + + @Override + public int getEntityId() { + return npc.getId(); + } + + public Entity getNpc() { + return npc; + } +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/PacketReader_1_19_4.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/PacketReader_1_19_4.java new file mode 100644 index 00000000..54cd0e14 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/PacketReader_1_19_4.java @@ -0,0 +1,74 @@ +package de.oliver.fancynpcs.v1_19_4; + +import de.oliver.fancylib.FancyLib; +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.actions.ActionTrigger; +import de.oliver.fancynpcs.api.events.PacketReceivedEvent; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageDecoder; +import net.minecraft.network.protocol.game.ServerboundInteractPacket; +import net.minecraft.server.level.ServerPlayer; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.inventory.EquipmentSlot; + +import java.util.List; + +public class PacketReader_1_19_4 implements Listener { + + public static boolean inject(Player player) { + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + Channel channel = serverPlayer.connection.connection.channel; + + if (channel.pipeline().get("PacketInjector") != null) { + return false; + } + + channel.pipeline().addAfter("decoder", "PacketInjector", new MessageToMessageDecoder() { + @Override + protected void decode(ChannelHandlerContext ctx, ServerboundInteractPacket msg, List out) { + out.add(msg); + + PacketReceivedEvent packetReceivedEvent = new PacketReceivedEvent(msg, player); + FancyLib.getInstance().getScheduler().runTaskLater(null, 1L, packetReceivedEvent::callEvent); + } + }); + + return true; + } + + @EventHandler + public void onPacketReceived(final PacketReceivedEvent event) { + // Skipping packets other than ServerboundInteractPacket... + if (!(event.getPacket() instanceof ServerboundInteractPacket interactPacket)) + return; + // Getting NPC from entity identifier. + final Npc npc = FancyNpcsPlugin.get().getNpcManager().getNpc(interactPacket.getEntityId()); + // Skipping entities that are not FancyNpcs' NPCs... + if (npc == null) + return; + // Getting interaction information. + final boolean isAttack = (interactPacket.getActionType() == ServerboundInteractPacket.ActionType.ATTACK); + final boolean isInteract = (interactPacket.getActionType() == ServerboundInteractPacket.ActionType.INTERACT_AT); + final EquipmentSlot hand = (interactPacket.getActionType() == ServerboundInteractPacket.ActionType.ATTACK) + ? EquipmentSlot.HAND + : ReflectionUtils.getValue(ReflectionUtils.getValue(interactPacket, "b"), "a").toString().equals("MAIN_HAND") // ServerboundInteractPacket.InteractionAction.hand + ? EquipmentSlot.HAND + : EquipmentSlot.OFF_HAND; + // This can optionally be ALSO called for OFF-HAND slot. Making sure to run logic only ONCE. + if (hand == EquipmentSlot.HAND) { + // This packet can be sent multiple times for interactions that are NOT attacks, making sure to run logic only ONCE. + if (isAttack || !isInteract || npc.getData().getType() == EntityType.ARMOR_STAND) { + npc.interact(event.getPlayer(), isAttack ? ActionTrigger.LEFT_CLICK : ActionTrigger.RIGHT_CLICK); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/ReflectionHelper.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/ReflectionHelper.java new file mode 100644 index 00000000..fa085393 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/ReflectionHelper.java @@ -0,0 +1,13 @@ +package de.oliver.fancynpcs.v1_19_4; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import net.minecraft.world.entity.Entity; + +public class ReflectionHelper { + + public static T getEntity(Npc npc) { + return (T) ReflectionUtils.getValue(npc, "npc"); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/AgeableMobAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/AgeableMobAttributes.java new file mode 100644 index 00000000..35236704 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/AgeableMobAttributes.java @@ -0,0 +1,38 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.AgeableMob; +import org.bukkit.entity.Ageable; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AgeableMobAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "baby", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Ageable.class.isAssignableFrom(type.getEntityClass())) + .toList(), + AgeableMobAttributes::setBaby + )); + + return attributes; + } + + private static void setBaby(Npc npc, String value) { + AgeableMob mob = ReflectionHelper.getEntity(npc); + + boolean isBaby = Boolean.parseBoolean(value); + + mob.setBaby(isBaby); + } +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/AllayAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/AllayAttributes.java new file mode 100644 index 00000000..65d2860c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/AllayAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.animal.allay.Allay; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class AllayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.ALLAY), + AllayAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Allay allay = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + allay.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/ArmorStandAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/ArmorStandAttributes.java new file mode 100644 index 00000000..bfd9ba32 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/ArmorStandAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class ArmorStandAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "show_arms", + List.of("true", "false"), + List.of(EntityType.ARMOR_STAND), + ArmorStandAttributes::setShowArms + )); + + return attributes; + } + + private static void setShowArms(Npc npc, String value) { + ArmorStand armorStand = ReflectionHelper.getEntity(npc); + + boolean showArms = Boolean.parseBoolean(value.toLowerCase()); + + armorStand.setShowArms(showArms); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/Attributes_1_19_4.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/Attributes_1_19_4.java new file mode 100644 index 00000000..bc2ffb21 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/Attributes_1_19_4.java @@ -0,0 +1,51 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.NpcAttribute; + +import java.util.ArrayList; +import java.util.List; + +public class Attributes_1_19_4 { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.addAll(EntityAttributes.getAllAttributes()); + attributes.addAll(LivingEntityAttributes.getAllAttributes()); + attributes.addAll(AgeableMobAttributes.getAllAttributes()); + attributes.addAll(IllagerAttributes.getAllAttributes()); + attributes.addAll(SpellCasterAttributes.getAllAttributes()); + + attributes.addAll(PlayerAttributes.getAllAttributes()); + attributes.addAll(SheepAttributes.getAllAttributes()); + attributes.addAll(VillagerAttributes.getAllAttributes()); + attributes.addAll(FrogAttributes.getAllAttributes()); + attributes.addAll(HorseAttributes.getAllAttributes()); + attributes.addAll(ParrotAttributes.getAllAttributes()); + attributes.addAll(AxolotlAttributes.getAllAttributes()); + attributes.addAll(TropicalFishAttributes.getAllAttributes()); + attributes.addAll(FoxAttributes.getAllAttributes()); + attributes.addAll(PandaAttributes.getAllAttributes()); + attributes.addAll(GoatAttributes.getAllAttributes()); + attributes.addAll(AllayAttributes.getAllAttributes()); + attributes.addAll(CamelAttributes.getAllAttributes()); + attributes.addAll(RabbitAttributes.getAllAttributes()); + attributes.addAll(PiglinAttributes.getAllAttributes()); + attributes.addAll(CatAttributes.getAllAttributes()); + attributes.addAll(ShulkerAttributes.getAllAttributes()); + attributes.addAll(WolfAttributes.getAllAttributes()); + attributes.addAll(SlimeAttributes.getAllAttributes()); + attributes.addAll(PigAttributes.getAllAttributes()); + attributes.addAll(ArmorStandAttributes.getAllAttributes()); + attributes.addAll(BeeAttributes.getAllAttributes()); + attributes.addAll(VexAttributes.getAllAttributes()); + + attributes.addAll(DisplayAttributes.getAllAttributes()); + attributes.addAll(TextDisplayAttributes.getAllAttributes()); + attributes.addAll(BlockDisplayAttributes.getAllAttributes()); + attributes.addAll(InteractionAttributes.getAllAttributes()); + + return attributes; + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/AxolotlAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/AxolotlAttributes.java new file mode 100644 index 00000000..680fc9a3 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/AxolotlAttributes.java @@ -0,0 +1,51 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.animal.axolotl.Axolotl; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AxolotlAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Axolotl.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "playing_dead", + List.of("true", "false"), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setPlayingDead + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + Axolotl.Variant variant = Axolotl.Variant.valueOf(value.toUpperCase()); + axolotl.setVariant(variant); + } + + private static void setPlayingDead(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + boolean playingDead = Boolean.parseBoolean(value); + axolotl.setPlayingDead(playingDead); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/BeeAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/BeeAttributes.java new file mode 100644 index 00000000..0eccb723 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/BeeAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Bee; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BeeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setAngry + )); + + attributes.add(new NpcAttribute( + "sting", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setSting + )); + + attributes.add(new NpcAttribute( + "nectar", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setNectar + )); + + attributes.add(new NpcAttribute( + "rolling", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setRolling + )); + + return attributes; + } + + private static void setAngry(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRemainingPersistentAngerTime(1); + case "false" -> bee.setRemainingPersistentAngerTime(0); + } + } + + private static void setSting(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasStung(false); + case "false" -> bee.setHasStung(true); + } + } + + private static void setNectar(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasNectar(true); + case "false" -> bee.setHasNectar(false); + } + } + + private static void setRolling(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRolling(true); + case "false" -> bee.setRolling(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/BlockDisplayAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/BlockDisplayAttributes.java new file mode 100644 index 00000000..f87c2a69 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/BlockDisplayAttributes.java @@ -0,0 +1,42 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Display; +import net.minecraft.world.level.block.Block; +import org.bukkit.Material; +import org.bukkit.Registry; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.StreamSupport; + +public class BlockDisplayAttributes { + + private static final List BLOCKS = StreamSupport.stream(Registry.MATERIAL.spliterator(), false).filter(Material::isBlock).map(it -> it.key().value()).toList(); + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "block", + BLOCKS, + List.of(EntityType.BLOCK_DISPLAY), + BlockDisplayAttributes::setBlock + )); + + return attributes; + } + + private static void setBlock(Npc npc, String value) { + Display.BlockDisplay display = ReflectionHelper.getEntity(npc); + + Block block = BuiltInRegistries.BLOCK.get(ResourceLocation.of("minecraft:" + value.toLowerCase(), ':')); + + display.setBlockState(block.defaultBlockState()); + } +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/CamelAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/CamelAttributes.java new file mode 100644 index 00000000..5e62b8d0 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/CamelAttributes.java @@ -0,0 +1,46 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.animal.camel.Camel; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class CamelAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "dashing"), + List.of(EntityType.CAMEL), + CamelAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Camel camel = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + camel.setDashing(false); + camel.standUp(); + } + case "sitting" -> { + camel.setDashing(false); + camel.sitDown(); + } + case "dashing" -> { + camel.standUp(); + camel.setDashing(true); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/CatAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/CatAttributes.java new file mode 100644 index 00000000..0861d66b --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/CatAttributes.java @@ -0,0 +1,64 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.Cat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class CatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(org.bukkit.entity.Cat.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.CAT), + CatAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.CAT), + CatAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + BuiltInRegistries.CAT_VARIANT.getOptional(ResourceLocation.of(value.toLowerCase(), ':')) + .ifPresent(cat::setVariant); + } + + private static void setPose(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + switch (value.toLowerCase()) { + case "standing" -> { + cat.setInSittingPose(false, false); + cat.setLying(false); + } + case "sleeping" -> { + cat.setInSittingPose(false, false); + cat.setLying(true); + } + case "sitting" -> { + cat.setLying(false); + cat.setOrderedToSit(true); + cat.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/DisplayAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/DisplayAttributes.java new file mode 100644 index 00000000..6a5482b6 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/DisplayAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class DisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "billboard", + Arrays.stream(org.bukkit.entity.Display.Billboard.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TEXT_DISPLAY, EntityType.BLOCK_DISPLAY, EntityType.ITEM_DISPLAY), + DisplayAttributes::setBillboard + )); + + return attributes; + } + + private static void setBillboard(Npc npc, String value) { + Display display = ReflectionHelper.getEntity(npc); + + Display.BillboardConstraints billboard = Display.BillboardConstraints.valueOf(value.toUpperCase()); + display.setBillboardConstraints(billboard); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/EntityAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/EntityAttributes.java new file mode 100644 index 00000000..fa2c28d9 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/EntityAttributes.java @@ -0,0 +1,103 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.Entity; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class EntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "on_fire", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnFire + )); + + attributes.add(new NpcAttribute( + "invisible", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setInvisible + )); + + attributes.add(new NpcAttribute( + "silent", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setSilent + )); + + attributes.add(new NpcAttribute( + "shaking", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setShaking + )); + + attributes.add(new NpcAttribute( + "on_ground", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnGround + )); + + /*attributes.add(new NpcAttribute( + "entity_pose", + Arrays.stream(Pose.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setEntityPose + ));*/ + + return attributes; + } + + private static void setOnFire(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onFire = Boolean.parseBoolean(value); + + entity.setSharedFlagOnFire(onFire); + + } + + private static void setInvisible(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean invisible = Boolean.parseBoolean(value); + + entity.setInvisible(invisible); + } + + private static void setSilent(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean silent = Boolean.parseBoolean(value); + + entity.setSilent(silent); + } + + private static void setShaking(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean shaking = Boolean.parseBoolean(value); + + entity.setTicksFrozen(shaking ? entity.getTicksRequiredToFreeze() : 0); + } + + private static void setOnGround(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onGround = Boolean.parseBoolean(value); + + entity.setOnGround(onGround); + } +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/FoxAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/FoxAttributes.java new file mode 100644 index 00000000..04325d2d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/FoxAttributes.java @@ -0,0 +1,66 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Fox; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FoxAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "type", + Arrays.stream(Fox.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.FOX), + FoxAttributes::setType + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.FOX), + FoxAttributes::setPose + )); + + return attributes; + } + + private static void setType(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + Fox.Type type = Fox.Type.valueOf(value.toUpperCase()); + fox.setVariant(type); + } + + private static void setPose(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + fox.setIsCrouching(false); + fox.setSleeping(false); + fox.setSitting(false, false); + } + case "sleeping" -> { + fox.setSleeping(true); + fox.setSitting(false, false); + fox.setIsCrouching(false); + } + case "sitting" -> { + fox.setSitting(true, false); + fox.setSleeping(false); + fox.setIsCrouching(false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/FrogAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/FrogAttributes.java new file mode 100644 index 00000000..8e9e4cbd --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/FrogAttributes.java @@ -0,0 +1,44 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.animal.FrogVariant; +import net.minecraft.world.entity.animal.frog.Frog; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FrogAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(org.bukkit.entity.Frog.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.FROG), + FrogAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Frog frog = ReflectionHelper.getEntity(npc); + + FrogVariant variant; + switch (value.toUpperCase()) { + case "COLD" -> variant = FrogVariant.COLD; + case "WARM" -> variant = FrogVariant.WARM; + default -> variant = FrogVariant.TEMPERATE; + } + + frog.setVariant(variant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/GoatAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/GoatAttributes.java new file mode 100644 index 00000000..78cd93cb --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/GoatAttributes.java @@ -0,0 +1,44 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.animal.goat.Goat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class GoatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "horns", + List.of("none", "left", "right", "both"), + List.of(EntityType.GOAT), + GoatAttributes::setHorns + )); + + return attributes; + } + + private static void setHorns(Npc npc, String value) { + Goat goat = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "none" -> goat.removeHorns(); + case "both" -> goat.addHorns(); + case "left" -> { + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, false); + } + case "right" -> { + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/HorseAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/HorseAttributes.java new file mode 100644 index 00000000..be1be861 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/HorseAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.animal.horse.Horse; +import net.minecraft.world.entity.animal.horse.Markings; +import net.minecraft.world.entity.animal.horse.Variant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class HorseAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "markings", + Arrays.stream(Markings.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setMarkings + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("eating", "rearing", "standing"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && (type == EntityType.HORSE || type == EntityType.DONKEY || + type == EntityType.MULE || type == EntityType.SKELETON_HORSE ||type == EntityType.ZOMBIE_HORSE)) + .toList(), + HorseAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Variant variant = Variant.valueOf(value.toUpperCase()); + horse.setVariant(variant); + } + + private static void setMarkings(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Markings markings = Markings.valueOf(value.toUpperCase()); + horse.setVariantAndMarkings(horse.getVariant(), markings); + } + + private static void setPose(Npc npc, String value) { + net.minecraft.world.entity.animal.horse.AbstractHorse horse = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + horse.setEating(false); + horse.setForceStanding(false); + } + case "rearing" -> { + horse.setForceStanding(true); + horse.setEating(false); + } + case "eating" -> { + horse.setForceStanding(false); + horse.setEating(true); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/IllagerAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/IllagerAttributes.java new file mode 100644 index 00000000..6f12d350 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/IllagerAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.raid.Raider; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Illager; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class IllagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "celebrating", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Illager.class.isAssignableFrom(type.getEntityClass())) + .toList(), + IllagerAttributes::setCelebrating + )); + + return attributes; + } + + private static void setCelebrating(Npc npc, String value) { + Raider raider = ReflectionHelper.getEntity(npc); + + boolean isCelebrating = Boolean.parseBoolean(value); + + raider.setCelebrating(isCelebrating); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/InteractionAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/InteractionAttributes.java new file mode 100644 index 00000000..7b9ab72f --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/InteractionAttributes.java @@ -0,0 +1,60 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.Interaction; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class InteractionAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "height", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setHeight + )); + + attributes.add(new NpcAttribute( + "width", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setWidth + )); + + return attributes; + } + + private static void setHeight(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float height; + try { + height = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setHeight(height); + } + + private static void setWidth(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float width; + try { + width = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setWidth(width); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/LivingEntityAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/LivingEntityAttributes.java new file mode 100644 index 00000000..b2afa53e --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/LivingEntityAttributes.java @@ -0,0 +1,67 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.InteractionHand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class LivingEntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + /*attributes.add(new NpcAttribute( + "hurt", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setHurt + ));*/ + + attributes.add(new NpcAttribute( + "use_item", + List.of("main_hand", "off_hand", "none"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setUseItem + )); + + return attributes; + } + + private static void setHurt(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + boolean isHurt = Boolean.parseBoolean(value); + + if (isHurt) { + livingEntity.hurtDuration = 1; + livingEntity.hurtTime = 1; + livingEntity.hurtMarked = true; + livingEntity.animateHurt(0); + } else { + livingEntity.hurtDuration = 0; + livingEntity.hurtTime = 0; + livingEntity.hurtMarked = false; + } + } + + private static void setUseItem(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + switch (value.toUpperCase()) { + case "NONE" -> livingEntity.stopUsingItem(); + case "MAIN_HAND" -> livingEntity.startUsingItem(InteractionHand.MAIN_HAND, true); + case "OFF_HAND" -> livingEntity.startUsingItem(InteractionHand.OFF_HAND, true); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/PandaAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/PandaAttributes.java new file mode 100644 index 00000000..0b2306d8 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/PandaAttributes.java @@ -0,0 +1,102 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.MappingKeys1_19_4; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.animal.Panda; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PandaAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "gene", + Arrays.stream(Panda.Gene.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PANDA), + PandaAttributes::setGene + )); + + attributes.add(new NpcAttribute( + "eating", + List.of("true", "false"), + List.of(EntityType.PANDA), + PandaAttributes::setEating + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "onBack", "rolling"), + List.of(EntityType.PANDA), + PandaAttributes::setPose + )); + + return attributes; + } + + private static void setGene(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + Panda.Gene gene = Panda.Gene.valueOf(value.toUpperCase()); + panda.setMainGene(gene); + } + + private static void setPose(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(false); + } + case "sitting" -> { + panda.roll(false); + panda.setOnBack(false); + setFlag(panda, 8, true); //sitting + } + case "onback" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(true); + } + case "rolling" -> { + setFlag(panda, 8, false); //sitting + panda.setOnBack(false); + panda.roll(true); + } + } + } + + private static void setEating(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + boolean eating = Boolean.parseBoolean(value); + + panda.eat(eating); + } + + private static void setFlag(Panda panda, int mask, boolean value) { + EntityDataAccessor DATA_ID_FLAGS = (EntityDataAccessor) ReflectionUtils.getValue(panda, MappingKeys1_19_4.PANDA__DATA_ID_FLAGS.getMapping()); + + byte b0 = panda.getEntityData().get(DATA_ID_FLAGS); + + if (value) { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 | mask)); + } else { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 & ~mask)); + } + + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/ParrotAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/ParrotAttributes.java new file mode 100644 index 00000000..485bda70 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/ParrotAttributes.java @@ -0,0 +1,59 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Parrot; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ParrotAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Parrot.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PARROT), + ParrotAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.PARROT), + ParrotAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + Parrot.Variant variant = Parrot.Variant.valueOf(value.toUpperCase()); + parrot.setVariant(variant); + } + + private static void setPose(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + parrot.setOrderedToSit(false); + parrot.setInSittingPose(false, false); + } + case "sitting" -> { + parrot.setOrderedToSit(true); + parrot.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/PigAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/PigAttributes.java new file mode 100644 index 00000000..15308dd5 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/PigAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Pig; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PigAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "has_saddle", + List.of("true", "false"), + List.of(EntityType.PIG), + PigAttributes::setHasSaddle + )); + + return attributes; + } + + private static void setHasSaddle(Npc npc, String value) { + Pig pig = ReflectionHelper.getEntity(npc); + + boolean hasSaddle = Boolean.parseBoolean(value.toLowerCase()); + + pig.steering.setSaddle(hasSaddle); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/PiglinAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/PiglinAttributes.java new file mode 100644 index 00000000..03f97ee0 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/PiglinAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.monster.piglin.Piglin; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PiglinAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.PIGLIN), + PiglinAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Piglin piglin = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + piglin.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/PlayerAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/PlayerAttributes.java new file mode 100644 index 00000000..d09d7ce5 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/PlayerAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.entity.player.Player; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PlayerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "crouching", "sleeping", "swimming", "sitting"), + List.of(EntityType.PLAYER), + PlayerAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Player player = ReflectionHelper.getEntity(npc); + + Pose pose = Pose.valueOf(value.toUpperCase()); + + EntityDataAccessor DATA_POSE = (EntityDataAccessor) ReflectionUtils.getStaticValue(Entity.class, "ar"); // DATA_POSE + player.getEntityData().set(DATA_POSE, pose); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/RabbitAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/RabbitAttributes.java new file mode 100644 index 00000000..a1862a33 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/RabbitAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Rabbit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class RabbitAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Rabbit.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.RABBIT), + RabbitAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Rabbit rabbit = ReflectionHelper.getEntity(npc); + + Rabbit.Variant variant = Rabbit.Variant.valueOf(value.toUpperCase()); + rabbit.setVariant(variant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/SheepAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/SheepAttributes.java new file mode 100644 index 00000000..0c1a49f0 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/SheepAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Sheep; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SheepAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "wool_color", + Arrays.stream(DyeColor.values()).map(dyeColor -> dyeColor.name().toLowerCase()).toList(), + List.of(EntityType.SHEEP), + SheepAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "sheared", + Arrays.asList("true", "false"), + List.of(EntityType.SHEEP), + SheepAttributes::setSheared + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + sheep.setColor(DyeColor.byName(value.toLowerCase(), DyeColor.WHITE)); + } + + private static void setSheared(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + boolean sheared = Boolean.parseBoolean(value); + + sheep.setSheared(sheared); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/ShulkerAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/ShulkerAttributes.java new file mode 100644 index 00000000..36b467b1 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/ShulkerAttributes.java @@ -0,0 +1,55 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class ShulkerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.SHULKER), + ShulkerAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "shield", + List.of("open", "closed"), + List.of(EntityType.SHULKER), + ShulkerAttributes::setShield + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.PURPLE); + shulker.setVariant(Optional.of(color)); + } + + private static void setShield(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "closed" -> shulker.setRawPeekAmount(0); + case "open" -> shulker.setRawPeekAmount(Byte.MAX_VALUE); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/SlimeAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/SlimeAttributes.java new file mode 100644 index 00000000..820d4d2d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/SlimeAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.monster.Slime; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class SlimeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "size", + new ArrayList<>(), + List.of(EntityType.SLIME), + SlimeAttributes::setSize + )); + + return attributes; + } + + private static void setSize(Npc npc, String value) { + Slime slime = ReflectionHelper.getEntity(npc); + + int size; + try { + size = Integer.parseInt(value); + } catch (NumberFormatException e) { + return; + } + + slime.setSize(size, false); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/SpellCasterAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/SpellCasterAttributes.java new file mode 100644 index 00000000..941ec73a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/SpellCasterAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.monster.SpellcasterIllager; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Spellcaster; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SpellCasterAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "casting", + Arrays.stream(SpellcasterIllager.IllagerSpell.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Spellcaster.class.isAssignableFrom(type.getEntityClass())) + .toList(), + SpellCasterAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + SpellcasterIllager spellcasterIllager = ReflectionHelper.getEntity(npc); + + SpellcasterIllager.IllagerSpell spell = SpellcasterIllager.IllagerSpell.valueOf(value); + + spellcasterIllager.setIsCastingSpell(spell); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/TextDisplayAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/TextDisplayAttributes.java new file mode 100644 index 00000000..c62a585e --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/TextDisplayAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import io.papermc.paper.adventure.PaperAdventure; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class TextDisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "text", + new ArrayList<>(), + List.of(EntityType.TEXT_DISPLAY), + TextDisplayAttributes::setText + )); + + return attributes; + } + + private static void setText(Npc npc, String value) { + Display.TextDisplay display = ReflectionHelper.getEntity(npc); + + Component text = MiniMessage.miniMessage().deserialize(value); + display.setText(PaperAdventure.asVanilla(text)); + } +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/TropicalFishAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/TropicalFishAttributes.java new file mode 100644 index 00000000..c2db364d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/TropicalFishAttributes.java @@ -0,0 +1,72 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.animal.TropicalFish; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TropicalFishAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pattern", + Arrays.stream(TropicalFish.Pattern.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPattern + )); + + attributes.add(new NpcAttribute( + "base_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setBaseColor + )); + + attributes.add(new NpcAttribute( + "pattern_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPatternColor + )); + + return attributes; + } + + private static void setPattern(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + TropicalFish.Pattern pattern = TropicalFish.Pattern.valueOf(value.toUpperCase()); + tropicalFish.setVariant(pattern); + } + + private static void setBaseColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), color, tropicalFish.getPatternColor()); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + + private static void setPatternColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), tropicalFish.getBaseColor(), color); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/VexAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/VexAttributes.java new file mode 100644 index 00000000..9082f066 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/VexAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.monster.Vex; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class VexAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "charging", + List.of("true", "false"), + List.of(EntityType.VEX), + VexAttributes::setCharging + )); + + return attributes; + } + + private static void setCharging(Npc npc, String value) { + Vex vex = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> vex.setIsCharging(true); + case "false" -> vex.setIsCharging(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/VillagerAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/VillagerAttributes.java new file mode 100644 index 00000000..0ce26b60 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/VillagerAttributes.java @@ -0,0 +1,86 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.entity.npc.VillagerType; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class VillagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "profession", + Arrays.stream(org.bukkit.entity.Villager.Profession.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setProfession + )); + + attributes.add(new NpcAttribute( + "type", + Arrays.stream(org.bukkit.entity.Villager.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setType + )); + + return attributes; + } + + private static void setProfession(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerProfession profession; + switch (value.toUpperCase()) { + case "ARMORER" -> profession = VillagerProfession.ARMORER; + case "BUTCHER" -> profession = VillagerProfession.BUTCHER; + case "CARTOGRAPHER" -> profession = VillagerProfession.CARTOGRAPHER; + case "CLERIC" -> profession = VillagerProfession.CLERIC; + case "FARMER" -> profession = VillagerProfession.FARMER; + case "FISHERMAN" -> profession = VillagerProfession.FISHERMAN; + case "FLETCHER" -> profession = VillagerProfession.FLETCHER; + case "LEATHERWORKER" -> profession = VillagerProfession.LEATHERWORKER; + case "LIBRARIAN" -> profession = VillagerProfession.LIBRARIAN; + case "MASON" -> profession = VillagerProfession.MASON; + case "NITWIT" -> profession = VillagerProfession.NITWIT; + case "SHEPHERD" -> profession = VillagerProfession.SHEPHERD; + case "TOOLSMITH" -> profession = VillagerProfession.TOOLSMITH; + case "WEAPONSMITH" -> profession = VillagerProfession.WEAPONSMITH; + + default -> profession = VillagerProfession.NONE; + } + + villager.setVillagerData(villager.getVillagerData().setProfession(profession)); + } + + private static void setType(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerType type; + switch (value.toUpperCase()) { + case "DESERT" -> type = VillagerType.DESERT; + case "JUNGLE" -> type = VillagerType.JUNGLE; + case "SAVANNA" -> type = VillagerType.SAVANNA; + case "SNOW" -> type = VillagerType.SNOW; + case "SWAMP" -> type = VillagerType.SWAMP; + case "TAIGA" -> type = VillagerType.TAIGA; + + default -> type = VillagerType.PLAINS; + } + + + villager.setVillagerData(villager.getVillagerData().setType(type)); + } + +} diff --git a/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/WolfAttributes.java b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/WolfAttributes.java new file mode 100644 index 00000000..2233fcbf --- /dev/null +++ b/plugins/fancynpcs/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/attributes/WolfAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_19_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Wolf; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class WolfAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.WOLF), + WolfAttributes::setPose + )); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.WOLF), + WolfAttributes::setAngry + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> wolf.setInSittingPose(false, false); + case "sitting" -> wolf.setInSittingPose(true, false); + } + } + + private static void setAngry(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + boolean angry = Boolean.parseBoolean(value.toLowerCase()); + + wolf.setRemainingPersistentAngerTime(angry ? 100 : 0); + } +} diff --git a/plugins/fancynpcs/implementation_1_20/build.gradle.kts b/plugins/fancynpcs/implementation_1_20/build.gradle.kts new file mode 100644 index 00000000..3d9c5563 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20/build.gradle.kts @@ -0,0 +1,31 @@ +plugins { + id("java-library") + id("io.papermc.paperweight.userdev") +} + +val minecraftVersion = "1.20" + +dependencies { + paperweight.paperDevBundle("$minecraftVersion-R0.1-SNAPSHOT") + + compileOnly(project(":plugins:fancynpcs:api")) + compileOnly("de.oliver:FancyLib:37") + compileOnly("org.lushplugins:ChatColorHandler:5.1.3") +} + + +tasks { + named("assemble") { + dependsOn(named("reobfJar")) + } + + javadoc { + options.encoding = Charsets.UTF_8.name() + } + + compileJava { + options.encoding = Charsets.UTF_8.name() + options.release = 17 + + } +} \ No newline at end of file diff --git a/plugins/fancynpcs/implementation_1_20/src/main/java/de/oliver/fancynpcs/v1_20/PacketReader_1_20.java b/plugins/fancynpcs/implementation_1_20/src/main/java/de/oliver/fancynpcs/v1_20/PacketReader_1_20.java new file mode 100644 index 00000000..f2cb1819 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20/src/main/java/de/oliver/fancynpcs/v1_20/PacketReader_1_20.java @@ -0,0 +1,76 @@ +package de.oliver.fancynpcs.v1_20; + +import de.oliver.fancylib.FancyLib; +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.actions.ActionTrigger; +import de.oliver.fancynpcs.api.events.PacketReceivedEvent; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageDecoder; +import net.minecraft.network.protocol.game.ServerboundInteractPacket; +import net.minecraft.server.level.ServerPlayer; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.inventory.EquipmentSlot; + +import java.util.List; + +public class PacketReader_1_20 implements Listener { + + public static boolean inject(Player player) { + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + Channel channel = serverPlayer.connection.connection.channel; + + if (channel.pipeline().get("PacketInjector") != null) { + return false; + } + + channel.pipeline().addAfter("decoder", "PacketInjector", new MessageToMessageDecoder() { + @Override + protected void decode(ChannelHandlerContext ctx, ServerboundInteractPacket msg, List out) { + out.add(msg); + + PacketReceivedEvent packetReceivedEvent = new PacketReceivedEvent(msg, player); + FancyLib.getInstance().getScheduler().runTaskLater(null, 1L, packetReceivedEvent::callEvent); + } + }); + + return true; + } + + @EventHandler + public void onPacketReceived(final PacketReceivedEvent event) { + // Skipping packets other than ServerboundInteractPacket... + if (!(event.getPacket() instanceof ServerboundInteractPacket interactPacket)) + return; + // Getting entity identifier. + final int entityId = interactPacket.getEntityId(); + // Getting NPC from entity identifier. + final Npc npc = FancyNpcsPlugin.get().getNpcManager().getNpc(entityId); + // Skipping entities that are not FancyNpcs' NPCs... + if (npc == null) + return; + // Getting interaction information. + final boolean isAttack = (interactPacket.getActionType() == ServerboundInteractPacket.ActionType.ATTACK); + final boolean isInteract = (interactPacket.getActionType() == ServerboundInteractPacket.ActionType.INTERACT_AT); + final EquipmentSlot hand = (interactPacket.getActionType() == ServerboundInteractPacket.ActionType.ATTACK) + ? EquipmentSlot.HAND + : ReflectionUtils.getValue(ReflectionUtils.getValue(interactPacket, "b"), "a").toString().equals("MAIN_HAND") // ServerboundInteractPacket.InteractionAction.hand + ? EquipmentSlot.HAND + : EquipmentSlot.OFF_HAND; + // This can optionally be ALSO called for OFF-HAND slot. Making sure to run logic only ONCE. + if (hand == EquipmentSlot.HAND) { + // This packet can be sent multiple times for interactions that are NOT attacks, making sure to run logic only ONCE. + if (isAttack || !isInteract || npc.getData().getType() == EntityType.ARMOR_STAND) { + npc.interact(event.getPlayer(), isAttack ? ActionTrigger.LEFT_CLICK : ActionTrigger.RIGHT_CLICK); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/build.gradle.kts b/plugins/fancynpcs/implementation_1_20_1/build.gradle.kts new file mode 100644 index 00000000..58e7aac8 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/build.gradle.kts @@ -0,0 +1,31 @@ +plugins { + id("java-library") + id("io.papermc.paperweight.userdev") +} + +val minecraftVersion = "1.20.1" + +dependencies { + paperweight.paperDevBundle("$minecraftVersion-R0.1-SNAPSHOT") + + compileOnly(project(":plugins:fancynpcs:api")) + compileOnly("de.oliver:FancyLib:37") + compileOnly("org.lushplugins:ChatColorHandler:5.1.3") +} + + +tasks { + named("assemble") { + dependsOn(named("reobfJar")) + } + + javadoc { + options.encoding = Charsets.UTF_8.name() + } + + compileJava { + options.encoding = Charsets.UTF_8.name() + options.release = 17 + + } +} \ No newline at end of file diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/MappingKeys1_20_1.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/MappingKeys1_20_1.java new file mode 100644 index 00000000..fcdfaf59 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/MappingKeys1_20_1.java @@ -0,0 +1,23 @@ +package de.oliver.fancynpcs.v1_20_1; + +public enum MappingKeys1_20_1 { + + ENTITY_TYPE__FACTORY("bA"), + SYNCHED_ENTITY_DATA__ITEMS_BY_ID("e"), + CLIENTBOUND_TELEPORT_ENTITY_PACKET__X("b"), + CLIENTBOUND_TELEPORT_ENTITY_PACKET__Y("c"), + CLIENTBOUND_TELEPORT_ENTITY_PACKET__Z("d"), + CLIENTBOUND_PLAYER_INFO_UPDATE_PACKET__ENTRIES("b"), + PANDA__DATA_ID_FLAGS("cb"), + ; + + private final String mapping; + + MappingKeys1_20_1(String mapping) { + this.mapping = mapping; + } + + public String getMapping() { + return mapping; + } +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/Npc_1_20_1.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/Npc_1_20_1.java new file mode 100644 index 00000000..c3d92af5 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/Npc_1_20_1.java @@ -0,0 +1,389 @@ +package de.oliver.fancynpcs.v1_20_1; + +import com.google.common.collect.ImmutableList; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.datafixers.util.Pair; +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.events.NpcSpawnEvent; +import de.oliver.fancynpcs.api.utils.NpcEquipmentSlot; +import io.papermc.paper.adventure.PaperAdventure; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.minecraft.Optionull; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.RemoteChatSession; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Display; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.scores.PlayerTeam; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.scores.Team; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_20_R1.CraftServer; +import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_20_R1.util.CraftNamespacedKey; +import org.bukkit.entity.Player; +import org.lushplugins.chatcolorhandler.ModernChatColorHandler; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class Npc_1_20_1 extends Npc { + + private final String localName; + private final UUID uuid; + private Entity npc; + private Display.TextDisplay sittingVehicle; + + public Npc_1_20_1(NpcData data) { + super(data); + + this.localName = generateLocalName(); + this.uuid = UUID.randomUUID(); + } + + @Override + public void create() { + MinecraftServer minecraftServer = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel serverLevel = ((CraftWorld) data.getLocation().getWorld()).getHandle(); + GameProfile gameProfile = new GameProfile(uuid, localName); + + if (data.getType() == org.bukkit.entity.EntityType.PLAYER) { + npc = new ServerPlayer(minecraftServer, serverLevel, new GameProfile(uuid, "")); + ((ServerPlayer) npc).gameProfile = gameProfile; + } else { + EntityType nmsType = BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(data.getType().getKey())); + EntityType.EntityFactory factory = (EntityType.EntityFactory) ReflectionUtils.getValue(nmsType, MappingKeys1_20_1.ENTITY_TYPE__FACTORY.getMapping()); // EntityType.factory + npc = factory.create(nmsType, serverLevel); + isTeamCreated.clear(); + } + } + + @Override + public void spawn(Player player) { + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc == null) { + return; + } + + if (!data.getLocation().getWorld().getName().equalsIgnoreCase(serverPlayer.level().getWorld().getName())) { + return; + } + + if (data.getSkinData() != null && data.getSkinData().hasTexture()) { + String value = data.getSkinData().getTextureValue(); + String signature = data.getSkinData().getTextureSignature(); + + ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues( + "textures", + ImmutableList.of(new Property("textures", value, signature)) + ); + } + + NpcSpawnEvent spawnEvent = new NpcSpawnEvent(this, player); + spawnEvent.callEvent(); + if (spawnEvent.isCancelled()) { + return; + } + + + if (npc instanceof ServerPlayer npcPlayer) { + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + // The constructor ClientboundPlayerInfoUpdatePacket(actions, entries) is not available in 1.20 + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, List.of(npcPlayer)); // KEEP + List entries = List.of(getEntry(npcPlayer, serverPlayer)); // KEEP + ReflectionUtils.setValue(playerInfoPacket, MappingKeys1_20_1.CLIENTBOUND_PLAYER_INFO_UPDATE_PACKET__ENTRIES.getMapping(), entries); // KEEP + serverPlayer.connection.send(playerInfoPacket); + + if (data.isSpawnEntity()) { + npc.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + ClientboundAddPlayerPacket spawnPlayerPacket = new ClientboundAddPlayerPacket(npcPlayer); // # keep! + serverPlayer.connection.send(spawnPlayerPacket); // # keep! + } + } + + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket(npc); // # keep! + serverPlayer.connection.send(addEntityPacket); // # keep! + + isVisibleForPlayer.put(player.getUniqueId(), true); + + int removeNpcsFromPlayerlistDelay = FancyNpcsPlugin.get().getFancyNpcConfig().getRemoveNpcsFromPlayerlistDelay(); + if (!data.isShowInTab() && removeNpcsFromPlayerlistDelay > 0) { + FancyNpcsPlugin.get().getNpcThread().schedule(() -> { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of(npc.getUUID())); + serverPlayer.connection.send(playerInfoRemovePacket); + }, removeNpcsFromPlayerlistDelay, TimeUnit.MILLISECONDS); + } + + update(player); + } + + @Override + public void remove(Player player) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc instanceof ServerPlayer npcPlayer) { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of((npcPlayer.getUUID()))); + serverPlayer.connection.send(playerInfoRemovePacket); + } + + // remove entity + ClientboundRemoveEntitiesPacket removeEntitiesPacket = new ClientboundRemoveEntitiesPacket(npc.getId()); + serverPlayer.connection.send(removeEntitiesPacket); + + // remove sitting vehicle + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + + isVisibleForPlayer.put(serverPlayer.getUUID(), false); + } + + @Override + public void lookAt(Player player, Location location) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setRot(location.getYaw(), location.getPitch()); + npc.setYHeadRot(location.getYaw()); + npc.setXRot(location.getPitch()); + npc.setYRot(location.getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket(npc); + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (location.getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + } + + @Override + public void update(Player player) { + if (npc == null) { + return; + } + + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + PlayerTeam team = new PlayerTeam(new Scoreboard(), "npc-" + localName); + team.getPlayers().clear(); + team.getPlayers().add(npc instanceof ServerPlayer npcPlayer ? npcPlayer.getGameProfile().getName() : npc.getStringUUID()); + team.setColor(PaperAdventure.asVanilla(data.getGlowingColor())); + if (!data.isCollidable()) { + team.setCollisionRule(Team.CollisionRule.NEVER); + } + + net.kyori.adventure.text.Component displayName = ModernChatColorHandler.translate(data.getDisplayName(), serverPlayer.getBukkitEntity()); + Component vanillaComponent = PaperAdventure.asVanilla(displayName); + if (!(npc instanceof ServerPlayer)) { + npc.setCustomName(vanillaComponent); + npc.setCustomNameVisible(true); + } else { + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } + + if (data.getDisplayName().equalsIgnoreCase("")) { + team.setNameTagVisibility(Team.Visibility.NEVER); + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } else { + team.setNameTagVisibility(Team.Visibility.ALWAYS); + } + + if (npc instanceof ServerPlayer npcPlayer) { + team.setPlayerPrefix(vanillaComponent); + npcPlayer.listName = vanillaComponent; + + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, getEntry(npcPlayer, serverPlayer)); + serverPlayer.connection.send(playerInfoPacket); + } + + boolean isTeamCreatedForPlayer = this.isTeamCreated.getOrDefault(player.getUniqueId(), false); + serverPlayer.connection.send(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, !isTeamCreatedForPlayer)); + isTeamCreated.put(player.getUniqueId(), true); + + npc.setGlowingTag(data.isGlowing()); + + if (data.getEquipment() != null && data.getEquipment().size() > 0) { + List> equipmentList = new ArrayList<>(); + + for (NpcEquipmentSlot slot : data.getEquipment().keySet()) { + equipmentList.add(new Pair<>(EquipmentSlot.byName(slot.toNmsName()), CraftItemStack.asNMSCopy(data.getEquipment().get(slot)))); + } + + ClientboundSetEquipmentPacket setEquipmentPacket = new ClientboundSetEquipmentPacket(npc.getId(), equipmentList); + serverPlayer.connection.send(setEquipmentPacket); + } + + if (npc instanceof ServerPlayer) { + // Enable second layer of skin (https://wiki.vg/Entity_metadata#Player) + npc.getEntityData().set(net.minecraft.world.entity.player.Player.DATA_PLAYER_MODE_CUSTOMISATION, (byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40)); + } + + data.applyAllAttributes(this); + + refreshEntityData(player); + + if (data.isSpawnEntity() && data.getLocation() != null) { + move(player, true); + } + + NpcAttribute playerPoseAttr = FancyNpcsPlugin.get().getAttributeManager().getAttributeByName(org.bukkit.entity.EntityType.PLAYER, "pose"); + if (data.getAttributes().containsKey(playerPoseAttr)) { + String pose = data.getAttributes().get(playerPoseAttr); + + if (pose.equals("sitting")) { + setSitting(serverPlayer); + } else { + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + } + + } + } + + @Override + protected void refreshEntityData(Player player) { + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + Int2ObjectMap> itemsById = (Int2ObjectMap>) ReflectionUtils.getValue(npc.getEntityData(), MappingKeys1_20_1.SYNCHED_ENTITY_DATA__ITEMS_BY_ID.getMapping()); // itemsById + List> entityData = new ArrayList<>(); + for (SynchedEntityData.DataItem dataItem : itemsById.values()) { + entityData.add(dataItem.value()); + } + ClientboundSetEntityDataPacket setEntityDataPacket = new ClientboundSetEntityDataPacket(npc.getId(), entityData); + serverPlayer.connection.send(setEntityDataPacket); + } + + public void move(Player player, boolean swingArm) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setPosRaw(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + npc.setRot(data.getLocation().getYaw(), data.getLocation().getPitch()); + npc.setYHeadRot(data.getLocation().getYaw()); + npc.setXRot(data.getLocation().getPitch()); + npc.setYRot(data.getLocation().getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket(npc); + ReflectionUtils.setValue(teleportEntityPacket, MappingKeys1_20_1.CLIENTBOUND_TELEPORT_ENTITY_PACKET__X.getMapping(), data.getLocation().x()); // 'x' + ReflectionUtils.setValue(teleportEntityPacket, MappingKeys1_20_1.CLIENTBOUND_TELEPORT_ENTITY_PACKET__Y.getMapping(), data.getLocation().y()); // 'y' + ReflectionUtils.setValue(teleportEntityPacket, MappingKeys1_20_1.CLIENTBOUND_TELEPORT_ENTITY_PACKET__Z.getMapping(), data.getLocation().z()); // 'z' + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (data.getLocation().getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + + if (swingArm && npc instanceof ServerPlayer) { + ClientboundAnimatePacket animatePacket = new ClientboundAnimatePacket(npc, 0); + serverPlayer.connection.send(animatePacket); + } + } + + private ClientboundPlayerInfoUpdatePacket.Entry getEntry(ServerPlayer npcPlayer, ServerPlayer viewer) { + GameProfile profile = npcPlayer.getGameProfile(); + if (data.isMirrorSkin() && viewer.getGameProfile().getProperties().containsKey("textures")) { + GameProfile newProfile = new GameProfile(profile.getId(), profile.getName()); + newProfile.getProperties().putAll(viewer.getGameProfile().getProperties()); + profile = newProfile; + } + + return new ClientboundPlayerInfoUpdatePacket.Entry( + npcPlayer.getUUID(), + profile, + data.isShowInTab(), + npcPlayer.latency, + npcPlayer.gameMode.getGameModeForPlayer(), + npcPlayer.getTabListDisplayName(), + Optionull.map(npcPlayer.getChatSession(), RemoteChatSession::asData) + ); + } + + public void setSitting(ServerPlayer serverPlayer) { + if (npc == null) { + return; + } + + if (sittingVehicle == null) { + sittingVehicle = new Display.TextDisplay(EntityType.TEXT_DISPLAY, ((CraftWorld) data.getLocation().getWorld()).getHandle()); + } + + sittingVehicle.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket(sittingVehicle); + serverPlayer.connection.send(addEntityPacket); + + sittingVehicle.passengers = ImmutableList.of(npc); + + ClientboundSetPassengersPacket packet = new ClientboundSetPassengersPacket(sittingVehicle); + serverPlayer.connection.send(packet); + } + + @Override + public float getEyeHeight() { + return npc.getEyeHeight(); + } + + @Override + public int getEntityId() { + return npc.getId(); + } + + public Entity getNpc() { + return npc; + } +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/ReflectionHelper.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/ReflectionHelper.java new file mode 100644 index 00000000..c28b32ff --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/ReflectionHelper.java @@ -0,0 +1,13 @@ +package de.oliver.fancynpcs.v1_20_1; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import net.minecraft.world.entity.Entity; + +public class ReflectionHelper { + + public static T getEntity(Npc npc) { + return (T) ReflectionUtils.getValue(npc, "npc"); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/AgeableMobAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/AgeableMobAttributes.java new file mode 100644 index 00000000..a42a9a56 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/AgeableMobAttributes.java @@ -0,0 +1,38 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.AgeableMob; +import org.bukkit.entity.Ageable; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AgeableMobAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "baby", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Ageable.class.isAssignableFrom(type.getEntityClass())) + .toList(), + AgeableMobAttributes::setBaby + )); + + return attributes; + } + + private static void setBaby(Npc npc, String value) { + AgeableMob mob = ReflectionHelper.getEntity(npc); + + boolean isBaby = Boolean.parseBoolean(value); + + mob.setBaby(isBaby); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/AllayAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/AllayAttributes.java new file mode 100644 index 00000000..3d627e9e --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/AllayAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.animal.allay.Allay; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class AllayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.ALLAY), + AllayAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Allay allay = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + allay.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/ArmorStandAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/ArmorStandAttributes.java new file mode 100644 index 00000000..c432960a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/ArmorStandAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class ArmorStandAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "show_arms", + List.of("true", "false"), + List.of(EntityType.ARMOR_STAND), + ArmorStandAttributes::setShowArms + )); + + return attributes; + } + + private static void setShowArms(Npc npc, String value) { + ArmorStand armorStand = ReflectionHelper.getEntity(npc); + + boolean showArms = Boolean.parseBoolean(value.toLowerCase()); + + armorStand.setShowArms(showArms); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/Attributes_1_20_1.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/Attributes_1_20_1.java new file mode 100644 index 00000000..f028ecbf --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/Attributes_1_20_1.java @@ -0,0 +1,51 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.NpcAttribute; + +import java.util.ArrayList; +import java.util.List; + +public class Attributes_1_20_1 { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.addAll(EntityAttributes.getAllAttributes()); + attributes.addAll(LivingEntityAttributes.getAllAttributes()); + attributes.addAll(AgeableMobAttributes.getAllAttributes()); + attributes.addAll(IllagerAttributes.getAllAttributes()); + attributes.addAll(SpellCasterAttributes.getAllAttributes()); + + attributes.addAll(PlayerAttributes.getAllAttributes()); + attributes.addAll(SheepAttributes.getAllAttributes()); + attributes.addAll(VillagerAttributes.getAllAttributes()); + attributes.addAll(FrogAttributes.getAllAttributes()); + attributes.addAll(HorseAttributes.getAllAttributes()); + attributes.addAll(ParrotAttributes.getAllAttributes()); + attributes.addAll(AxolotlAttributes.getAllAttributes()); + attributes.addAll(TropicalFishAttributes.getAllAttributes()); + attributes.addAll(FoxAttributes.getAllAttributes()); + attributes.addAll(PandaAttributes.getAllAttributes()); + attributes.addAll(GoatAttributes.getAllAttributes()); + attributes.addAll(AllayAttributes.getAllAttributes()); + attributes.addAll(CamelAttributes.getAllAttributes()); + attributes.addAll(RabbitAttributes.getAllAttributes()); + attributes.addAll(PiglinAttributes.getAllAttributes()); + attributes.addAll(CatAttributes.getAllAttributes()); + attributes.addAll(ShulkerAttributes.getAllAttributes()); + attributes.addAll(WolfAttributes.getAllAttributes()); + attributes.addAll(SlimeAttributes.getAllAttributes()); + attributes.addAll(PigAttributes.getAllAttributes()); + attributes.addAll(ArmorStandAttributes.getAllAttributes()); + attributes.addAll(BeeAttributes.getAllAttributes()); + attributes.addAll(VexAttributes.getAllAttributes()); + + attributes.addAll(DisplayAttributes.getAllAttributes()); + attributes.addAll(TextDisplayAttributes.getAllAttributes()); + attributes.addAll(BlockDisplayAttributes.getAllAttributes()); + attributes.addAll(InteractionAttributes.getAllAttributes()); + + return attributes; + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/AxolotlAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/AxolotlAttributes.java new file mode 100644 index 00000000..9af14de3 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/AxolotlAttributes.java @@ -0,0 +1,51 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.animal.axolotl.Axolotl; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AxolotlAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Axolotl.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "playing_dead", + List.of("true", "false"), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setPlayingDead + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + Axolotl.Variant variant = Axolotl.Variant.valueOf(value.toUpperCase()); + axolotl.setVariant(variant); + } + + private static void setPlayingDead(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + boolean playingDead = Boolean.parseBoolean(value); + axolotl.setPlayingDead(playingDead); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/BeeAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/BeeAttributes.java new file mode 100644 index 00000000..a596a070 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/BeeAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.animal.Bee; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BeeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setAngry + )); + + attributes.add(new NpcAttribute( + "sting", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setSting + )); + + attributes.add(new NpcAttribute( + "nectar", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setNectar + )); + + attributes.add(new NpcAttribute( + "rolling", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setRolling + )); + + return attributes; + } + + private static void setAngry(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRemainingPersistentAngerTime(1); + case "false" -> bee.setRemainingPersistentAngerTime(0); + } + } + + private static void setSting(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasStung(false); + case "false" -> bee.setHasStung(true); + } + } + + private static void setNectar(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasNectar(true); + case "false" -> bee.setHasNectar(false); + } + } + + private static void setRolling(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRolling(true); + case "false" -> bee.setRolling(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/BlockDisplayAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/BlockDisplayAttributes.java new file mode 100644 index 00000000..f060f372 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/BlockDisplayAttributes.java @@ -0,0 +1,42 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Display; +import net.minecraft.world.level.block.Block; +import org.bukkit.Material; +import org.bukkit.Registry; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.StreamSupport; + +public class BlockDisplayAttributes { + + private static final List BLOCKS = StreamSupport.stream(Registry.MATERIAL.spliterator(), false).filter(Material::isBlock).map(it -> it.key().value()).toList(); + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "block", + BLOCKS, + List.of(EntityType.BLOCK_DISPLAY), + BlockDisplayAttributes::setBlock + )); + + return attributes; + } + + private static void setBlock(Npc npc, String value) { + Display.BlockDisplay display = ReflectionHelper.getEntity(npc); + + Block block = BuiltInRegistries.BLOCK.get(ResourceLocation.of("minecraft:" + value.toLowerCase(), ':')); + + display.setBlockState(block.defaultBlockState()); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/CamelAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/CamelAttributes.java new file mode 100644 index 00000000..a831536b --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/CamelAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.animal.camel.Camel; +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class CamelAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "dashing"), + List.of(EntityType.CAMEL), + CamelAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Camel camel = ReflectionHelper.getEntity(npc); + + Bukkit.getScheduler().runTask(FancyNpcsPlugin.get().getPlugin(), () -> { + switch (value.toLowerCase()) { + case "standing" -> { + camel.setDashing(false); + camel.standUp(); + } + case "sitting" -> { + camel.setDashing(false); + camel.sitDown(); + } + case "dashing" -> { + camel.standUpInstantly(); + camel.setDashing(true); + } + } + }); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/CatAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/CatAttributes.java new file mode 100644 index 00000000..7b231195 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/CatAttributes.java @@ -0,0 +1,64 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.Cat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class CatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(org.bukkit.entity.Cat.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.CAT), + CatAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.CAT), + CatAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + BuiltInRegistries.CAT_VARIANT.getOptional(ResourceLocation.of(value.toLowerCase(), ':')) + .ifPresent(cat::setVariant); + } + + private static void setPose(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + switch (value.toLowerCase()) { + case "standing" -> { + cat.setInSittingPose(false, false); + cat.setLying(false); + } + case "sleeping" -> { + cat.setInSittingPose(false, false); + cat.setLying(true); + } + case "sitting" -> { + cat.setLying(false); + cat.setOrderedToSit(true); + cat.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/DisplayAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/DisplayAttributes.java new file mode 100644 index 00000000..107313a7 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/DisplayAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class DisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "billboard", + Arrays.stream(org.bukkit.entity.Display.Billboard.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TEXT_DISPLAY, EntityType.BLOCK_DISPLAY, EntityType.ITEM_DISPLAY), + DisplayAttributes::setBillboard + )); + + return attributes; + } + + private static void setBillboard(Npc npc, String value) { + Display display = ReflectionHelper.getEntity(npc); + + Display.BillboardConstraints billboard = Display.BillboardConstraints.valueOf(value.toUpperCase()); + display.setBillboardConstraints(billboard); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/EntityAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/EntityAttributes.java new file mode 100644 index 00000000..3b02e3b8 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/EntityAttributes.java @@ -0,0 +1,103 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.Entity; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class EntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "on_fire", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnFire + )); + + attributes.add(new NpcAttribute( + "invisible", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setInvisible + )); + + attributes.add(new NpcAttribute( + "silent", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setSilent + )); + + attributes.add(new NpcAttribute( + "shaking", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setShaking + )); + + attributes.add(new NpcAttribute( + "on_ground", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnGround + )); + + /*attributes.add(new NpcAttribute( + "entity_pose", + Arrays.stream(Pose.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setEntityPose + ));*/ + + return attributes; + } + + private static void setOnFire(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onFire = Boolean.parseBoolean(value); + + entity.setSharedFlagOnFire(onFire); + + } + + private static void setInvisible(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean invisible = Boolean.parseBoolean(value); + + entity.setInvisible(invisible); + } + + private static void setSilent(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean silent = Boolean.parseBoolean(value); + + entity.setSilent(silent); + } + + private static void setShaking(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean shaking = Boolean.parseBoolean(value); + + entity.setTicksFrozen(shaking ? entity.getTicksRequiredToFreeze() : 0); + } + + private static void setOnGround(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onGround = Boolean.parseBoolean(value); + + entity.setOnGround(onGround); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/FoxAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/FoxAttributes.java new file mode 100644 index 00000000..10b7abae --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/FoxAttributes.java @@ -0,0 +1,66 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.animal.Fox; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FoxAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "type", + Arrays.stream(Fox.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.FOX), + FoxAttributes::setType + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.FOX), + FoxAttributes::setPose + )); + + return attributes; + } + + private static void setType(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + Fox.Type type = Fox.Type.valueOf(value.toUpperCase()); + fox.setVariant(type); + } + + private static void setPose(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + fox.setIsCrouching(false); + fox.setSleeping(false); + fox.setSitting(false, false); + } + case "sleeping" -> { + fox.setSleeping(true); + fox.setSitting(false, false); + fox.setIsCrouching(false); + } + case "sitting" -> { + fox.setSitting(true, false); + fox.setSleeping(false); + fox.setIsCrouching(false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/FrogAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/FrogAttributes.java new file mode 100644 index 00000000..75c20d82 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/FrogAttributes.java @@ -0,0 +1,44 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.animal.FrogVariant; +import net.minecraft.world.entity.animal.frog.Frog; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FrogAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(org.bukkit.entity.Frog.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.FROG), + FrogAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Frog frog = ReflectionHelper.getEntity(npc); + + FrogVariant variant; + switch (value.toUpperCase()) { + case "COLD" -> variant = FrogVariant.COLD; + case "WARM" -> variant = FrogVariant.WARM; + default -> variant = FrogVariant.TEMPERATE; + } + + frog.setVariant(variant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/GoatAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/GoatAttributes.java new file mode 100644 index 00000000..c84ef02a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/GoatAttributes.java @@ -0,0 +1,44 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.animal.goat.Goat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class GoatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "horns", + List.of("none", "left", "right", "both"), + List.of(EntityType.GOAT), + GoatAttributes::setHorns + )); + + return attributes; + } + + private static void setHorns(Npc npc, String value) { + Goat goat = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "none" -> goat.removeHorns(); + case "both" -> goat.addHorns(); + case "left" -> { + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, false); + } + case "right" -> { + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/HorseAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/HorseAttributes.java new file mode 100644 index 00000000..245a0f9a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/HorseAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.animal.horse.Horse; +import net.minecraft.world.entity.animal.horse.Markings; +import net.minecraft.world.entity.animal.horse.Variant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class HorseAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "markings", + Arrays.stream(Markings.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setMarkings + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("eating", "rearing", "standing"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && (type == EntityType.HORSE || type == EntityType.DONKEY || + type == EntityType.MULE || type == EntityType.SKELETON_HORSE ||type == EntityType.ZOMBIE_HORSE)) + .toList(), + HorseAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Variant variant = Variant.valueOf(value.toUpperCase()); + horse.setVariant(variant); + } + + private static void setMarkings(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Markings markings = Markings.valueOf(value.toUpperCase()); + horse.setVariantAndMarkings(horse.getVariant(), markings); + } + + private static void setPose(Npc npc, String value) { + net.minecraft.world.entity.animal.horse.AbstractHorse horse = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + horse.setEating(false); + horse.setForceStanding(false); + } + case "rearing" -> { + horse.setForceStanding(true); + horse.setEating(false); + } + case "eating" -> { + horse.setForceStanding(false); + horse.setEating(true); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/IllagerAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/IllagerAttributes.java new file mode 100644 index 00000000..872e03a1 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/IllagerAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.raid.Raider; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Illager; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class IllagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "celebrating", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Illager.class.isAssignableFrom(type.getEntityClass())) + .toList(), + IllagerAttributes::setCelebrating + )); + + return attributes; + } + + private static void setCelebrating(Npc npc, String value) { + Raider raider = ReflectionHelper.getEntity(npc); + + boolean isCelebrating = Boolean.parseBoolean(value); + + raider.setCelebrating(isCelebrating); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/InteractionAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/InteractionAttributes.java new file mode 100644 index 00000000..a6d6a18a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/InteractionAttributes.java @@ -0,0 +1,60 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.Interaction; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class InteractionAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "height", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setHeight + )); + + attributes.add(new NpcAttribute( + "width", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setWidth + )); + + return attributes; + } + + private static void setHeight(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float height; + try { + height = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setHeight(height); + } + + private static void setWidth(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float width; + try { + width = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setWidth(width); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/LivingEntityAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/LivingEntityAttributes.java new file mode 100644 index 00000000..7918f7bb --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/LivingEntityAttributes.java @@ -0,0 +1,67 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.InteractionHand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class LivingEntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + /*attributes.add(new NpcAttribute( + "hurt", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setHurt + ));*/ + + attributes.add(new NpcAttribute( + "use_item", + List.of("main_hand", "off_hand", "none"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setUseItem + )); + + return attributes; + } + + private static void setHurt(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + boolean isHurt = Boolean.parseBoolean(value); + + if (isHurt) { + livingEntity.hurtDuration = 1; + livingEntity.hurtTime = 1; + livingEntity.hurtMarked = true; + livingEntity.animateHurt(0); + } else { + livingEntity.hurtDuration = 0; + livingEntity.hurtTime = 0; + livingEntity.hurtMarked = false; + } + } + + private static void setUseItem(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + switch (value.toUpperCase()) { + case "NONE" -> livingEntity.stopUsingItem(); + case "MAIN_HAND" -> livingEntity.startUsingItem(InteractionHand.MAIN_HAND, true); + case "OFF_HAND" -> livingEntity.startUsingItem(InteractionHand.OFF_HAND, true); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/PandaAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/PandaAttributes.java new file mode 100644 index 00000000..a4a69b7d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/PandaAttributes.java @@ -0,0 +1,102 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.MappingKeys1_20_1; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.animal.Panda; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PandaAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "gene", + Arrays.stream(Panda.Gene.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PANDA), + PandaAttributes::setGene + )); + + attributes.add(new NpcAttribute( + "eating", + List.of("true", "false"), + List.of(EntityType.PANDA), + PandaAttributes::setEating + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "onBack", "rolling"), + List.of(EntityType.PANDA), + PandaAttributes::setPose + )); + + return attributes; + } + + private static void setGene(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + Panda.Gene gene = Panda.Gene.valueOf(value.toUpperCase()); + panda.setMainGene(gene); + } + + private static void setPose(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(false); + } + case "sitting" -> { + panda.roll(false); + panda.setOnBack(false); + setFlag(panda, 8, true); //sitting + } + case "onback" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(true); + } + case "rolling" -> { + setFlag(panda, 8, false); //sitting + panda.setOnBack(false); + panda.roll(true); + } + } + } + + private static void setEating(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + boolean eating = Boolean.parseBoolean(value); + + panda.eat(eating); + } + + private static void setFlag(Panda panda, int mask, boolean value) { + EntityDataAccessor DATA_ID_FLAGS = (EntityDataAccessor) ReflectionUtils.getValue(panda, MappingKeys1_20_1.PANDA__DATA_ID_FLAGS.getMapping()); + + byte b0 = panda.getEntityData().get(DATA_ID_FLAGS); + + if (value) { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 | mask)); + } else { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 & ~mask)); + } + + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/ParrotAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/ParrotAttributes.java new file mode 100644 index 00000000..8408dbfb --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/ParrotAttributes.java @@ -0,0 +1,59 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.animal.Parrot; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ParrotAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Parrot.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PARROT), + ParrotAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.PARROT), + ParrotAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + Parrot.Variant variant = Parrot.Variant.valueOf(value.toUpperCase()); + parrot.setVariant(variant); + } + + private static void setPose(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + parrot.setOrderedToSit(false); + parrot.setInSittingPose(false, false); + } + case "sitting" -> { + parrot.setOrderedToSit(true); + parrot.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/PigAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/PigAttributes.java new file mode 100644 index 00000000..0c561e2c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/PigAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.animal.Pig; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PigAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "has_saddle", + List.of("true", "false"), + List.of(EntityType.PIG), + PigAttributes::setHasSaddle + )); + + return attributes; + } + + private static void setHasSaddle(Npc npc, String value) { + Pig pig = ReflectionHelper.getEntity(npc); + + boolean hasSaddle = Boolean.parseBoolean(value.toLowerCase()); + + pig.steering.setSaddle(hasSaddle); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/PiglinAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/PiglinAttributes.java new file mode 100644 index 00000000..cd42dc2b --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/PiglinAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.monster.piglin.Piglin; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PiglinAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.PIGLIN), + PiglinAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Piglin piglin = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + piglin.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/PlayerAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/PlayerAttributes.java new file mode 100644 index 00000000..ff055d39 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/PlayerAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.entity.player.Player; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PlayerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "crouching", "sleeping", "swimming", "sitting"), + List.of(EntityType.PLAYER), + PlayerAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Player player = ReflectionHelper.getEntity(npc); + + Pose pose = Pose.valueOf(value.toUpperCase()); + + EntityDataAccessor DATA_POSE = (EntityDataAccessor) ReflectionUtils.getStaticValue(Entity.class, "ar"); // DATA_POSE + player.getEntityData().set(DATA_POSE, pose); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/RabbitAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/RabbitAttributes.java new file mode 100644 index 00000000..cd1a9957 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/RabbitAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.animal.Rabbit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class RabbitAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Rabbit.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.RABBIT), + RabbitAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Rabbit rabbit = ReflectionHelper.getEntity(npc); + + Rabbit.Variant variant = Rabbit.Variant.valueOf(value.toUpperCase()); + rabbit.setVariant(variant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/SheepAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/SheepAttributes.java new file mode 100644 index 00000000..6c22c907 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/SheepAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.animal.Sheep; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SheepAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "wool_color", + Arrays.stream(DyeColor.values()).map(dyeColor -> dyeColor.name().toLowerCase()).toList(), + List.of(EntityType.SHEEP), + SheepAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "sheared", + Arrays.asList("true", "false"), + List.of(EntityType.SHEEP), + SheepAttributes::setSheared + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + sheep.setColor(DyeColor.byName(value.toLowerCase(), DyeColor.WHITE)); + } + + private static void setSheared(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + boolean sheared = Boolean.parseBoolean(value); + + sheep.setSheared(sheared); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/ShulkerAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/ShulkerAttributes.java new file mode 100644 index 00000000..42cae529 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/ShulkerAttributes.java @@ -0,0 +1,55 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class ShulkerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.SHULKER), + ShulkerAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "shield", + List.of("open", "closed"), + List.of(EntityType.SHULKER), + ShulkerAttributes::setShield + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.PURPLE); + shulker.setVariant(Optional.of(color)); + } + + private static void setShield(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "closed" -> shulker.setRawPeekAmount(0); + case "open" -> shulker.setRawPeekAmount(Byte.MAX_VALUE); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/SlimeAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/SlimeAttributes.java new file mode 100644 index 00000000..bcd54ccc --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/SlimeAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.monster.Slime; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class SlimeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "size", + new ArrayList<>(), + List.of(EntityType.SLIME), + SlimeAttributes::setSize + )); + + return attributes; + } + + private static void setSize(Npc npc, String value) { + Slime slime = ReflectionHelper.getEntity(npc); + + int size; + try { + size = Integer.parseInt(value); + } catch (NumberFormatException e) { + return; + } + + slime.setSize(size, false); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/SpellCasterAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/SpellCasterAttributes.java new file mode 100644 index 00000000..72766ebd --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/SpellCasterAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.monster.SpellcasterIllager; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Spellcaster; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SpellCasterAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "casting", + Arrays.stream(SpellcasterIllager.IllagerSpell.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Spellcaster.class.isAssignableFrom(type.getEntityClass())) + .toList(), + SpellCasterAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + SpellcasterIllager spellcasterIllager = ReflectionHelper.getEntity(npc); + + SpellcasterIllager.IllagerSpell spell = SpellcasterIllager.IllagerSpell.valueOf(value); + + spellcasterIllager.setIsCastingSpell(spell); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/TextDisplayAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/TextDisplayAttributes.java new file mode 100644 index 00000000..64cf1932 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/TextDisplayAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import io.papermc.paper.adventure.PaperAdventure; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class TextDisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "text", + new ArrayList<>(), + List.of(EntityType.TEXT_DISPLAY), + TextDisplayAttributes::setText + )); + + return attributes; + } + + private static void setText(Npc npc, String value) { + Display.TextDisplay display = ReflectionHelper.getEntity(npc); + + Component text = MiniMessage.miniMessage().deserialize(value); + display.setText(PaperAdventure.asVanilla(text)); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/TropicalFishAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/TropicalFishAttributes.java new file mode 100644 index 00000000..b9e05a2c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/TropicalFishAttributes.java @@ -0,0 +1,72 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.animal.TropicalFish; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TropicalFishAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pattern", + Arrays.stream(TropicalFish.Pattern.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPattern + )); + + attributes.add(new NpcAttribute( + "base_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setBaseColor + )); + + attributes.add(new NpcAttribute( + "pattern_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPatternColor + )); + + return attributes; + } + + private static void setPattern(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + TropicalFish.Pattern pattern = TropicalFish.Pattern.valueOf(value.toUpperCase()); + tropicalFish.setVariant(pattern); + } + + private static void setBaseColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), color, tropicalFish.getPatternColor()); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + + private static void setPatternColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), tropicalFish.getBaseColor(), color); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/VexAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/VexAttributes.java new file mode 100644 index 00000000..ebab43db --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/VexAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.monster.Vex; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class VexAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "charging", + List.of("true", "false"), + List.of(EntityType.VEX), + VexAttributes::setCharging + )); + + return attributes; + } + + private static void setCharging(Npc npc, String value) { + Vex vex = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> vex.setIsCharging(true); + case "false" -> vex.setIsCharging(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/VillagerAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/VillagerAttributes.java new file mode 100644 index 00000000..bae9fa91 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/VillagerAttributes.java @@ -0,0 +1,86 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.entity.npc.VillagerType; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class VillagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "profession", + Arrays.stream(org.bukkit.entity.Villager.Profession.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setProfession + )); + + attributes.add(new NpcAttribute( + "type", + Arrays.stream(org.bukkit.entity.Villager.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setType + )); + + return attributes; + } + + private static void setProfession(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerProfession profession; + switch (value.toUpperCase()) { + case "ARMORER" -> profession = VillagerProfession.ARMORER; + case "BUTCHER" -> profession = VillagerProfession.BUTCHER; + case "CARTOGRAPHER" -> profession = VillagerProfession.CARTOGRAPHER; + case "CLERIC" -> profession = VillagerProfession.CLERIC; + case "FARMER" -> profession = VillagerProfession.FARMER; + case "FISHERMAN" -> profession = VillagerProfession.FISHERMAN; + case "FLETCHER" -> profession = VillagerProfession.FLETCHER; + case "LEATHERWORKER" -> profession = VillagerProfession.LEATHERWORKER; + case "LIBRARIAN" -> profession = VillagerProfession.LIBRARIAN; + case "MASON" -> profession = VillagerProfession.MASON; + case "NITWIT" -> profession = VillagerProfession.NITWIT; + case "SHEPHERD" -> profession = VillagerProfession.SHEPHERD; + case "TOOLSMITH" -> profession = VillagerProfession.TOOLSMITH; + case "WEAPONSMITH" -> profession = VillagerProfession.WEAPONSMITH; + + default -> profession = VillagerProfession.NONE; + } + + villager.setVillagerData(villager.getVillagerData().setProfession(profession)); + } + + private static void setType(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerType type; + switch (value.toUpperCase()) { + case "DESERT" -> type = VillagerType.DESERT; + case "JUNGLE" -> type = VillagerType.JUNGLE; + case "SAVANNA" -> type = VillagerType.SAVANNA; + case "SNOW" -> type = VillagerType.SNOW; + case "SWAMP" -> type = VillagerType.SWAMP; + case "TAIGA" -> type = VillagerType.TAIGA; + + default -> type = VillagerType.PLAINS; + } + + + villager.setVillagerData(villager.getVillagerData().setType(type)); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/WolfAttributes.java b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/WolfAttributes.java new file mode 100644 index 00000000..4f76761c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/attributes/WolfAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_20_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_1.ReflectionHelper; +import net.minecraft.world.entity.animal.Wolf; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class WolfAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.WOLF), + WolfAttributes::setPose + )); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.WOLF), + WolfAttributes::setAngry + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> wolf.setInSittingPose(false, false); + case "sitting" -> wolf.setInSittingPose(true, false); + } + } + + private static void setAngry(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + boolean angry = Boolean.parseBoolean(value.toLowerCase()); + + wolf.setRemainingPersistentAngerTime(angry ? 100 : 0); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_2/build.gradle.kts b/plugins/fancynpcs/implementation_1_20_2/build.gradle.kts new file mode 100644 index 00000000..c79b9935 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/build.gradle.kts @@ -0,0 +1,31 @@ +plugins { + id("java-library") + id("io.papermc.paperweight.userdev") +} + +val minecraftVersion = "1.20.2" + +dependencies { + paperweight.paperDevBundle("$minecraftVersion-R0.1-SNAPSHOT") + + compileOnly(project(":plugins:fancynpcs:api")) + compileOnly("de.oliver:FancyLib:37") + compileOnly("org.lushplugins:ChatColorHandler:5.1.3") +} + + +tasks { + named("assemble") { + dependsOn(named("reobfJar")) + } + + javadoc { + options.encoding = Charsets.UTF_8.name() + } + + compileJava { + options.encoding = Charsets.UTF_8.name() + options.release = 17 + + } +} \ No newline at end of file diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/MappingKeys1_20_2.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/MappingKeys1_20_2.java new file mode 100644 index 00000000..2bc99e76 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/MappingKeys1_20_2.java @@ -0,0 +1,22 @@ +package de.oliver.fancynpcs.v1_20_2; + +public enum MappingKeys1_20_2 { + + ENTITY_TYPE__FACTORY("bA"), + SYNCHED_ENTITY_DATA__ITEMS_BY_ID("e"), + CLIENTBOUND_TELEPORT_ENTITY_PACKET__X("b"), + CLIENTBOUND_TELEPORT_ENTITY_PACKET__Y("c"), + CLIENTBOUND_TELEPORT_ENTITY_PACKET__Z("d"), + PANDA__DATA_ID_FLAGS("cb"), + ; + + private final String mapping; + + MappingKeys1_20_2(String mapping) { + this.mapping = mapping; + } + + public String getMapping() { + return mapping; + } +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/Npc_1_20_2.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/Npc_1_20_2.java new file mode 100644 index 00000000..08ae9d34 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/Npc_1_20_2.java @@ -0,0 +1,386 @@ +package de.oliver.fancynpcs.v1_20_2; + +import com.google.common.collect.ImmutableList; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.datafixers.util.Pair; +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.events.NpcSpawnEvent; +import de.oliver.fancynpcs.api.utils.NpcEquipmentSlot; +import io.papermc.paper.adventure.PaperAdventure; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.minecraft.Optionull; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.RemoteChatSession; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ClientInformation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; +import net.minecraft.world.entity.Display; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.scores.PlayerTeam; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.scores.Team; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_20_R2.CraftServer; +import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_20_R2.util.CraftNamespacedKey; +import org.bukkit.entity.Player; +import org.lushplugins.chatcolorhandler.ModernChatColorHandler; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class Npc_1_20_2 extends Npc { + + private final String localName; + private final UUID uuid; + private Entity npc; + private Display.TextDisplay sittingVehicle; + + public Npc_1_20_2(NpcData data) { + super(data); + + this.localName = generateLocalName(); + this.uuid = UUID.randomUUID(); + } + + @Override + public void create() { + MinecraftServer minecraftServer = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel serverLevel = ((CraftWorld) data.getLocation().getWorld()).getHandle(); + GameProfile gameProfile = new GameProfile(uuid, localName); + + if (data.getType() == org.bukkit.entity.EntityType.PLAYER) { + npc = new ServerPlayer(minecraftServer, serverLevel, new GameProfile(uuid, ""), ClientInformation.createDefault()); + ((ServerPlayer) npc).gameProfile = gameProfile; + } else { + EntityType nmsType = BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(data.getType().getKey())); + EntityType.EntityFactory factory = (EntityType.EntityFactory) ReflectionUtils.getValue(nmsType, MappingKeys1_20_2.ENTITY_TYPE__FACTORY.getMapping()); // EntityType.factory + npc = factory.create(nmsType, serverLevel); + isTeamCreated.clear(); + } + } + + @Override + public void spawn(Player player) { + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc == null) { + return; + } + + if (!data.getLocation().getWorld().getName().equalsIgnoreCase(serverPlayer.level().getWorld().getName())) { + return; + } + + if (data.getSkinData() != null && data.getSkinData().hasTexture()) { + String value = data.getSkinData().getTextureValue(); + String signature = data.getSkinData().getTextureSignature(); + + ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues( + "textures", + ImmutableList.of(new Property("textures", value, signature)) + ); + } + + NpcSpawnEvent spawnEvent = new NpcSpawnEvent(this, player); + spawnEvent.callEvent(); + if (spawnEvent.isCancelled()) { + return; + } + + + if (npc instanceof ServerPlayer npcPlayer) { + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, getEntry(npcPlayer, serverPlayer)); + serverPlayer.connection.send(playerInfoPacket); + + if (data.isSpawnEntity()) { + npc.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + } + } + + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket(npc); + serverPlayer.connection.send(addEntityPacket); + + isVisibleForPlayer.put(player.getUniqueId(), true); + + int removeNpcsFromPlayerlistDelay = FancyNpcsPlugin.get().getFancyNpcConfig().getRemoveNpcsFromPlayerlistDelay(); + if (!data.isShowInTab() && removeNpcsFromPlayerlistDelay > 0) { + FancyNpcsPlugin.get().getNpcThread().schedule(() -> { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of(npc.getUUID())); + serverPlayer.connection.send(playerInfoRemovePacket); + }, removeNpcsFromPlayerlistDelay, TimeUnit.MILLISECONDS); + } + + update(player); + } + + @Override + public void remove(Player player) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc instanceof ServerPlayer npcPlayer) { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of((npcPlayer.getUUID()))); + serverPlayer.connection.send(playerInfoRemovePacket); + } + + // remove entity + ClientboundRemoveEntitiesPacket removeEntitiesPacket = new ClientboundRemoveEntitiesPacket(npc.getId()); + serverPlayer.connection.send(removeEntitiesPacket); + + // remove sitting vehicle + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + + isVisibleForPlayer.put(serverPlayer.getUUID(), false); + } + + @Override + public void lookAt(Player player, Location location) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setRot(location.getYaw(), location.getPitch()); + npc.setYHeadRot(location.getYaw()); + npc.setXRot(location.getPitch()); + npc.setYRot(location.getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket(npc); + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (location.getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + } + + @Override + public void update(Player player) { + if (npc == null) { + return; + } + + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + PlayerTeam team = new PlayerTeam(new Scoreboard(), "npc-" + localName); + team.getPlayers().clear(); + team.getPlayers().add(npc instanceof ServerPlayer npcPlayer ? npcPlayer.getGameProfile().getName() : npc.getStringUUID()); + team.setColor(PaperAdventure.asVanilla(data.getGlowingColor())); + if (!data.isCollidable()) { + team.setCollisionRule(Team.CollisionRule.NEVER); + } + + net.kyori.adventure.text.Component displayName = ModernChatColorHandler.translate(data.getDisplayName(), serverPlayer.getBukkitEntity()); + Component vanillaComponent = PaperAdventure.asVanilla(displayName); + if (!(npc instanceof ServerPlayer)) { + npc.setCustomName(vanillaComponent); + npc.setCustomNameVisible(true); + } else { + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } + + if (data.getDisplayName().equalsIgnoreCase("")) { + team.setNameTagVisibility(Team.Visibility.NEVER); + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } else { + team.setNameTagVisibility(Team.Visibility.ALWAYS); + } + + if (npc instanceof ServerPlayer npcPlayer) { + team.setPlayerPrefix(vanillaComponent); + npcPlayer.listName = vanillaComponent; + + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, getEntry(npcPlayer, serverPlayer)); + serverPlayer.connection.send(playerInfoPacket); + } + + boolean isTeamCreatedForPlayer = this.isTeamCreated.getOrDefault(player.getUniqueId(), false); + serverPlayer.connection.send(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, !isTeamCreatedForPlayer)); + isTeamCreated.put(player.getUniqueId(), true); + + npc.setGlowingTag(data.isGlowing()); + + if (data.getEquipment() != null && data.getEquipment().size() > 0) { + List> equipmentList = new ArrayList<>(); + + for (NpcEquipmentSlot slot : data.getEquipment().keySet()) { + equipmentList.add(new Pair<>(EquipmentSlot.byName(slot.toNmsName()), CraftItemStack.asNMSCopy(data.getEquipment().get(slot)))); + } + + ClientboundSetEquipmentPacket setEquipmentPacket = new ClientboundSetEquipmentPacket(npc.getId(), equipmentList); + serverPlayer.connection.send(setEquipmentPacket); + } + + if (npc instanceof ServerPlayer) { + // Enable second layer of skin (https://wiki.vg/Entity_metadata#Player) + npc.getEntityData().set(net.minecraft.world.entity.player.Player.DATA_PLAYER_MODE_CUSTOMISATION, (byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40)); + } + + data.applyAllAttributes(this); + + refreshEntityData(player); + + if (data.isSpawnEntity() && data.getLocation() != null) { + move(player, true); + } + + NpcAttribute playerPoseAttr = FancyNpcsPlugin.get().getAttributeManager().getAttributeByName(org.bukkit.entity.EntityType.PLAYER, "pose"); + if (data.getAttributes().containsKey(playerPoseAttr)) { + String pose = data.getAttributes().get(playerPoseAttr); + + if (pose.equals("sitting")) { + setSitting(serverPlayer); + } else { + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + } + + } + } + + @Override + protected void refreshEntityData(Player player) { + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + Int2ObjectMap> itemsById = (Int2ObjectMap>) ReflectionUtils.getValue(npc.getEntityData(), MappingKeys1_20_2.SYNCHED_ENTITY_DATA__ITEMS_BY_ID.getMapping()); // itemsById + List> entityData = new ArrayList<>(); + for (SynchedEntityData.DataItem dataItem : itemsById.values()) { + entityData.add(dataItem.value()); + } + ClientboundSetEntityDataPacket setEntityDataPacket = new ClientboundSetEntityDataPacket(npc.getId(), entityData); + serverPlayer.connection.send(setEntityDataPacket); + } + + public void move(Player player, boolean swingArm) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setPosRaw(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + npc.setRot(data.getLocation().getYaw(), data.getLocation().getPitch()); + npc.setYHeadRot(data.getLocation().getYaw()); + npc.setXRot(data.getLocation().getPitch()); + npc.setYRot(data.getLocation().getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket(npc); + ReflectionUtils.setValue(teleportEntityPacket, MappingKeys1_20_2.CLIENTBOUND_TELEPORT_ENTITY_PACKET__X.getMapping(), data.getLocation().x()); // 'x' + ReflectionUtils.setValue(teleportEntityPacket, MappingKeys1_20_2.CLIENTBOUND_TELEPORT_ENTITY_PACKET__Y.getMapping(), data.getLocation().y()); // 'y' + ReflectionUtils.setValue(teleportEntityPacket, MappingKeys1_20_2.CLIENTBOUND_TELEPORT_ENTITY_PACKET__Z.getMapping(), data.getLocation().z()); // 'z' + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (data.getLocation().getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + + if (swingArm && npc instanceof ServerPlayer) { + ClientboundAnimatePacket animatePacket = new ClientboundAnimatePacket(npc, 0); + serverPlayer.connection.send(animatePacket); + } + } + + private ClientboundPlayerInfoUpdatePacket.Entry getEntry(ServerPlayer npcPlayer, ServerPlayer viewer) { + GameProfile profile = npcPlayer.getGameProfile(); + if (data.isMirrorSkin() && ServerLoginPacketListenerImpl.isValidUsername(viewer.getGameProfile().getName())) { + GameProfile newProfile = new GameProfile(profile.getId(), profile.getName()); + newProfile.getProperties().putAll(viewer.getGameProfile().getProperties()); + profile = newProfile; + } + + return new ClientboundPlayerInfoUpdatePacket.Entry( + npcPlayer.getUUID(), + profile, + data.isShowInTab(), + 69, + npcPlayer.gameMode.getGameModeForPlayer(), + npcPlayer.getTabListDisplayName(), + Optionull.map(npcPlayer.getChatSession(), RemoteChatSession::asData) + ); + } + + public void setSitting(ServerPlayer serverPlayer) { + if (npc == null) { + return; + } + + if (sittingVehicle == null) { + sittingVehicle = new Display.TextDisplay(EntityType.TEXT_DISPLAY, ((CraftWorld) data.getLocation().getWorld()).getHandle()); + } + + sittingVehicle.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket(sittingVehicle); + serverPlayer.connection.send(addEntityPacket); + + sittingVehicle.passengers = ImmutableList.of(npc); + + ClientboundSetPassengersPacket packet = new ClientboundSetPassengersPacket(sittingVehicle); + serverPlayer.connection.send(packet); + } + + @Override + public float getEyeHeight() { + return npc.getEyeHeight(); + } + + @Override + public int getEntityId() { + return npc.getId(); + } + + public Entity getNpc() { + return npc; + } +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/ReflectionHelper.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/ReflectionHelper.java new file mode 100644 index 00000000..cd0acc2d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/ReflectionHelper.java @@ -0,0 +1,13 @@ +package de.oliver.fancynpcs.v1_20_2; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import net.minecraft.world.entity.Entity; + +public class ReflectionHelper { + + public static T getEntity(Npc npc) { + return (T) ReflectionUtils.getValue(npc, "npc"); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/AgeableMobAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/AgeableMobAttributes.java new file mode 100644 index 00000000..36f44cad --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/AgeableMobAttributes.java @@ -0,0 +1,38 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.AgeableMob; +import org.bukkit.entity.Ageable; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AgeableMobAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "baby", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Ageable.class.isAssignableFrom(type.getEntityClass())) + .toList(), + AgeableMobAttributes::setBaby + )); + + return attributes; + } + + private static void setBaby(Npc npc, String value) { + AgeableMob mob = ReflectionHelper.getEntity(npc); + + boolean isBaby = Boolean.parseBoolean(value); + + mob.setBaby(isBaby); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/AllayAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/AllayAttributes.java new file mode 100644 index 00000000..bc9759b9 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/AllayAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.animal.allay.Allay; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class AllayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.ALLAY), + AllayAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Allay allay = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + allay.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/ArmorStandAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/ArmorStandAttributes.java new file mode 100644 index 00000000..e9d3884e --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/ArmorStandAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class ArmorStandAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "show_arms", + List.of("true", "false"), + List.of(EntityType.ARMOR_STAND), + ArmorStandAttributes::setShowArms + )); + + return attributes; + } + + private static void setShowArms(Npc npc, String value) { + ArmorStand armorStand = ReflectionHelper.getEntity(npc); + + boolean showArms = Boolean.parseBoolean(value.toLowerCase()); + + armorStand.setShowArms(showArms); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/Attributes_1_20_2.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/Attributes_1_20_2.java new file mode 100644 index 00000000..77433ed8 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/Attributes_1_20_2.java @@ -0,0 +1,51 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.NpcAttribute; + +import java.util.ArrayList; +import java.util.List; + +public class Attributes_1_20_2 { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.addAll(EntityAttributes.getAllAttributes()); + attributes.addAll(LivingEntityAttributes.getAllAttributes()); + attributes.addAll(AgeableMobAttributes.getAllAttributes()); + attributes.addAll(IllagerAttributes.getAllAttributes()); + attributes.addAll(SpellCasterAttributes.getAllAttributes()); + + attributes.addAll(PlayerAttributes.getAllAttributes()); + attributes.addAll(SheepAttributes.getAllAttributes()); + attributes.addAll(VillagerAttributes.getAllAttributes()); + attributes.addAll(FrogAttributes.getAllAttributes()); + attributes.addAll(HorseAttributes.getAllAttributes()); + attributes.addAll(ParrotAttributes.getAllAttributes()); + attributes.addAll(AxolotlAttributes.getAllAttributes()); + attributes.addAll(TropicalFishAttributes.getAllAttributes()); + attributes.addAll(FoxAttributes.getAllAttributes()); + attributes.addAll(PandaAttributes.getAllAttributes()); + attributes.addAll(GoatAttributes.getAllAttributes()); + attributes.addAll(AllayAttributes.getAllAttributes()); + attributes.addAll(CamelAttributes.getAllAttributes()); + attributes.addAll(RabbitAttributes.getAllAttributes()); + attributes.addAll(PiglinAttributes.getAllAttributes()); + attributes.addAll(CatAttributes.getAllAttributes()); + attributes.addAll(ShulkerAttributes.getAllAttributes()); + attributes.addAll(WolfAttributes.getAllAttributes()); + attributes.addAll(SlimeAttributes.getAllAttributes()); + attributes.addAll(PigAttributes.getAllAttributes()); + attributes.addAll(ArmorStandAttributes.getAllAttributes()); + attributes.addAll(BeeAttributes.getAllAttributes()); + attributes.addAll(VexAttributes.getAllAttributes()); + + attributes.addAll(DisplayAttributes.getAllAttributes()); + attributes.addAll(TextDisplayAttributes.getAllAttributes()); + attributes.addAll(BlockDisplayAttributes.getAllAttributes()); + attributes.addAll(InteractionAttributes.getAllAttributes()); + + return attributes; + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/AxolotlAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/AxolotlAttributes.java new file mode 100644 index 00000000..69f52d41 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/AxolotlAttributes.java @@ -0,0 +1,51 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.animal.axolotl.Axolotl; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AxolotlAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Axolotl.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "playing_dead", + List.of("true", "false"), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setPlayingDead + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + Axolotl.Variant variant = Axolotl.Variant.valueOf(value.toUpperCase()); + axolotl.setVariant(variant); + } + + private static void setPlayingDead(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + boolean playingDead = Boolean.parseBoolean(value); + axolotl.setPlayingDead(playingDead); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/BeeAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/BeeAttributes.java new file mode 100644 index 00000000..1c574ce5 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/BeeAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.animal.Bee; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BeeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setAngry + )); + + attributes.add(new NpcAttribute( + "sting", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setSting + )); + + attributes.add(new NpcAttribute( + "nectar", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setNectar + )); + + attributes.add(new NpcAttribute( + "rolling", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setRolling + )); + + return attributes; + } + + private static void setAngry(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRemainingPersistentAngerTime(1); + case "false" -> bee.setRemainingPersistentAngerTime(0); + } + } + + private static void setSting(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasStung(false); + case "false" -> bee.setHasStung(true); + } + } + + private static void setNectar(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasNectar(true); + case "false" -> bee.setHasNectar(false); + } + } + + private static void setRolling(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRolling(true); + case "false" -> bee.setRolling(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/BlockDisplayAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/BlockDisplayAttributes.java new file mode 100644 index 00000000..69958375 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/BlockDisplayAttributes.java @@ -0,0 +1,41 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Display; +import net.minecraft.world.level.block.Block; +import org.bukkit.Material; +import org.bukkit.Registry; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BlockDisplayAttributes { + + private static final List BLOCKS = Registry.MATERIAL.stream().filter(Material::isBlock).map(it -> it.key().value()).toList(); + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "block", + BLOCKS, + List.of(EntityType.BLOCK_DISPLAY), + BlockDisplayAttributes::setBlock + )); + + return attributes; + } + + private static void setBlock(Npc npc, String value) { + Display.BlockDisplay display = ReflectionHelper.getEntity(npc); + + Block block = BuiltInRegistries.BLOCK.get(ResourceLocation.of("minecraft:" + value.toLowerCase(), ':')); + + display.setBlockState(block.defaultBlockState()); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/CamelAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/CamelAttributes.java new file mode 100644 index 00000000..43fde4d9 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/CamelAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.animal.camel.Camel; +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class CamelAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "dashing"), + List.of(EntityType.CAMEL), + CamelAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Camel camel = ReflectionHelper.getEntity(npc); + + Bukkit.getScheduler().runTask(FancyNpcsPlugin.get().getPlugin(), () -> { + switch (value.toLowerCase()) { + case "standing" -> { + camel.setDashing(false); + camel.standUp(); + } + case "sitting" -> { + camel.setDashing(false); + camel.sitDown(); + } + case "dashing" -> { + camel.standUpInstantly(); + camel.setDashing(true); + } + } + }); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/CatAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/CatAttributes.java new file mode 100644 index 00000000..0afa3825 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/CatAttributes.java @@ -0,0 +1,64 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.Cat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class CatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(org.bukkit.entity.Cat.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.CAT), + CatAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.CAT), + CatAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + BuiltInRegistries.CAT_VARIANT.getOptional(ResourceLocation.of(value.toLowerCase(), ':')) + .ifPresent(cat::setVariant); + } + + private static void setPose(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + switch (value.toLowerCase()) { + case "standing" -> { + cat.setInSittingPose(false, false); + cat.setLying(false); + } + case "sleeping" -> { + cat.setInSittingPose(false, false); + cat.setLying(true); + } + case "sitting" -> { + cat.setLying(false); + cat.setOrderedToSit(true); + cat.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/DisplayAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/DisplayAttributes.java new file mode 100644 index 00000000..e4131203 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/DisplayAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class DisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "billboard", + Arrays.stream(org.bukkit.entity.Display.Billboard.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TEXT_DISPLAY, EntityType.BLOCK_DISPLAY, EntityType.ITEM_DISPLAY), + DisplayAttributes::setBillboard + )); + + return attributes; + } + + private static void setBillboard(Npc npc, String value) { + Display display = ReflectionHelper.getEntity(npc); + + Display.BillboardConstraints billboard = Display.BillboardConstraints.valueOf(value.toUpperCase()); + display.setBillboardConstraints(billboard); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/EntityAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/EntityAttributes.java new file mode 100644 index 00000000..292aa232 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/EntityAttributes.java @@ -0,0 +1,103 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.Entity; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class EntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "on_fire", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnFire + )); + + attributes.add(new NpcAttribute( + "invisible", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setInvisible + )); + + attributes.add(new NpcAttribute( + "silent", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setSilent + )); + + attributes.add(new NpcAttribute( + "shaking", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setShaking + )); + + attributes.add(new NpcAttribute( + "on_ground", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnGround + )); + + /*attributes.add(new NpcAttribute( + "entity_pose", + Arrays.stream(Pose.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setEntityPose + ));*/ + + return attributes; + } + + private static void setOnFire(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onFire = Boolean.parseBoolean(value); + + entity.setSharedFlagOnFire(onFire); + + } + + private static void setInvisible(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean invisible = Boolean.parseBoolean(value); + + entity.setInvisible(invisible); + } + + private static void setSilent(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean silent = Boolean.parseBoolean(value); + + entity.setSilent(silent); + } + + private static void setShaking(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean shaking = Boolean.parseBoolean(value); + + entity.setTicksFrozen(shaking ? entity.getTicksRequiredToFreeze() : 0); + } + + private static void setOnGround(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onGround = Boolean.parseBoolean(value); + + entity.setOnGround(onGround); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/FoxAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/FoxAttributes.java new file mode 100644 index 00000000..50541615 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/FoxAttributes.java @@ -0,0 +1,66 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.animal.Fox; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FoxAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "type", + Arrays.stream(Fox.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.FOX), + FoxAttributes::setType + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.FOX), + FoxAttributes::setPose + )); + + return attributes; + } + + private static void setType(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + Fox.Type type = Fox.Type.valueOf(value.toUpperCase()); + fox.setVariant(type); + } + + private static void setPose(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + fox.setIsCrouching(false); + fox.setSleeping(false); + fox.setSitting(false, false); + } + case "sleeping" -> { + fox.setSleeping(true); + fox.setSitting(false, false); + fox.setIsCrouching(false); + } + case "sitting" -> { + fox.setSitting(true, false); + fox.setSleeping(false); + fox.setIsCrouching(false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/FrogAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/FrogAttributes.java new file mode 100644 index 00000000..abafd8c9 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/FrogAttributes.java @@ -0,0 +1,44 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.animal.FrogVariant; +import net.minecraft.world.entity.animal.frog.Frog; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FrogAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(org.bukkit.entity.Frog.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.FROG), + FrogAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Frog frog = ReflectionHelper.getEntity(npc); + + FrogVariant variant; + switch (value.toUpperCase()) { + case "COLD" -> variant = FrogVariant.COLD; + case "WARM" -> variant = FrogVariant.WARM; + default -> variant = FrogVariant.TEMPERATE; + } + + frog.setVariant(variant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/GoatAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/GoatAttributes.java new file mode 100644 index 00000000..08612bcf --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/GoatAttributes.java @@ -0,0 +1,44 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.animal.goat.Goat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class GoatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "horns", + List.of("none", "left", "right", "both"), + List.of(EntityType.GOAT), + GoatAttributes::setHorns + )); + + return attributes; + } + + private static void setHorns(Npc npc, String value) { + Goat goat = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "none" -> goat.removeHorns(); + case "both" -> goat.addHorns(); + case "left" -> { + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, false); + } + case "right" -> { + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/HorseAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/HorseAttributes.java new file mode 100644 index 00000000..dd34ae6d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/HorseAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.animal.horse.Horse; +import net.minecraft.world.entity.animal.horse.Markings; +import net.minecraft.world.entity.animal.horse.Variant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class HorseAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "markings", + Arrays.stream(Markings.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setMarkings + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("eating", "rearing", "standing"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && (type == EntityType.HORSE || type == EntityType.DONKEY || + type == EntityType.MULE || type == EntityType.SKELETON_HORSE ||type == EntityType.ZOMBIE_HORSE)) + .toList(), + HorseAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Variant variant = Variant.valueOf(value.toUpperCase()); + horse.setVariant(variant); + } + + private static void setMarkings(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Markings markings = Markings.valueOf(value.toUpperCase()); + horse.setVariantAndMarkings(horse.getVariant(), markings); + } + + private static void setPose(Npc npc, String value) { + net.minecraft.world.entity.animal.horse.AbstractHorse horse = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + horse.setEating(false); + horse.setForceStanding(false); + } + case "rearing" -> { + horse.setForceStanding(true); + horse.setEating(false); + } + case "eating" -> { + horse.setForceStanding(false); + horse.setEating(true); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/IllagerAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/IllagerAttributes.java new file mode 100644 index 00000000..9336a069 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/IllagerAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.raid.Raider; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Illager; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class IllagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "celebrating", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Illager.class.isAssignableFrom(type.getEntityClass())) + .toList(), + IllagerAttributes::setCelebrating + )); + + return attributes; + } + + private static void setCelebrating(Npc npc, String value) { + Raider raider = ReflectionHelper.getEntity(npc); + + boolean isCelebrating = Boolean.parseBoolean(value); + + raider.setCelebrating(isCelebrating); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/InteractionAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/InteractionAttributes.java new file mode 100644 index 00000000..428ff7a7 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/InteractionAttributes.java @@ -0,0 +1,60 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.Interaction; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class InteractionAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "height", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setHeight + )); + + attributes.add(new NpcAttribute( + "width", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setWidth + )); + + return attributes; + } + + private static void setHeight(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float height; + try { + height = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setHeight(height); + } + + private static void setWidth(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float width; + try { + width = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setWidth(width); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/LivingEntityAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/LivingEntityAttributes.java new file mode 100644 index 00000000..183248ec --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/LivingEntityAttributes.java @@ -0,0 +1,67 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.InteractionHand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class LivingEntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + /*attributes.add(new NpcAttribute( + "hurt", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setHurt + ));*/ + + attributes.add(new NpcAttribute( + "use_item", + List.of("main_hand", "off_hand", "none"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setUseItem + )); + + return attributes; + } + + private static void setHurt(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + boolean isHurt = Boolean.parseBoolean(value); + + if (isHurt) { + livingEntity.hurtDuration = 1; + livingEntity.hurtTime = 1; + livingEntity.hurtMarked = true; + livingEntity.animateHurt(0); + } else { + livingEntity.hurtDuration = 0; + livingEntity.hurtTime = 0; + livingEntity.hurtMarked = false; + } + } + + private static void setUseItem(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + switch (value.toUpperCase()) { + case "NONE" -> livingEntity.stopUsingItem(); + case "MAIN_HAND" -> livingEntity.startUsingItem(InteractionHand.MAIN_HAND, true); + case "OFF_HAND" -> livingEntity.startUsingItem(InteractionHand.OFF_HAND, true); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/PandaAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/PandaAttributes.java new file mode 100644 index 00000000..d68520b3 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/PandaAttributes.java @@ -0,0 +1,102 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.MappingKeys1_20_2; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.animal.Panda; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PandaAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "gene", + Arrays.stream(Panda.Gene.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PANDA), + PandaAttributes::setGene + )); + + attributes.add(new NpcAttribute( + "eating", + List.of("true", "false"), + List.of(EntityType.PANDA), + PandaAttributes::setEating + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "onBack", "rolling"), + List.of(EntityType.PANDA), + PandaAttributes::setPose + )); + + return attributes; + } + + private static void setGene(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + Panda.Gene gene = Panda.Gene.valueOf(value.toUpperCase()); + panda.setMainGene(gene); + } + + private static void setPose(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(false); + } + case "sitting" -> { + panda.roll(false); + panda.setOnBack(false); + setFlag(panda, 8, true); //sitting + } + case "onback" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(true); + } + case "rolling" -> { + setFlag(panda, 8, false); //sitting + panda.setOnBack(false); + panda.roll(true); + } + } + } + + private static void setEating(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + boolean eating = Boolean.parseBoolean(value); + + panda.eat(eating); + } + + private static void setFlag(Panda panda, int mask, boolean value) { + EntityDataAccessor DATA_ID_FLAGS = (EntityDataAccessor) ReflectionUtils.getValue(panda, MappingKeys1_20_2.PANDA__DATA_ID_FLAGS.getMapping()); + + byte b0 = panda.getEntityData().get(DATA_ID_FLAGS); + + if (value) { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 | mask)); + } else { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 & ~mask)); + } + + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/ParrotAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/ParrotAttributes.java new file mode 100644 index 00000000..646134af --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/ParrotAttributes.java @@ -0,0 +1,59 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.animal.Parrot; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ParrotAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Parrot.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PARROT), + ParrotAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.PARROT), + ParrotAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + Parrot.Variant variant = Parrot.Variant.valueOf(value.toUpperCase()); + parrot.setVariant(variant); + } + + private static void setPose(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + parrot.setOrderedToSit(false); + parrot.setInSittingPose(false, false); + } + case "sitting" -> { + parrot.setOrderedToSit(true); + parrot.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/PigAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/PigAttributes.java new file mode 100644 index 00000000..cbf1822b --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/PigAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.animal.Pig; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PigAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "has_saddle", + List.of("true", "false"), + List.of(EntityType.PIG), + PigAttributes::setHasSaddle + )); + + return attributes; + } + + private static void setHasSaddle(Npc npc, String value) { + Pig pig = ReflectionHelper.getEntity(npc); + + boolean hasSaddle = Boolean.parseBoolean(value.toLowerCase()); + + pig.steering.setSaddle(hasSaddle); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/PiglinAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/PiglinAttributes.java new file mode 100644 index 00000000..8b5d9738 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/PiglinAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.monster.piglin.Piglin; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PiglinAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.PIGLIN), + PiglinAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Piglin piglin = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + piglin.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/PlayerAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/PlayerAttributes.java new file mode 100644 index 00000000..7c6b5fd0 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/PlayerAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.entity.player.Player; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PlayerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "crouching", "sleeping", "swimming", "sitting"), + List.of(EntityType.PLAYER), + PlayerAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Player player = ReflectionHelper.getEntity(npc); + + Pose pose = Pose.valueOf(value.toUpperCase()); + + EntityDataAccessor DATA_POSE = (EntityDataAccessor) ReflectionUtils.getStaticValue(Entity.class, "as"); // DATA_POSE + player.getEntityData().set(DATA_POSE, pose); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/RabbitAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/RabbitAttributes.java new file mode 100644 index 00000000..d223d394 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/RabbitAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.animal.Rabbit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class RabbitAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Rabbit.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.RABBIT), + RabbitAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Rabbit rabbit = ReflectionHelper.getEntity(npc); + + Rabbit.Variant variant = Rabbit.Variant.valueOf(value.toUpperCase()); + rabbit.setVariant(variant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/SheepAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/SheepAttributes.java new file mode 100644 index 00000000..74d278bc --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/SheepAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.animal.Sheep; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SheepAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "wool_color", + Arrays.stream(DyeColor.values()).map(dyeColor -> dyeColor.name().toLowerCase()).toList(), + List.of(EntityType.SHEEP), + SheepAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "sheared", + Arrays.asList("true", "false"), + List.of(EntityType.SHEEP), + SheepAttributes::setSheared + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + sheep.setColor(DyeColor.byName(value.toLowerCase(), DyeColor.WHITE)); + } + + private static void setSheared(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + boolean sheared = Boolean.parseBoolean(value); + + sheep.setSheared(sheared); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/ShulkerAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/ShulkerAttributes.java new file mode 100644 index 00000000..4332a7f8 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/ShulkerAttributes.java @@ -0,0 +1,55 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class ShulkerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.SHULKER), + ShulkerAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "shield", + List.of("open", "closed"), + List.of(EntityType.SHULKER), + ShulkerAttributes::setShield + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.PURPLE); + shulker.setVariant(Optional.of(color)); + } + + private static void setShield(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "closed" -> shulker.setRawPeekAmount(0); + case "open" -> shulker.setRawPeekAmount(Byte.MAX_VALUE); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/SlimeAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/SlimeAttributes.java new file mode 100644 index 00000000..a183f610 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/SlimeAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.monster.Slime; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class SlimeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "size", + new ArrayList<>(), + List.of(EntityType.SLIME), + SlimeAttributes::setSize + )); + + return attributes; + } + + private static void setSize(Npc npc, String value) { + Slime slime = ReflectionHelper.getEntity(npc); + + int size; + try { + size = Integer.parseInt(value); + } catch (NumberFormatException e) { + return; + } + + slime.setSize(size, false); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/SpellCasterAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/SpellCasterAttributes.java new file mode 100644 index 00000000..43a64374 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/SpellCasterAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.monster.SpellcasterIllager; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Spellcaster; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SpellCasterAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "casting", + Arrays.stream(SpellcasterIllager.IllagerSpell.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Spellcaster.class.isAssignableFrom(type.getEntityClass())) + .toList(), + SpellCasterAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + SpellcasterIllager spellcasterIllager = ReflectionHelper.getEntity(npc); + + SpellcasterIllager.IllagerSpell spell = SpellcasterIllager.IllagerSpell.valueOf(value); + + spellcasterIllager.setIsCastingSpell(spell); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/TextDisplayAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/TextDisplayAttributes.java new file mode 100644 index 00000000..f155730e --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/TextDisplayAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import io.papermc.paper.adventure.PaperAdventure; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class TextDisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "text", + new ArrayList<>(), + List.of(EntityType.TEXT_DISPLAY), + TextDisplayAttributes::setText + )); + + return attributes; + } + + private static void setText(Npc npc, String value) { + Display.TextDisplay display = ReflectionHelper.getEntity(npc); + + Component text = MiniMessage.miniMessage().deserialize(value); + display.setText(PaperAdventure.asVanilla(text)); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/TropicalFishAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/TropicalFishAttributes.java new file mode 100644 index 00000000..1896fdb9 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/TropicalFishAttributes.java @@ -0,0 +1,72 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.animal.TropicalFish; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TropicalFishAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pattern", + Arrays.stream(TropicalFish.Pattern.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPattern + )); + + attributes.add(new NpcAttribute( + "base_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setBaseColor + )); + + attributes.add(new NpcAttribute( + "pattern_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPatternColor + )); + + return attributes; + } + + private static void setPattern(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + TropicalFish.Pattern pattern = TropicalFish.Pattern.valueOf(value.toUpperCase()); + tropicalFish.setVariant(pattern); + } + + private static void setBaseColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), color, tropicalFish.getPatternColor()); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + + private static void setPatternColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), tropicalFish.getBaseColor(), color); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/VexAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/VexAttributes.java new file mode 100644 index 00000000..97ab3825 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/VexAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.monster.Vex; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class VexAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "charging", + List.of("true", "false"), + List.of(EntityType.VEX), + VexAttributes::setCharging + )); + + return attributes; + } + + private static void setCharging(Npc npc, String value) { + Vex vex = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> vex.setIsCharging(true); + case "false" -> vex.setIsCharging(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/VillagerAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/VillagerAttributes.java new file mode 100644 index 00000000..0017f998 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/VillagerAttributes.java @@ -0,0 +1,86 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.entity.npc.VillagerType; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class VillagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "profession", + Arrays.stream(org.bukkit.entity.Villager.Profession.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setProfession + )); + + attributes.add(new NpcAttribute( + "type", + Arrays.stream(org.bukkit.entity.Villager.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setType + )); + + return attributes; + } + + private static void setProfession(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerProfession profession; + switch (value.toUpperCase()) { + case "ARMORER" -> profession = VillagerProfession.ARMORER; + case "BUTCHER" -> profession = VillagerProfession.BUTCHER; + case "CARTOGRAPHER" -> profession = VillagerProfession.CARTOGRAPHER; + case "CLERIC" -> profession = VillagerProfession.CLERIC; + case "FARMER" -> profession = VillagerProfession.FARMER; + case "FISHERMAN" -> profession = VillagerProfession.FISHERMAN; + case "FLETCHER" -> profession = VillagerProfession.FLETCHER; + case "LEATHERWORKER" -> profession = VillagerProfession.LEATHERWORKER; + case "LIBRARIAN" -> profession = VillagerProfession.LIBRARIAN; + case "MASON" -> profession = VillagerProfession.MASON; + case "NITWIT" -> profession = VillagerProfession.NITWIT; + case "SHEPHERD" -> profession = VillagerProfession.SHEPHERD; + case "TOOLSMITH" -> profession = VillagerProfession.TOOLSMITH; + case "WEAPONSMITH" -> profession = VillagerProfession.WEAPONSMITH; + + default -> profession = VillagerProfession.NONE; + } + + villager.setVillagerData(villager.getVillagerData().setProfession(profession)); + } + + private static void setType(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerType type; + switch (value.toUpperCase()) { + case "DESERT" -> type = VillagerType.DESERT; + case "JUNGLE" -> type = VillagerType.JUNGLE; + case "SAVANNA" -> type = VillagerType.SAVANNA; + case "SNOW" -> type = VillagerType.SNOW; + case "SWAMP" -> type = VillagerType.SWAMP; + case "TAIGA" -> type = VillagerType.TAIGA; + + default -> type = VillagerType.PLAINS; + } + + + villager.setVillagerData(villager.getVillagerData().setType(type)); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/WolfAttributes.java b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/WolfAttributes.java new file mode 100644 index 00000000..7c158e89 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/attributes/WolfAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_20_2.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_2.ReflectionHelper; +import net.minecraft.world.entity.animal.Wolf; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class WolfAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.WOLF), + WolfAttributes::setPose + )); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.WOLF), + WolfAttributes::setAngry + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> wolf.setInSittingPose(false, false); + case "sitting" -> wolf.setInSittingPose(true, false); + } + } + + private static void setAngry(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + boolean angry = Boolean.parseBoolean(value.toLowerCase()); + + wolf.setRemainingPersistentAngerTime(angry ? 100 : 0); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_4/build.gradle.kts b/plugins/fancynpcs/implementation_1_20_4/build.gradle.kts new file mode 100644 index 00000000..42027fe1 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/build.gradle.kts @@ -0,0 +1,31 @@ +plugins { + id("java-library") + id("io.papermc.paperweight.userdev") +} + +val minecraftVersion = "1.20.4" + +dependencies { + paperweight.paperDevBundle("$minecraftVersion-R0.1-SNAPSHOT") + + compileOnly(project(":plugins:fancynpcs:api")) + compileOnly("de.oliver:FancyLib:37") + compileOnly("org.lushplugins:ChatColorHandler:5.1.3") +} + + +tasks { + named("assemble") { + dependsOn(named("reobfJar")) + } + + javadoc { + options.encoding = Charsets.UTF_8.name() + } + + compileJava { + options.encoding = Charsets.UTF_8.name() + options.release = 17 + + } +} \ No newline at end of file diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/MappingKeys1_20_4.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/MappingKeys1_20_4.java new file mode 100644 index 00000000..4ff5f8ae --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/MappingKeys1_20_4.java @@ -0,0 +1,22 @@ +package de.oliver.fancynpcs.v1_20_4; + +public enum MappingKeys1_20_4 { + + ENTITY_TYPE__FACTORY("bC"), + SYNCHED_ENTITY_DATA__ITEMS_BY_ID("e"), + CLIENTBOUND_TELEPORT_ENTITY_PACKET__X("b"), + CLIENTBOUND_TELEPORT_ENTITY_PACKET__Y("c"), + CLIENTBOUND_TELEPORT_ENTITY_PACKET__Z("d"), + PANDA__DATA_ID_FLAGS("cb"), + ; + + private final String mapping; + + MappingKeys1_20_4(String mapping) { + this.mapping = mapping; + } + + public String getMapping() { + return mapping; + } +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/Npc_1_20_4.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/Npc_1_20_4.java new file mode 100644 index 00000000..8270de3d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/Npc_1_20_4.java @@ -0,0 +1,385 @@ +package de.oliver.fancynpcs.v1_20_4; + +import com.google.common.collect.ImmutableList; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.datafixers.util.Pair; +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.events.NpcSpawnEvent; +import de.oliver.fancynpcs.api.utils.NpcEquipmentSlot; +import io.papermc.paper.adventure.PaperAdventure; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.minecraft.Optionull; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.RemoteChatSession; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ClientInformation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Display; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.scores.PlayerTeam; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.scores.Team; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_20_R3.CraftServer; +import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey; +import org.bukkit.entity.Player; +import org.lushplugins.chatcolorhandler.ModernChatColorHandler; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class Npc_1_20_4 extends Npc { + + private final String localName; + private final UUID uuid; + private Entity npc; + private Display.TextDisplay sittingVehicle; + + public Npc_1_20_4(NpcData data) { + super(data); + + this.localName = generateLocalName(); + this.uuid = UUID.randomUUID(); + } + + @Override + public void create() { + MinecraftServer minecraftServer = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel serverLevel = ((CraftWorld) data.getLocation().getWorld()).getHandle(); + GameProfile gameProfile = new GameProfile(uuid, localName); + + if (data.getType() == org.bukkit.entity.EntityType.PLAYER) { + npc = new ServerPlayer(minecraftServer, serverLevel, new GameProfile(uuid, ""), ClientInformation.createDefault()); + ((ServerPlayer) npc).gameProfile = gameProfile; + } else { + EntityType nmsType = BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(data.getType().getKey())); + EntityType.EntityFactory factory = (EntityType.EntityFactory) ReflectionUtils.getValue(nmsType, MappingKeys1_20_4.ENTITY_TYPE__FACTORY.getMapping()); // EntityType.factory + npc = factory.create(nmsType, serverLevel); + isTeamCreated.clear(); + } + } + + @Override + public void spawn(Player player) { + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc == null) { + return; + } + + if (!data.getLocation().getWorld().getName().equalsIgnoreCase(serverPlayer.level().getWorld().getName())) { + return; + } + + if (data.getSkinData() != null && data.getSkinData().hasTexture()) { + String value = data.getSkinData().getTextureValue(); + String signature = data.getSkinData().getTextureSignature(); + + ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues( + "textures", + ImmutableList.of(new Property("textures", value, signature)) + ); + } + + NpcSpawnEvent spawnEvent = new NpcSpawnEvent(this, player); + spawnEvent.callEvent(); + if (spawnEvent.isCancelled()) { + return; + } + + + if (npc instanceof ServerPlayer npcPlayer) { + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, getEntry(npcPlayer, serverPlayer)); + serverPlayer.connection.send(playerInfoPacket); + + if (data.isSpawnEntity()) { + npc.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + } + } + + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket(npc); + serverPlayer.connection.send(addEntityPacket); + + isVisibleForPlayer.put(player.getUniqueId(), true); + + int removeNpcsFromPlayerlistDelay = FancyNpcsPlugin.get().getFancyNpcConfig().getRemoveNpcsFromPlayerlistDelay(); + if (!data.isShowInTab() && removeNpcsFromPlayerlistDelay > 0) { + FancyNpcsPlugin.get().getNpcThread().schedule(() -> { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of(npc.getUUID())); + serverPlayer.connection.send(playerInfoRemovePacket); + }, removeNpcsFromPlayerlistDelay, TimeUnit.MILLISECONDS); + } + + update(player); + } + + @Override + public void remove(Player player) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc instanceof ServerPlayer npcPlayer) { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of((npcPlayer.getUUID()))); + serverPlayer.connection.send(playerInfoRemovePacket); + } + + // remove entity + ClientboundRemoveEntitiesPacket removeEntitiesPacket = new ClientboundRemoveEntitiesPacket(npc.getId()); + serverPlayer.connection.send(removeEntitiesPacket); + + // remove sitting vehicle + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + + isVisibleForPlayer.put(serverPlayer.getUUID(), false); + } + + @Override + public void lookAt(Player player, Location location) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setRot(location.getYaw(), location.getPitch()); + npc.setYHeadRot(location.getYaw()); + npc.setXRot(location.getPitch()); + npc.setYRot(location.getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket(npc); + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (location.getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + } + + @Override + public void update(Player player) { + if (npc == null) { + return; + } + + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + PlayerTeam team = new PlayerTeam(new Scoreboard(), "npc-" + localName); + team.getPlayers().clear(); + team.getPlayers().add(npc instanceof ServerPlayer npcPlayer ? npcPlayer.getGameProfile().getName() : npc.getStringUUID()); + team.setColor(PaperAdventure.asVanilla(data.getGlowingColor())); + if (!data.isCollidable()) { + team.setCollisionRule(Team.CollisionRule.NEVER); + } + + net.kyori.adventure.text.Component displayName = ModernChatColorHandler.translate(data.getDisplayName(), serverPlayer.getBukkitEntity()); + Component vanillaComponent = PaperAdventure.asVanilla(displayName); + if (!(npc instanceof ServerPlayer)) { + npc.setCustomName(vanillaComponent); + npc.setCustomNameVisible(true); + } else { + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } + + if (data.getDisplayName().equalsIgnoreCase("")) { + team.setNameTagVisibility(Team.Visibility.NEVER); + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } else { + team.setNameTagVisibility(Team.Visibility.ALWAYS); + } + + if (npc instanceof ServerPlayer npcPlayer) { + team.setPlayerPrefix(vanillaComponent); + npcPlayer.listName = vanillaComponent; + + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, getEntry(npcPlayer, serverPlayer)); + serverPlayer.connection.send(playerInfoPacket); + } + + boolean isTeamCreatedForPlayer = this.isTeamCreated.getOrDefault(player.getUniqueId(), false); + serverPlayer.connection.send(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, !isTeamCreatedForPlayer)); + isTeamCreated.put(player.getUniqueId(), true); + + npc.setGlowingTag(data.isGlowing()); + + if (data.getEquipment() != null && data.getEquipment().size() > 0) { + List> equipmentList = new ArrayList<>(); + + for (NpcEquipmentSlot slot : data.getEquipment().keySet()) { + equipmentList.add(new Pair<>(EquipmentSlot.byName(slot.toNmsName()), CraftItemStack.asNMSCopy(data.getEquipment().get(slot)))); + } + + ClientboundSetEquipmentPacket setEquipmentPacket = new ClientboundSetEquipmentPacket(npc.getId(), equipmentList); + serverPlayer.connection.send(setEquipmentPacket); + } + + if (npc instanceof ServerPlayer) { + // Enable second layer of skin (https://wiki.vg/Entity_metadata#Player) + npc.getEntityData().set(net.minecraft.world.entity.player.Player.DATA_PLAYER_MODE_CUSTOMISATION, (byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40)); + } + + data.applyAllAttributes(this); + + refreshEntityData(player); + + if (data.isSpawnEntity() && data.getLocation() != null) { + move(player, true); + } + + NpcAttribute playerPoseAttr = FancyNpcsPlugin.get().getAttributeManager().getAttributeByName(org.bukkit.entity.EntityType.PLAYER, "pose"); + if (data.getAttributes().containsKey(playerPoseAttr)) { + String pose = data.getAttributes().get(playerPoseAttr); + + if (pose.equals("sitting")) { + setSitting(serverPlayer); + } else { + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + } + + } + } + + @Override + protected void refreshEntityData(Player player) { + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + Int2ObjectMap> itemsById = (Int2ObjectMap>) ReflectionUtils.getValue(npc.getEntityData(), MappingKeys1_20_4.SYNCHED_ENTITY_DATA__ITEMS_BY_ID.getMapping()); // itemsById + List> entityData = new ArrayList<>(); + for (SynchedEntityData.DataItem dataItem : itemsById.values()) { + entityData.add(dataItem.value()); + } + ClientboundSetEntityDataPacket setEntityDataPacket = new ClientboundSetEntityDataPacket(npc.getId(), entityData); + serverPlayer.connection.send(setEntityDataPacket); + } + + public void move(Player player, boolean swingArm) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setPosRaw(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + npc.setRot(data.getLocation().getYaw(), data.getLocation().getPitch()); + npc.setYHeadRot(data.getLocation().getYaw()); + npc.setXRot(data.getLocation().getPitch()); + npc.setYRot(data.getLocation().getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket(npc); + ReflectionUtils.setValue(teleportEntityPacket, MappingKeys1_20_4.CLIENTBOUND_TELEPORT_ENTITY_PACKET__X.getMapping(), data.getLocation().x()); // 'x' + ReflectionUtils.setValue(teleportEntityPacket, MappingKeys1_20_4.CLIENTBOUND_TELEPORT_ENTITY_PACKET__Y.getMapping(), data.getLocation().y()); // 'y' + ReflectionUtils.setValue(teleportEntityPacket, MappingKeys1_20_4.CLIENTBOUND_TELEPORT_ENTITY_PACKET__Z.getMapping(), data.getLocation().z()); // 'z' + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (data.getLocation().getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + + if (swingArm && npc instanceof ServerPlayer) { + ClientboundAnimatePacket animatePacket = new ClientboundAnimatePacket(npc, 0); + serverPlayer.connection.send(animatePacket); + } + } + + private ClientboundPlayerInfoUpdatePacket.Entry getEntry(ServerPlayer npcPlayer, ServerPlayer viewer) { + GameProfile profile = npcPlayer.getGameProfile(); + if (data.isMirrorSkin() && net.minecraft.world.entity.player.Player.isValidUsername(viewer.getGameProfile().getName())) { + GameProfile newProfile = new GameProfile(profile.getId(), profile.getName()); + newProfile.getProperties().putAll(viewer.getGameProfile().getProperties()); + profile = newProfile; + } + + return new ClientboundPlayerInfoUpdatePacket.Entry( + npcPlayer.getUUID(), + profile, + data.isShowInTab(), + 69, + npcPlayer.gameMode.getGameModeForPlayer(), + npcPlayer.getTabListDisplayName(), + Optionull.map(npcPlayer.getChatSession(), RemoteChatSession::asData) + ); + } + + public void setSitting(ServerPlayer serverPlayer) { + if (npc == null) { + return; + } + + if (sittingVehicle == null) { + sittingVehicle = new Display.TextDisplay(EntityType.TEXT_DISPLAY, ((CraftWorld) data.getLocation().getWorld()).getHandle()); + } + + sittingVehicle.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket(sittingVehicle); + serverPlayer.connection.send(addEntityPacket); + + sittingVehicle.passengers = ImmutableList.of(npc); + + ClientboundSetPassengersPacket packet = new ClientboundSetPassengersPacket(sittingVehicle); + serverPlayer.connection.send(packet); + } + + @Override + public float getEyeHeight() { + return npc.getEyeHeight(); + } + + @Override + public int getEntityId() { + return npc.getId(); + } + + public Entity getNpc() { + return npc; + } +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/ReflectionHelper.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/ReflectionHelper.java new file mode 100644 index 00000000..f9a97b0f --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/ReflectionHelper.java @@ -0,0 +1,13 @@ +package de.oliver.fancynpcs.v1_20_4; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import net.minecraft.world.entity.Entity; + +public class ReflectionHelper { + + public static T getEntity(Npc npc) { + return (T) ReflectionUtils.getValue(npc, "npc"); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/AgeableMobAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/AgeableMobAttributes.java new file mode 100644 index 00000000..28cc325d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/AgeableMobAttributes.java @@ -0,0 +1,38 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.AgeableMob; +import org.bukkit.entity.Ageable; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AgeableMobAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "baby", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Ageable.class.isAssignableFrom(type.getEntityClass())) + .toList(), + AgeableMobAttributes::setBaby + )); + + return attributes; + } + + private static void setBaby(Npc npc, String value) { + AgeableMob mob = ReflectionHelper.getEntity(npc); + + boolean isBaby = Boolean.parseBoolean(value); + + mob.setBaby(isBaby); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/AllayAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/AllayAttributes.java new file mode 100644 index 00000000..48b68a82 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/AllayAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.animal.allay.Allay; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class AllayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.ALLAY), + AllayAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Allay allay = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + allay.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/ArmorStandAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/ArmorStandAttributes.java new file mode 100644 index 00000000..874c7de4 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/ArmorStandAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Pig; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class ArmorStandAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "show_arms", + List.of("true", "false"), + List.of(EntityType.ARMOR_STAND), + ArmorStandAttributes::setShowArms + )); + + return attributes; + } + + private static void setShowArms(Npc npc, String value) { + ArmorStand armorStand = ReflectionHelper.getEntity(npc); + + boolean showArms = Boolean.parseBoolean(value.toLowerCase()); + + armorStand.setShowArms(showArms); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/Attributes_1_20_4.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/Attributes_1_20_4.java new file mode 100644 index 00000000..dbb963a3 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/Attributes_1_20_4.java @@ -0,0 +1,51 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.NpcAttribute; + +import java.util.ArrayList; +import java.util.List; + +public class Attributes_1_20_4 { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.addAll(EntityAttributes.getAllAttributes()); + attributes.addAll(LivingEntityAttributes.getAllAttributes()); + attributes.addAll(AgeableMobAttributes.getAllAttributes()); + attributes.addAll(IllagerAttributes.getAllAttributes()); + attributes.addAll(SpellCasterAttributes.getAllAttributes()); + + attributes.addAll(PlayerAttributes.getAllAttributes()); + attributes.addAll(SheepAttributes.getAllAttributes()); + attributes.addAll(VillagerAttributes.getAllAttributes()); + attributes.addAll(FrogAttributes.getAllAttributes()); + attributes.addAll(HorseAttributes.getAllAttributes()); + attributes.addAll(ParrotAttributes.getAllAttributes()); + attributes.addAll(AxolotlAttributes.getAllAttributes()); + attributes.addAll(TropicalFishAttributes.getAllAttributes()); + attributes.addAll(FoxAttributes.getAllAttributes()); + attributes.addAll(PandaAttributes.getAllAttributes()); + attributes.addAll(GoatAttributes.getAllAttributes()); + attributes.addAll(AllayAttributes.getAllAttributes()); + attributes.addAll(CamelAttributes.getAllAttributes()); + attributes.addAll(RabbitAttributes.getAllAttributes()); + attributes.addAll(PiglinAttributes.getAllAttributes()); + attributes.addAll(CatAttributes.getAllAttributes()); + attributes.addAll(ShulkerAttributes.getAllAttributes()); + attributes.addAll(WolfAttributes.getAllAttributes()); + attributes.addAll(SlimeAttributes.getAllAttributes()); + attributes.addAll(PigAttributes.getAllAttributes()); + attributes.addAll(ArmorStandAttributes.getAllAttributes()); + attributes.addAll(BeeAttributes.getAllAttributes()); + attributes.addAll(VexAttributes.getAllAttributes()); + + attributes.addAll(DisplayAttributes.getAllAttributes()); + attributes.addAll(TextDisplayAttributes.getAllAttributes()); + attributes.addAll(BlockDisplayAttributes.getAllAttributes()); + attributes.addAll(InteractionAttributes.getAllAttributes()); + + return attributes; + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/AxolotlAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/AxolotlAttributes.java new file mode 100644 index 00000000..cd841633 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/AxolotlAttributes.java @@ -0,0 +1,51 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.animal.axolotl.Axolotl; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AxolotlAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Axolotl.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "playing_dead", + List.of("true", "false"), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setPlayingDead + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + Axolotl.Variant variant = Axolotl.Variant.valueOf(value.toUpperCase()); + axolotl.setVariant(variant); + } + + private static void setPlayingDead(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + boolean playingDead = Boolean.parseBoolean(value); + axolotl.setPlayingDead(playingDead); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/BeeAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/BeeAttributes.java new file mode 100644 index 00000000..0585bac8 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/BeeAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Bee; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BeeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setAngry + )); + + attributes.add(new NpcAttribute( + "sting", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setSting + )); + + attributes.add(new NpcAttribute( + "nectar", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setNectar + )); + + attributes.add(new NpcAttribute( + "rolling", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setRolling + )); + + return attributes; + } + + private static void setAngry(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRemainingPersistentAngerTime(1); + case "false" -> bee.setRemainingPersistentAngerTime(0); + } + } + + private static void setSting(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasStung(false); + case "false" -> bee.setHasStung(true); + } + } + + private static void setNectar(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasNectar(true); + case "false" -> bee.setHasNectar(false); + } + } + + private static void setRolling(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRolling(true); + case "false" -> bee.setRolling(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/BlockDisplayAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/BlockDisplayAttributes.java new file mode 100644 index 00000000..30ac9537 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/BlockDisplayAttributes.java @@ -0,0 +1,41 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Display; +import net.minecraft.world.level.block.Block; +import org.bukkit.Material; +import org.bukkit.Registry; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BlockDisplayAttributes { + + private static final List BLOCKS = Registry.MATERIAL.stream().filter(Material::isBlock).map(it -> it.key().value()).toList(); + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "block", + BLOCKS, + List.of(EntityType.BLOCK_DISPLAY), + BlockDisplayAttributes::setBlock + )); + + return attributes; + } + + private static void setBlock(Npc npc, String value) { + Display.BlockDisplay display = ReflectionHelper.getEntity(npc); + + Block block = BuiltInRegistries.BLOCK.get(ResourceLocation.of("minecraft:" + value.toLowerCase(), ':')); + + display.setBlockState(block.defaultBlockState()); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/CamelAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/CamelAttributes.java new file mode 100644 index 00000000..43ae93c4 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/CamelAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.animal.camel.Camel; +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class CamelAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "dashing"), + List.of(EntityType.CAMEL), + CamelAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Camel camel = ReflectionHelper.getEntity(npc); + + Bukkit.getScheduler().runTask(FancyNpcsPlugin.get().getPlugin(), () -> { + switch (value.toLowerCase()) { + case "standing" -> { + camel.setDashing(false); + camel.standUp(); + } + case "sitting" -> { + camel.setDashing(false); + camel.sitDown(); + } + case "dashing" -> { + camel.standUpInstantly(); + camel.setDashing(true); + } + } + }); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/CatAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/CatAttributes.java new file mode 100644 index 00000000..83606ff1 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/CatAttributes.java @@ -0,0 +1,64 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.Cat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class CatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(org.bukkit.entity.Cat.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.CAT), + CatAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.CAT), + CatAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + BuiltInRegistries.CAT_VARIANT.getOptional(ResourceLocation.of(value.toLowerCase(), ':')) + .ifPresent(cat::setVariant); + } + + private static void setPose(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + switch (value.toLowerCase()) { + case "standing" -> { + cat.setInSittingPose(false, false); + cat.setLying(false); + } + case "sleeping" -> { + cat.setInSittingPose(false, false); + cat.setLying(true); + } + case "sitting" -> { + cat.setLying(false); + cat.setOrderedToSit(true); + cat.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/DisplayAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/DisplayAttributes.java new file mode 100644 index 00000000..04754043 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/DisplayAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class DisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "billboard", + Arrays.stream(org.bukkit.entity.Display.Billboard.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TEXT_DISPLAY, EntityType.BLOCK_DISPLAY, EntityType.ITEM_DISPLAY), + DisplayAttributes::setBillboard + )); + + return attributes; + } + + private static void setBillboard(Npc npc, String value) { + Display display = ReflectionHelper.getEntity(npc); + + Display.BillboardConstraints billboard = Display.BillboardConstraints.valueOf(value.toUpperCase()); + display.setBillboardConstraints(billboard); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/EntityAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/EntityAttributes.java new file mode 100644 index 00000000..13fad3e2 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/EntityAttributes.java @@ -0,0 +1,114 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.entity.monster.AbstractIllager; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class EntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "on_fire", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnFire + )); + + attributes.add(new NpcAttribute( + "invisible", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setInvisible + )); + + attributes.add(new NpcAttribute( + "silent", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setSilent + )); + + attributes.add(new NpcAttribute( + "shaking", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setShaking + )); + + attributes.add(new NpcAttribute( + "on_ground", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnGround + )); + + /*attributes.add(new NpcAttribute( + "entity_pose", + Arrays.stream(Pose.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setEntityPose + ));*/ + + return attributes; + } + + private static void setOnFire(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onFire = Boolean.parseBoolean(value); + + entity.setSharedFlagOnFire(onFire); + + } + + private static void setInvisible(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean invisible = Boolean.parseBoolean(value); + + entity.setInvisible(invisible); + } + + private static void setSilent(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean silent = Boolean.parseBoolean(value); + + entity.setSilent(silent); + } + + private static void setShaking(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean shaking = Boolean.parseBoolean(value); + + entity.setTicksFrozen(shaking ? entity.getTicksRequiredToFreeze() : 0); + } + + private static void setOnGround(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onGround = Boolean.parseBoolean(value); + + entity.setOnGround(onGround); + } + + /*private static void setEntityPose(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + Pose entityPose = Pose.valueOf(value); + + //setpose should not be called async + //entity.setPose(entityPose); + }*/ +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/FoxAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/FoxAttributes.java new file mode 100644 index 00000000..140d1fe6 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/FoxAttributes.java @@ -0,0 +1,66 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Fox; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FoxAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "type", + Arrays.stream(Fox.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.FOX), + FoxAttributes::setType + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.FOX), + FoxAttributes::setPose + )); + + return attributes; + } + + private static void setType(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + Fox.Type type = Fox.Type.valueOf(value.toUpperCase()); + fox.setVariant(type); + } + + private static void setPose(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + fox.setIsCrouching(false); + fox.setSleeping(false); + fox.setSitting(false, false); + } + case "sleeping" -> { + fox.setSleeping(true); + fox.setSitting(false, false); + fox.setIsCrouching(false); + } + case "sitting" -> { + fox.setSitting(true, false); + fox.setSleeping(false); + fox.setIsCrouching(false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/FrogAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/FrogAttributes.java new file mode 100644 index 00000000..d7e5cb36 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/FrogAttributes.java @@ -0,0 +1,44 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.animal.FrogVariant; +import net.minecraft.world.entity.animal.frog.Frog; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FrogAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(org.bukkit.entity.Frog.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.FROG), + FrogAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Frog frog = ReflectionHelper.getEntity(npc); + + FrogVariant variant; + switch (value.toUpperCase()) { + case "COLD" -> variant = FrogVariant.COLD; + case "WARM" -> variant = FrogVariant.WARM; + default -> variant = FrogVariant.TEMPERATE; + } + + frog.setVariant(variant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/GoatAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/GoatAttributes.java new file mode 100644 index 00000000..7aa6b8e6 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/GoatAttributes.java @@ -0,0 +1,44 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.animal.goat.Goat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class GoatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "horns", + List.of("none", "left", "right", "both"), + List.of(EntityType.GOAT), + GoatAttributes::setHorns + )); + + return attributes; + } + + private static void setHorns(Npc npc, String value) { + Goat goat = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "none" -> goat.removeHorns(); + case "both" -> goat.addHorns(); + case "left" -> { + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, false); + } + case "right" -> { + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/HorseAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/HorseAttributes.java new file mode 100644 index 00000000..6e142690 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/HorseAttributes.java @@ -0,0 +1,87 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.animal.camel.Camel; +import net.minecraft.world.entity.animal.horse.Horse; +import net.minecraft.world.entity.animal.horse.Markings; +import net.minecraft.world.entity.animal.horse.Variant; +import org.bukkit.entity.AbstractHorse; +import org.bukkit.entity.Ageable; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class HorseAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "markings", + Arrays.stream(Markings.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setMarkings + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("eating", "rearing", "standing"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && (type == EntityType.HORSE || type == EntityType.DONKEY || + type == EntityType.MULE || type == EntityType.SKELETON_HORSE ||type == EntityType.ZOMBIE_HORSE)) + .toList(), + HorseAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Variant variant = Variant.valueOf(value.toUpperCase()); + horse.setVariant(variant); + } + + private static void setMarkings(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Markings markings = Markings.valueOf(value.toUpperCase()); + horse.setVariantAndMarkings(horse.getVariant(), markings); + } + + private static void setPose(Npc npc, String value) { + net.minecraft.world.entity.animal.horse.AbstractHorse horse = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + horse.setEating(false); + horse.setForceStanding(false); + } + case "rearing" -> { + horse.setForceStanding(true); + horse.setEating(false); + } + case "eating" -> { + horse.setForceStanding(false); + horse.setEating(true); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/IllagerAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/IllagerAttributes.java new file mode 100644 index 00000000..d5335412 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/IllagerAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.raid.Raider; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Illager; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class IllagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "celebrating", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Illager.class.isAssignableFrom(type.getEntityClass())) + .toList(), + IllagerAttributes::setCelebrating + )); + + return attributes; + } + + private static void setCelebrating(Npc npc, String value) { + Raider raider = ReflectionHelper.getEntity(npc); + + boolean isCelebrating = Boolean.parseBoolean(value); + + raider.setCelebrating(isCelebrating); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/InteractionAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/InteractionAttributes.java new file mode 100644 index 00000000..c0c2409d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/InteractionAttributes.java @@ -0,0 +1,60 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.Interaction; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class InteractionAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "height", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setHeight + )); + + attributes.add(new NpcAttribute( + "width", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setWidth + )); + + return attributes; + } + + private static void setHeight(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float height; + try { + height = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setHeight(height); + } + + private static void setWidth(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float width; + try { + width = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setWidth(width); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/LivingEntityAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/LivingEntityAttributes.java new file mode 100644 index 00000000..17ee992d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/LivingEntityAttributes.java @@ -0,0 +1,67 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.InteractionHand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class LivingEntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + /*attributes.add(new NpcAttribute( + "hurt", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setHurt + ));*/ + + attributes.add(new NpcAttribute( + "use_item", + List.of("main_hand", "off_hand", "none"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setUseItem + )); + + return attributes; + } + + private static void setHurt(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + boolean isHurt = Boolean.parseBoolean(value); + + if (isHurt) { + livingEntity.hurtDuration = 1; + livingEntity.hurtTime = 1; + livingEntity.hurtMarked = true; + livingEntity.animateHurt(0); + } else { + livingEntity.hurtDuration = 0; + livingEntity.hurtTime = 0; + livingEntity.hurtMarked = false; + } + } + + private static void setUseItem(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + switch (value.toUpperCase()) { + case "NONE" -> livingEntity.stopUsingItem(); + case "MAIN_HAND" -> livingEntity.startUsingItem(InteractionHand.MAIN_HAND, true); + case "OFF_HAND" -> livingEntity.startUsingItem(InteractionHand.OFF_HAND, true); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/PandaAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/PandaAttributes.java new file mode 100644 index 00000000..3690c82a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/PandaAttributes.java @@ -0,0 +1,102 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.MappingKeys1_20_4; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.animal.Panda; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PandaAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "gene", + Arrays.stream(Panda.Gene.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PANDA), + PandaAttributes::setGene + )); + + attributes.add(new NpcAttribute( + "eating", + List.of("true", "false"), + List.of(EntityType.PANDA), + PandaAttributes::setEating + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "onBack", "rolling"), + List.of(EntityType.PANDA), + PandaAttributes::setPose + )); + + return attributes; + } + + private static void setGene(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + Panda.Gene gene = Panda.Gene.valueOf(value.toUpperCase()); + panda.setMainGene(gene); + } + + private static void setPose(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(false); + } + case "sitting" -> { + panda.roll(false); + panda.setOnBack(false); + setFlag(panda, 8, true); //sitting + } + case "onback" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(true); + } + case "rolling" -> { + setFlag(panda, 8, false); //sitting + panda.setOnBack(false); + panda.roll(true); + } + } + } + + private static void setEating(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + boolean eating = Boolean.parseBoolean(value); + + panda.eat(eating); + } + + private static void setFlag(Panda panda, int mask, boolean value) { + EntityDataAccessor DATA_ID_FLAGS = (EntityDataAccessor) ReflectionUtils.getValue(panda, MappingKeys1_20_4.PANDA__DATA_ID_FLAGS.getMapping()); + + byte b0 = panda.getEntityData().get(DATA_ID_FLAGS); + + if (value) { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 | mask)); + } else { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 & ~mask)); + } + + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/ParrotAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/ParrotAttributes.java new file mode 100644 index 00000000..453ae45b --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/ParrotAttributes.java @@ -0,0 +1,59 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Parrot; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ParrotAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Parrot.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PARROT), + ParrotAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.PARROT), + ParrotAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + Parrot.Variant variant = Parrot.Variant.valueOf(value.toUpperCase()); + parrot.setVariant(variant); + } + + private static void setPose(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + parrot.setOrderedToSit(false); + parrot.setInSittingPose(false, false); + } + case "sitting" -> { + parrot.setOrderedToSit(true); + parrot.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/PigAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/PigAttributes.java new file mode 100644 index 00000000..0ff00102 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/PigAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Pig; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PigAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "has_saddle", + List.of("true", "false"), + List.of(EntityType.PIG), + PigAttributes::setHasSaddle + )); + + return attributes; + } + + private static void setHasSaddle(Npc npc, String value) { + Pig pig = ReflectionHelper.getEntity(npc); + + boolean hasSaddle = Boolean.parseBoolean(value.toLowerCase()); + + pig.steering.setSaddle(hasSaddle); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/PiglinAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/PiglinAttributes.java new file mode 100644 index 00000000..83f0a8fe --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/PiglinAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.monster.piglin.Piglin; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PiglinAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.PIGLIN), + PiglinAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Piglin piglin = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + piglin.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/PlayerAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/PlayerAttributes.java new file mode 100644 index 00000000..37afe64a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/PlayerAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.entity.player.Player; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PlayerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "crouching", "sleeping", "swimming", "sitting"), + List.of(EntityType.PLAYER), + PlayerAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Player player = ReflectionHelper.getEntity(npc); + + Pose pose = Pose.valueOf(value.toUpperCase()); + + EntityDataAccessor DATA_POSE = (EntityDataAccessor) ReflectionUtils.getStaticValue(Entity.class, "as"); // DATA_POSE + player.getEntityData().set(DATA_POSE, pose); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/RabbitAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/RabbitAttributes.java new file mode 100644 index 00000000..fec1a83a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/RabbitAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Rabbit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class RabbitAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Rabbit.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.RABBIT), + RabbitAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Rabbit rabbit = ReflectionHelper.getEntity(npc); + + Rabbit.Variant variant = Rabbit.Variant.valueOf(value.toUpperCase()); + rabbit.setVariant(variant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/SheepAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/SheepAttributes.java new file mode 100644 index 00000000..d9e7f1a7 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/SheepAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Sheep; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SheepAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "wool_color", + Arrays.stream(DyeColor.values()).map(dyeColor -> dyeColor.name().toLowerCase()).toList(), + List.of(EntityType.SHEEP), + SheepAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "sheared", + Arrays.asList("true", "false"), + List.of(EntityType.SHEEP), + SheepAttributes::setSheared + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + sheep.setColor(DyeColor.byName(value.toLowerCase(), DyeColor.WHITE)); + } + + private static void setSheared(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + boolean sheared = Boolean.parseBoolean(value); + + sheep.setSheared(sheared); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/ShulkerAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/ShulkerAttributes.java new file mode 100644 index 00000000..82ffaea5 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/ShulkerAttributes.java @@ -0,0 +1,55 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class ShulkerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.SHULKER), + ShulkerAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "shield", + List.of("open", "closed"), + List.of(EntityType.SHULKER), + ShulkerAttributes::setShield + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.PURPLE); + shulker.setVariant(Optional.of(color)); + } + + private static void setShield(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "closed" -> shulker.setRawPeekAmount(0); + case "open" -> shulker.setRawPeekAmount(Byte.MAX_VALUE); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/SlimeAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/SlimeAttributes.java new file mode 100644 index 00000000..f63f8e81 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/SlimeAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.monster.Slime; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class SlimeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "size", + new ArrayList<>(), + List.of(EntityType.SLIME), + SlimeAttributes::setSize + )); + + return attributes; + } + + private static void setSize(Npc npc, String value) { + Slime slime = ReflectionHelper.getEntity(npc); + + int size; + try { + size = Integer.parseInt(value); + } catch (NumberFormatException e) { + return; + } + + slime.setSize(size, false); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/SpellCasterAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/SpellCasterAttributes.java new file mode 100644 index 00000000..939de50a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/SpellCasterAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.monster.SpellcasterIllager; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Spellcaster; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SpellCasterAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "casting", + Arrays.stream(SpellcasterIllager.IllagerSpell.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Spellcaster.class.isAssignableFrom(type.getEntityClass())) + .toList(), + SpellCasterAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + SpellcasterIllager spellcasterIllager = ReflectionHelper.getEntity(npc); + + SpellcasterIllager.IllagerSpell spell = SpellcasterIllager.IllagerSpell.valueOf(value); + + spellcasterIllager.setIsCastingSpell(spell); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/TextDisplayAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/TextDisplayAttributes.java new file mode 100644 index 00000000..6711b221 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/TextDisplayAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import io.papermc.paper.adventure.PaperAdventure; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class TextDisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "text", + new ArrayList<>(), + List.of(EntityType.TEXT_DISPLAY), + TextDisplayAttributes::setText + )); + + return attributes; + } + + private static void setText(Npc npc, String value) { + Display.TextDisplay display = ReflectionHelper.getEntity(npc); + + Component text = MiniMessage.miniMessage().deserialize(value); + display.setText(PaperAdventure.asVanilla(text)); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/TropicalFishAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/TropicalFishAttributes.java new file mode 100644 index 00000000..6ac2ba67 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/TropicalFishAttributes.java @@ -0,0 +1,72 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.animal.TropicalFish; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TropicalFishAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pattern", + Arrays.stream(TropicalFish.Pattern.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPattern + )); + + attributes.add(new NpcAttribute( + "base_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setBaseColor + )); + + attributes.add(new NpcAttribute( + "pattern_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPatternColor + )); + + return attributes; + } + + private static void setPattern(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + TropicalFish.Pattern pattern = TropicalFish.Pattern.valueOf(value.toUpperCase()); + tropicalFish.setVariant(pattern); + } + + private static void setBaseColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), color, tropicalFish.getPatternColor()); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + + private static void setPatternColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), tropicalFish.getBaseColor(), color); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/VexAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/VexAttributes.java new file mode 100644 index 00000000..df015d2f --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/VexAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.monster.Vex; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class VexAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "charging", + List.of("true", "false"), + List.of(EntityType.VEX), + VexAttributes::setCharging + )); + + return attributes; + } + + private static void setCharging(Npc npc, String value) { + Vex vex = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> vex.setIsCharging(true); + case "false" -> vex.setIsCharging(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/VillagerAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/VillagerAttributes.java new file mode 100644 index 00000000..b306b8d8 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/VillagerAttributes.java @@ -0,0 +1,86 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.entity.npc.VillagerType; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class VillagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "profession", + Arrays.stream(org.bukkit.entity.Villager.Profession.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setProfession + )); + + attributes.add(new NpcAttribute( + "type", + Arrays.stream(org.bukkit.entity.Villager.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setType + )); + + return attributes; + } + + private static void setProfession(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerProfession profession; + switch (value.toUpperCase()) { + case "ARMORER" -> profession = VillagerProfession.ARMORER; + case "BUTCHER" -> profession = VillagerProfession.BUTCHER; + case "CARTOGRAPHER" -> profession = VillagerProfession.CARTOGRAPHER; + case "CLERIC" -> profession = VillagerProfession.CLERIC; + case "FARMER" -> profession = VillagerProfession.FARMER; + case "FISHERMAN" -> profession = VillagerProfession.FISHERMAN; + case "FLETCHER" -> profession = VillagerProfession.FLETCHER; + case "LEATHERWORKER" -> profession = VillagerProfession.LEATHERWORKER; + case "LIBRARIAN" -> profession = VillagerProfession.LIBRARIAN; + case "MASON" -> profession = VillagerProfession.MASON; + case "NITWIT" -> profession = VillagerProfession.NITWIT; + case "SHEPHERD" -> profession = VillagerProfession.SHEPHERD; + case "TOOLSMITH" -> profession = VillagerProfession.TOOLSMITH; + case "WEAPONSMITH" -> profession = VillagerProfession.WEAPONSMITH; + + default -> profession = VillagerProfession.NONE; + } + + villager.setVillagerData(villager.getVillagerData().setProfession(profession)); + } + + private static void setType(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerType type; + switch (value.toUpperCase()) { + case "DESERT" -> type = VillagerType.DESERT; + case "JUNGLE" -> type = VillagerType.JUNGLE; + case "SAVANNA" -> type = VillagerType.SAVANNA; + case "SNOW" -> type = VillagerType.SNOW; + case "SWAMP" -> type = VillagerType.SWAMP; + case "TAIGA" -> type = VillagerType.TAIGA; + + default -> type = VillagerType.PLAINS; + } + + + villager.setVillagerData(villager.getVillagerData().setType(type)); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/WolfAttributes.java b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/WolfAttributes.java new file mode 100644 index 00000000..5de9df0c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/attributes/WolfAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_20_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Wolf; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class WolfAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.WOLF), + WolfAttributes::setPose + )); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.WOLF), + WolfAttributes::setAngry + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> wolf.setInSittingPose(false, false); + case "sitting" -> wolf.setInSittingPose(true, false); + } + } + + private static void setAngry(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + boolean angry = Boolean.parseBoolean(value.toLowerCase()); + + wolf.setRemainingPersistentAngerTime(angry ? 100 : 0); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_6/build.gradle.kts b/plugins/fancynpcs/implementation_1_20_6/build.gradle.kts new file mode 100644 index 00000000..98a9cdfb --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("java-library") + id("io.papermc.paperweight.userdev") +} + +val minecraftVersion = "1.20.6" + +paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.MOJANG_PRODUCTION + +dependencies { + paperweight.paperDevBundle("$minecraftVersion-R0.1-SNAPSHOT") + + compileOnly(project(":plugins:fancynpcs:api")) + compileOnly("de.oliver:FancyLib:37") + compileOnly("org.lushplugins:ChatColorHandler:5.1.3") +} + + +tasks { + javadoc { + options.encoding = Charsets.UTF_8.name() + } + + compileJava { + options.encoding = Charsets.UTF_8.name() + options.release = 21 + } +} \ No newline at end of file diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/MappingKeys1_20_6.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/MappingKeys1_20_6.java new file mode 100644 index 00000000..25fbf633 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/MappingKeys1_20_6.java @@ -0,0 +1,22 @@ +package de.oliver.fancynpcs.v1_20_6; + +public enum MappingKeys1_20_6 { + + ENTITY_TYPE__FACTORY("factory"), + SYNCHED_ENTITY_DATA__ITEMS_BY_ID("itemsById"), + CLIENTBOUND_TELEPORT_ENTITY_PACKET__X("x"), + CLIENTBOUND_TELEPORT_ENTITY_PACKET__Y("y"), + CLIENTBOUND_TELEPORT_ENTITY_PACKET__Z("z"), + PANDA__DATA_ID_FLAGS("DATA_ID_FLAGS"), + ; + + private final String mapping; + + MappingKeys1_20_6(String mapping) { + this.mapping = mapping; + } + + public String getMapping() { + return mapping; + } +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/Npc_1_20_6.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/Npc_1_20_6.java new file mode 100644 index 00000000..6127d316 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/Npc_1_20_6.java @@ -0,0 +1,395 @@ +package de.oliver.fancynpcs.v1_20_6; + +import com.google.common.collect.ImmutableList; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.datafixers.util.Pair; +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.events.NpcSpawnEvent; +import de.oliver.fancynpcs.api.utils.NpcEquipmentSlot; +import io.papermc.paper.adventure.PaperAdventure; +import net.minecraft.Optionull; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.RemoteChatSession; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ClientInformation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.*; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.scores.PlayerTeam; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.scores.Team; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.entity.Player; +import org.lushplugins.chatcolorhandler.ModernChatColorHandler; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class Npc_1_20_6 extends Npc { + + private final String localName; + private final UUID uuid; + private Entity npc; + private Display.TextDisplay sittingVehicle; + + public Npc_1_20_6(NpcData data) { + super(data); + + this.localName = generateLocalName(); + this.uuid = UUID.randomUUID(); + } + + @Override + public void create() { + MinecraftServer minecraftServer = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel serverLevel = ((CraftWorld) data.getLocation().getWorld()).getHandle(); + GameProfile gameProfile = new GameProfile(uuid, localName); + + if (data.getType() == org.bukkit.entity.EntityType.PLAYER) { + npc = new ServerPlayer(minecraftServer, serverLevel, new GameProfile(uuid, ""), ClientInformation.createDefault()); + ((ServerPlayer) npc).gameProfile = gameProfile; + } else { + EntityType nmsType = BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(data.getType().getKey())); + EntityType.EntityFactory factory = (EntityType.EntityFactory) ReflectionUtils.getValue(nmsType, MappingKeys1_20_6.ENTITY_TYPE__FACTORY.getMapping()); // EntityType.factory + npc = factory.create(nmsType, serverLevel); + isTeamCreated.clear(); + } + } + + @Override + public void spawn(Player player) { + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc == null) { + return; + } + + if (!data.getLocation().getWorld().getName().equalsIgnoreCase(serverPlayer.level().getWorld().getName())) { + return; + } + + if (data.getSkinData() != null && data.getSkinData().hasTexture()) { + String value = data.getSkinData().getTextureValue(); + String signature = data.getSkinData().getTextureSignature(); + + ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues( + "textures", + ImmutableList.of(new Property("textures", value, signature)) + ); + } + + NpcSpawnEvent spawnEvent = new NpcSpawnEvent(this, player); + spawnEvent.callEvent(); + if (spawnEvent.isCancelled()) { + return; + } + + + if (npc instanceof ServerPlayer npcPlayer) { + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, getEntry(npcPlayer, serverPlayer)); + serverPlayer.connection.send(playerInfoPacket); + + if (data.isSpawnEntity()) { + npc.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + } + } + + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket(npc); + serverPlayer.connection.send(addEntityPacket); + + isVisibleForPlayer.put(player.getUniqueId(), true); + + int removeNpcsFromPlayerlistDelay = FancyNpcsPlugin.get().getFancyNpcConfig().getRemoveNpcsFromPlayerlistDelay(); + if (!data.isShowInTab() && removeNpcsFromPlayerlistDelay > 0) { + FancyNpcsPlugin.get().getNpcThread().schedule(() -> { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of(npc.getUUID())); + serverPlayer.connection.send(playerInfoRemovePacket); + }, removeNpcsFromPlayerlistDelay, TimeUnit.MILLISECONDS); + } + + update(player); + } + + @Override + public void remove(Player player) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc instanceof ServerPlayer npcPlayer) { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of((npcPlayer.getUUID()))); + serverPlayer.connection.send(playerInfoRemovePacket); + } + + // remove entity + ClientboundRemoveEntitiesPacket removeEntitiesPacket = new ClientboundRemoveEntitiesPacket(npc.getId()); + serverPlayer.connection.send(removeEntitiesPacket); + + // remove sitting vehicle + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + + isVisibleForPlayer.put(serverPlayer.getUUID(), false); + } + + @Override + public void lookAt(Player player, Location location) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setRot(location.getYaw(), location.getPitch()); + npc.setYHeadRot(location.getYaw()); + npc.setXRot(location.getPitch()); + npc.setYRot(location.getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket(npc); + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (location.getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + } + + @Override + public void update(Player player) { + if (npc == null) { + return; + } + + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + PlayerTeam team = new PlayerTeam(new Scoreboard(), "npc-" + localName); + team.getPlayers().clear(); + team.getPlayers().add(npc instanceof ServerPlayer npcPlayer ? npcPlayer.getGameProfile().getName() : npc.getStringUUID()); + team.setColor(PaperAdventure.asVanilla(data.getGlowingColor())); + if (!data.isCollidable()) { + team.setCollisionRule(Team.CollisionRule.NEVER); + } + + net.kyori.adventure.text.Component displayName = ModernChatColorHandler.translate(data.getDisplayName(), serverPlayer.getBukkitEntity()); + Component vanillaComponent = PaperAdventure.asVanilla(displayName); + if (!(npc instanceof ServerPlayer)) { + npc.setCustomName(vanillaComponent); + npc.setCustomNameVisible(true); + } else { + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } + + if (data.getDisplayName().equalsIgnoreCase("")) { + team.setNameTagVisibility(Team.Visibility.NEVER); + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } else { + team.setNameTagVisibility(Team.Visibility.ALWAYS); + } + + if (npc instanceof ServerPlayer npcPlayer) { + team.setPlayerPrefix(vanillaComponent); + npcPlayer.listName = vanillaComponent; + + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, getEntry(npcPlayer, serverPlayer)); + serverPlayer.connection.send(playerInfoPacket); + } + + boolean isTeamCreatedForPlayer = this.isTeamCreated.getOrDefault(player.getUniqueId(), false); + serverPlayer.connection.send(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, !isTeamCreatedForPlayer)); + isTeamCreated.put(player.getUniqueId(), true); + + npc.setGlowingTag(data.isGlowing()); + + if (data.getEquipment() != null && data.getEquipment().size() > 0) { + List> equipmentList = new ArrayList<>(); + + for (NpcEquipmentSlot slot : data.getEquipment().keySet()) { + equipmentList.add(new Pair<>(EquipmentSlot.byName(slot.toNmsName()), CraftItemStack.asNMSCopy(data.getEquipment().get(slot)))); + } + + ClientboundSetEquipmentPacket setEquipmentPacket = new ClientboundSetEquipmentPacket(npc.getId(), equipmentList); + serverPlayer.connection.send(setEquipmentPacket); + } + + if (npc instanceof ServerPlayer) { + // Enable second layer of skin (https://wiki.vg/Entity_metadata#Player) + npc.getEntityData().set(net.minecraft.world.entity.player.Player.DATA_PLAYER_MODE_CUSTOMISATION, (byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40)); + } + + data.applyAllAttributes(this); + + refreshEntityData(player); + + if (data.isSpawnEntity() && data.getLocation() != null) { + move(player, true); + } + + NpcAttribute playerPoseAttr = FancyNpcsPlugin.get().getAttributeManager().getAttributeByName(org.bukkit.entity.EntityType.PLAYER, "pose"); + if (data.getAttributes().containsKey(playerPoseAttr)) { + String pose = data.getAttributes().get(playerPoseAttr); + + if (pose.equals("sitting")) { + setSitting(serverPlayer); + } else { + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + } + + } + + if (npc instanceof LivingEntity) { + Holder.Reference scaleAttribute = BuiltInRegistries.ATTRIBUTE.getHolder(new ResourceLocation("generic.scale")).get(); + AttributeInstance attributeInstance = new AttributeInstance(scaleAttribute, (a) -> { + }); + attributeInstance.setBaseValue(data.getScale()); + + ClientboundUpdateAttributesPacket updateAttributesPacket = new ClientboundUpdateAttributesPacket(npc.getId(), List.of(attributeInstance)); + serverPlayer.connection.send(updateAttributesPacket); + } + } + + @Override + protected void refreshEntityData(Player player) { + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + SynchedEntityData.DataItem[] itemsById = (SynchedEntityData.DataItem[]) ReflectionUtils.getValue(npc.getEntityData(), MappingKeys1_20_6.SYNCHED_ENTITY_DATA__ITEMS_BY_ID.getMapping()); // itemsById + List> entityData = new ArrayList<>(); + for (SynchedEntityData.DataItem dataItem : itemsById) { + entityData.add(dataItem.value()); + } + ClientboundSetEntityDataPacket setEntityDataPacket = new ClientboundSetEntityDataPacket(npc.getId(), entityData); + serverPlayer.connection.send(setEntityDataPacket); + } + + public void move(Player player, boolean swingArm) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setPosRaw(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + npc.setRot(data.getLocation().getYaw(), data.getLocation().getPitch()); + npc.setYHeadRot(data.getLocation().getYaw()); + npc.setXRot(data.getLocation().getPitch()); + npc.setYRot(data.getLocation().getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket(npc); + ReflectionUtils.setValue(teleportEntityPacket, MappingKeys1_20_6.CLIENTBOUND_TELEPORT_ENTITY_PACKET__X.getMapping(), data.getLocation().x()); // 'x' + ReflectionUtils.setValue(teleportEntityPacket, MappingKeys1_20_6.CLIENTBOUND_TELEPORT_ENTITY_PACKET__Y.getMapping(), data.getLocation().y()); // 'y' + ReflectionUtils.setValue(teleportEntityPacket, MappingKeys1_20_6.CLIENTBOUND_TELEPORT_ENTITY_PACKET__Z.getMapping(), data.getLocation().z()); // 'z' + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (data.getLocation().getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + + if (swingArm && npc instanceof ServerPlayer) { + ClientboundAnimatePacket animatePacket = new ClientboundAnimatePacket(npc, 0); + serverPlayer.connection.send(animatePacket); + } + } + + private ClientboundPlayerInfoUpdatePacket.Entry getEntry(ServerPlayer npcPlayer, ServerPlayer viewer) { + GameProfile profile = npcPlayer.getGameProfile(); + if (data.isMirrorSkin()) { + GameProfile newProfile = new GameProfile(profile.getId(), profile.getName()); + newProfile.getProperties().putAll(viewer.getGameProfile().getProperties()); + profile = newProfile; + } + + return new ClientboundPlayerInfoUpdatePacket.Entry( + npcPlayer.getUUID(), + profile, + data.isShowInTab(), + 69, + npcPlayer.gameMode.getGameModeForPlayer(), + npcPlayer.getTabListDisplayName(), + Optionull.map(npcPlayer.getChatSession(), RemoteChatSession::asData) + ); + } + + public void setSitting(ServerPlayer serverPlayer) { + if (npc == null) { + return; + } + + if (sittingVehicle == null) { + sittingVehicle = new Display.TextDisplay(EntityType.TEXT_DISPLAY, ((CraftWorld) data.getLocation().getWorld()).getHandle()); + } + + sittingVehicle.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket(sittingVehicle); + serverPlayer.connection.send(addEntityPacket); + + sittingVehicle.passengers = ImmutableList.of(npc); + + ClientboundSetPassengersPacket packet = new ClientboundSetPassengersPacket(sittingVehicle); + serverPlayer.connection.send(packet); + } + + @Override + public float getEyeHeight() { + return npc.getEyeHeight(); + } + + @Override + public int getEntityId() { + return npc.getId(); + } + + public Entity getNpc() { + return npc; + } +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/ReflectionHelper.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/ReflectionHelper.java new file mode 100644 index 00000000..7ef171bf --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/ReflectionHelper.java @@ -0,0 +1,13 @@ +package de.oliver.fancynpcs.v1_20_6; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import net.minecraft.world.entity.Entity; + +public class ReflectionHelper { + + public static T getEntity(Npc npc) { + return (T) ReflectionUtils.getValue(npc, "npc"); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/AgeableMobAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/AgeableMobAttributes.java new file mode 100644 index 00000000..657bfe70 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/AgeableMobAttributes.java @@ -0,0 +1,38 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.AgeableMob; +import org.bukkit.entity.Ageable; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AgeableMobAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "baby", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Ageable.class.isAssignableFrom(type.getEntityClass())) + .toList(), + AgeableMobAttributes::setBaby + )); + + return attributes; + } + + private static void setBaby(Npc npc, String value) { + AgeableMob mob = ReflectionHelper.getEntity(npc); + + boolean isBaby = Boolean.parseBoolean(value); + + mob.setBaby(isBaby); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/AllayAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/AllayAttributes.java new file mode 100644 index 00000000..06b14fde --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/AllayAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.animal.allay.Allay; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class AllayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.ALLAY), + AllayAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Allay allay = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + allay.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/ArmadilloAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/ArmadilloAttributes.java new file mode 100644 index 00000000..ded0beba --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/ArmadilloAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.animal.armadillo.Armadillo; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class ArmadilloAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("idle", "rolling", "unrolling", "scared"), + List.of(EntityType.ARMADILLO), + ArmadilloAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Armadillo armadillo = ReflectionHelper.getEntity(npc); + + Armadillo.ArmadilloState state = Armadillo.ArmadilloState.valueOf(value.toUpperCase()); + + armadillo.switchToState(state); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/ArmorStandAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/ArmorStandAttributes.java new file mode 100644 index 00000000..509d0cce --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/ArmorStandAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class ArmorStandAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "show_arms", + List.of("true", "false"), + List.of(EntityType.ARMOR_STAND), + ArmorStandAttributes::setShowArms + )); + + return attributes; + } + + private static void setShowArms(Npc npc, String value) { + ArmorStand armorStand = ReflectionHelper.getEntity(npc); + + boolean showArms = Boolean.parseBoolean(value.toLowerCase()); + + armorStand.setShowArms(showArms); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/Attributes_1_20_5.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/Attributes_1_20_5.java new file mode 100644 index 00000000..b5207d15 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/Attributes_1_20_5.java @@ -0,0 +1,52 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.NpcAttribute; + +import java.util.ArrayList; +import java.util.List; + +public class Attributes_1_20_5 { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.addAll(EntityAttributes.getAllAttributes()); + attributes.addAll(LivingEntityAttributes.getAllAttributes()); + attributes.addAll(AgeableMobAttributes.getAllAttributes()); + attributes.addAll(IllagerAttributes.getAllAttributes()); + attributes.addAll(SpellCasterAttributes.getAllAttributes()); + + attributes.addAll(PlayerAttributes.getAllAttributes()); + attributes.addAll(SheepAttributes.getAllAttributes()); + attributes.addAll(VillagerAttributes.getAllAttributes()); + attributes.addAll(FrogAttributes.getAllAttributes()); + attributes.addAll(HorseAttributes.getAllAttributes()); + attributes.addAll(ParrotAttributes.getAllAttributes()); + attributes.addAll(AxolotlAttributes.getAllAttributes()); + attributes.addAll(TropicalFishAttributes.getAllAttributes()); + attributes.addAll(FoxAttributes.getAllAttributes()); + attributes.addAll(PandaAttributes.getAllAttributes()); + attributes.addAll(GoatAttributes.getAllAttributes()); + attributes.addAll(AllayAttributes.getAllAttributes()); + attributes.addAll(CamelAttributes.getAllAttributes()); + attributes.addAll(RabbitAttributes.getAllAttributes()); + attributes.addAll(PiglinAttributes.getAllAttributes()); + attributes.addAll(CatAttributes.getAllAttributes()); + attributes.addAll(ShulkerAttributes.getAllAttributes()); + attributes.addAll(WolfAttributes.getAllAttributes()); + attributes.addAll(SlimeAttributes.getAllAttributes()); + attributes.addAll(PigAttributes.getAllAttributes()); + attributes.addAll(ArmorStandAttributes.getAllAttributes()); + attributes.addAll(BeeAttributes.getAllAttributes()); + attributes.addAll(VexAttributes.getAllAttributes()); + attributes.addAll(ArmadilloAttributes.getAllAttributes()); + + attributes.addAll(DisplayAttributes.getAllAttributes()); + attributes.addAll(TextDisplayAttributes.getAllAttributes()); + attributes.addAll(BlockDisplayAttributes.getAllAttributes()); + attributes.addAll(InteractionAttributes.getAllAttributes()); + + return attributes; + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/AxolotlAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/AxolotlAttributes.java new file mode 100644 index 00000000..c99b44e8 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/AxolotlAttributes.java @@ -0,0 +1,51 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.animal.axolotl.Axolotl; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AxolotlAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Axolotl.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "playing_dead", + List.of("true", "false"), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setPlayingDead + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + Axolotl.Variant variant = Axolotl.Variant.valueOf(value.toUpperCase()); + axolotl.setVariant(variant); + } + + private static void setPlayingDead(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + boolean playingDead = Boolean.parseBoolean(value); + axolotl.setPlayingDead(playingDead); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/BeeAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/BeeAttributes.java new file mode 100644 index 00000000..efa7d8af --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/BeeAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.animal.Bee; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BeeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setAngry + )); + + attributes.add(new NpcAttribute( + "sting", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setSting + )); + + attributes.add(new NpcAttribute( + "nectar", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setNectar + )); + + attributes.add(new NpcAttribute( + "rolling", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setRolling + )); + + return attributes; + } + + private static void setAngry(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRemainingPersistentAngerTime(1); + case "false" -> bee.setRemainingPersistentAngerTime(0); + } + } + + private static void setSting(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasStung(false); + case "false" -> bee.setHasStung(true); + } + } + + private static void setNectar(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasNectar(true); + case "false" -> bee.setHasNectar(false); + } + } + + private static void setRolling(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRolling(true); + case "false" -> bee.setRolling(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/BlockDisplayAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/BlockDisplayAttributes.java new file mode 100644 index 00000000..2203d567 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/BlockDisplayAttributes.java @@ -0,0 +1,41 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Display; +import net.minecraft.world.level.block.Block; +import org.bukkit.Material; +import org.bukkit.Registry; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BlockDisplayAttributes { + + private static final List BLOCKS = Registry.MATERIAL.stream().filter(Material::isBlock).map(it -> it.key().value()).toList(); + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "block", + BLOCKS, + List.of(EntityType.BLOCK_DISPLAY), + BlockDisplayAttributes::setBlock + )); + + return attributes; + } + + private static void setBlock(Npc npc, String value) { + Display.BlockDisplay display = ReflectionHelper.getEntity(npc); + + Block block = BuiltInRegistries.BLOCK.get(ResourceLocation.of("minecraft:" + value.toLowerCase(), ':')); + + display.setBlockState(block.defaultBlockState()); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/CamelAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/CamelAttributes.java new file mode 100644 index 00000000..5fb82c6b --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/CamelAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.animal.camel.Camel; +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class CamelAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "dashing"), + List.of(EntityType.CAMEL), + CamelAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Camel camel = ReflectionHelper.getEntity(npc); + + Bukkit.getScheduler().runTask(FancyNpcsPlugin.get().getPlugin(), () -> { + switch (value.toLowerCase()) { + case "standing" -> { + camel.setDashing(false); + camel.standUp(); + } + case "sitting" -> { + camel.setDashing(false); + camel.sitDown(); + } + case "dashing" -> { + camel.standUpInstantly(); + camel.setDashing(true); + } + } + }); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/CatAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/CatAttributes.java new file mode 100644 index 00000000..10bd9610 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/CatAttributes.java @@ -0,0 +1,64 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.Cat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class CatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(org.bukkit.entity.Cat.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.CAT), + CatAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.CAT), + CatAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + BuiltInRegistries.CAT_VARIANT.getHolder(ResourceLocation.of(value.toLowerCase(), ':')) + .ifPresent(cat::setVariant); + } + + private static void setPose(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + switch (value.toLowerCase()) { + case "standing" -> { + cat.setInSittingPose(false, false); + cat.setLying(false); + } + case "sleeping" -> { + cat.setInSittingPose(false, false); + cat.setLying(true); + } + case "sitting" -> { + cat.setLying(false); + cat.setOrderedToSit(true); + cat.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/DisplayAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/DisplayAttributes.java new file mode 100644 index 00000000..a2cc94dc --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/DisplayAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class DisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "billboard", + Arrays.stream(org.bukkit.entity.Display.Billboard.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TEXT_DISPLAY, EntityType.BLOCK_DISPLAY, EntityType.ITEM_DISPLAY), + DisplayAttributes::setBillboard + )); + + return attributes; + } + + private static void setBillboard(Npc npc, String value) { + Display display = ReflectionHelper.getEntity(npc); + + Display.BillboardConstraints billboard = Display.BillboardConstraints.valueOf(value.toUpperCase()); + display.setBillboardConstraints(billboard); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/EntityAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/EntityAttributes.java new file mode 100644 index 00000000..94303261 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/EntityAttributes.java @@ -0,0 +1,103 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.Entity; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class EntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "on_fire", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnFire + )); + + attributes.add(new NpcAttribute( + "invisible", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setInvisible + )); + + attributes.add(new NpcAttribute( + "silent", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setSilent + )); + + attributes.add(new NpcAttribute( + "shaking", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setShaking + )); + + attributes.add(new NpcAttribute( + "on_ground", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnGround + )); + + /*attributes.add(new NpcAttribute( + "entity_pose", + Arrays.stream(Pose.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setEntityPose + ));*/ + + return attributes; + } + + private static void setOnFire(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onFire = Boolean.parseBoolean(value); + + entity.setSharedFlagOnFire(onFire); + + } + + private static void setInvisible(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean invisible = Boolean.parseBoolean(value); + + entity.setInvisible(invisible); + } + + private static void setSilent(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean silent = Boolean.parseBoolean(value); + + entity.setSilent(silent); + } + + private static void setShaking(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean shaking = Boolean.parseBoolean(value); + + entity.setTicksFrozen(shaking ? entity.getTicksRequiredToFreeze() : 0); + } + + private static void setOnGround(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onGround = Boolean.parseBoolean(value); + + entity.setOnGround(onGround); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/FoxAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/FoxAttributes.java new file mode 100644 index 00000000..962b3283 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/FoxAttributes.java @@ -0,0 +1,66 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.animal.Fox; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FoxAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "type", + Arrays.stream(Fox.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.FOX), + FoxAttributes::setType + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.FOX), + FoxAttributes::setPose + )); + + return attributes; + } + + private static void setType(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + Fox.Type type = Fox.Type.valueOf(value.toUpperCase()); + fox.setVariant(type); + } + + private static void setPose(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + fox.setIsCrouching(false); + fox.setSleeping(false); + fox.setSitting(false, false); + } + case "sleeping" -> { + fox.setSleeping(true); + fox.setSitting(false, false); + fox.setIsCrouching(false); + } + case "sitting" -> { + fox.setSitting(true, false); + fox.setSleeping(false); + fox.setIsCrouching(false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/FrogAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/FrogAttributes.java new file mode 100644 index 00000000..a0f4c28b --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/FrogAttributes.java @@ -0,0 +1,41 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FrogAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(org.bukkit.entity.Frog.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.FROG), + FrogAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { +// Frog frog = ReflectionHelper.getEntity(npc); +// +// FrogVariant variant; +// switch (value.toUpperCase()) { +// case "COLD" -> variant = FrogVariant.COLD; +// case "WARM" -> variant = FrogVariant.WARM; +// default -> variant = FrogVariant.TEMPERATE; +// } +// +// frog.setVariant(variant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/GoatAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/GoatAttributes.java new file mode 100644 index 00000000..a1ad269e --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/GoatAttributes.java @@ -0,0 +1,44 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.animal.goat.Goat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class GoatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "horns", + List.of("none", "left", "right", "both"), + List.of(EntityType.GOAT), + GoatAttributes::setHorns + )); + + return attributes; + } + + private static void setHorns(Npc npc, String value) { + Goat goat = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "none" -> goat.removeHorns(); + case "both" -> goat.addHorns(); + case "left" -> { + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, false); + } + case "right" -> { + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/HorseAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/HorseAttributes.java new file mode 100644 index 00000000..740d8b47 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/HorseAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.animal.horse.Horse; +import net.minecraft.world.entity.animal.horse.Markings; +import net.minecraft.world.entity.animal.horse.Variant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class HorseAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "markings", + Arrays.stream(Markings.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setMarkings + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("eating", "rearing", "standing"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && (type == EntityType.HORSE || type == EntityType.DONKEY || + type == EntityType.MULE || type == EntityType.SKELETON_HORSE ||type == EntityType.ZOMBIE_HORSE)) + .toList(), + HorseAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Variant variant = Variant.valueOf(value.toUpperCase()); + horse.setVariant(variant); + } + + private static void setMarkings(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Markings markings = Markings.valueOf(value.toUpperCase()); + horse.setVariantAndMarkings(horse.getVariant(), markings); + } + + private static void setPose(Npc npc, String value) { + net.minecraft.world.entity.animal.horse.AbstractHorse horse = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + horse.setEating(false); + horse.setForceStanding(false); + } + case "rearing" -> { + horse.setForceStanding(true); + horse.setEating(false); + } + case "eating" -> { + horse.setForceStanding(false); + horse.setEating(true); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/IllagerAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/IllagerAttributes.java new file mode 100644 index 00000000..44e97207 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/IllagerAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.raid.Raider; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Illager; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class IllagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "celebrating", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Illager.class.isAssignableFrom(type.getEntityClass())) + .toList(), + IllagerAttributes::setCelebrating + )); + + return attributes; + } + + private static void setCelebrating(Npc npc, String value) { + Raider raider = ReflectionHelper.getEntity(npc); + + boolean isCelebrating = Boolean.parseBoolean(value); + + raider.setCelebrating(isCelebrating); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/InteractionAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/InteractionAttributes.java new file mode 100644 index 00000000..68ec1919 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/InteractionAttributes.java @@ -0,0 +1,60 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.Interaction; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class InteractionAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "height", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setHeight + )); + + attributes.add(new NpcAttribute( + "width", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setWidth + )); + + return attributes; + } + + private static void setHeight(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float height; + try { + height = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setHeight(height); + } + + private static void setWidth(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float width; + try { + width = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setWidth(width); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/LivingEntityAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/LivingEntityAttributes.java new file mode 100644 index 00000000..269bd352 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/LivingEntityAttributes.java @@ -0,0 +1,67 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.InteractionHand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class LivingEntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + /*attributes.add(new NpcAttribute( + "hurt", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setHurt + ));*/ + + attributes.add(new NpcAttribute( + "use_item", + List.of("main_hand", "off_hand", "none"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setUseItem + )); + + return attributes; + } + + private static void setHurt(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + boolean isHurt = Boolean.parseBoolean(value); + + if (isHurt) { + livingEntity.hurtDuration = 1; + livingEntity.hurtTime = 1; + livingEntity.hurtMarked = true; + livingEntity.animateHurt(0); + } else { + livingEntity.hurtDuration = 0; + livingEntity.hurtTime = 0; + livingEntity.hurtMarked = false; + } + } + + private static void setUseItem(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + switch (value.toUpperCase()) { + case "NONE" -> livingEntity.stopUsingItem(); + case "MAIN_HAND" -> livingEntity.startUsingItem(InteractionHand.MAIN_HAND, true); + case "OFF_HAND" -> livingEntity.startUsingItem(InteractionHand.OFF_HAND, true); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/PandaAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/PandaAttributes.java new file mode 100644 index 00000000..96d53c39 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/PandaAttributes.java @@ -0,0 +1,102 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.MappingKeys1_20_6; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.animal.Panda; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PandaAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "gene", + Arrays.stream(Panda.Gene.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PANDA), + PandaAttributes::setGene + )); + + attributes.add(new NpcAttribute( + "eating", + List.of("true", "false"), + List.of(EntityType.PANDA), + PandaAttributes::setEating + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "onBack", "rolling"), + List.of(EntityType.PANDA), + PandaAttributes::setPose + )); + + return attributes; + } + + private static void setGene(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + Panda.Gene gene = Panda.Gene.valueOf(value.toUpperCase()); + panda.setMainGene(gene); + } + + private static void setPose(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(false); + } + case "sitting" -> { + panda.roll(false); + panda.setOnBack(false); + setFlag(panda, 8, true); //sitting + } + case "onback" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(true); + } + case "rolling" -> { + setFlag(panda, 8, false); //sitting + panda.setOnBack(false); + panda.roll(true); + } + } + } + + private static void setEating(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + boolean eating = Boolean.parseBoolean(value); + + panda.eat(eating); + } + + private static void setFlag(Panda panda, int mask, boolean value) { + EntityDataAccessor DATA_ID_FLAGS = (EntityDataAccessor) ReflectionUtils.getValue(panda, MappingKeys1_20_6.PANDA__DATA_ID_FLAGS.getMapping()); + + byte b0 = panda.getEntityData().get(DATA_ID_FLAGS); + + if (value) { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 | mask)); + } else { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 & ~mask)); + } + + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/ParrotAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/ParrotAttributes.java new file mode 100644 index 00000000..8bbd0593 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/ParrotAttributes.java @@ -0,0 +1,59 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.animal.Parrot; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ParrotAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Parrot.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PARROT), + ParrotAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.PARROT), + ParrotAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + Parrot.Variant variant = Parrot.Variant.valueOf(value.toUpperCase()); + parrot.setVariant(variant); + } + + private static void setPose(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + parrot.setOrderedToSit(false); + parrot.setInSittingPose(false, false); + } + case "sitting" -> { + parrot.setOrderedToSit(true); + parrot.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/PigAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/PigAttributes.java new file mode 100644 index 00000000..a00e792a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/PigAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.animal.Pig; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PigAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "has_saddle", + List.of("true", "false"), + List.of(EntityType.PIG), + PigAttributes::setHasSaddle + )); + + return attributes; + } + + private static void setHasSaddle(Npc npc, String value) { + Pig pig = ReflectionHelper.getEntity(npc); + + boolean hasSaddle = Boolean.parseBoolean(value.toLowerCase()); + + pig.steering.setSaddle(hasSaddle); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/PiglinAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/PiglinAttributes.java new file mode 100644 index 00000000..d691df05 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/PiglinAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.monster.piglin.Piglin; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PiglinAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.PIGLIN), + PiglinAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Piglin piglin = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + piglin.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/PlayerAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/PlayerAttributes.java new file mode 100644 index 00000000..b3ca6da8 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/PlayerAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.entity.player.Player; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PlayerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "crouching", "sleeping", "swimming", "sitting"), + List.of(EntityType.PLAYER), + PlayerAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Player player = ReflectionHelper.getEntity(npc); + + Pose pose = Pose.valueOf(value.toUpperCase()); + + EntityDataAccessor DATA_POSE = (EntityDataAccessor) ReflectionUtils.getStaticValue(Entity.class, "DATA_POSE"); // DATA_POSE + player.getEntityData().set(DATA_POSE, pose); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/RabbitAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/RabbitAttributes.java new file mode 100644 index 00000000..83963a06 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/RabbitAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.animal.Rabbit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class RabbitAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Rabbit.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.RABBIT), + RabbitAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Rabbit rabbit = ReflectionHelper.getEntity(npc); + + Rabbit.Variant variant = Rabbit.Variant.valueOf(value.toUpperCase()); + rabbit.setVariant(variant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/SheepAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/SheepAttributes.java new file mode 100644 index 00000000..1dc8fbb4 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/SheepAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.animal.Sheep; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SheepAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "wool_color", + Arrays.stream(DyeColor.values()).map(dyeColor -> dyeColor.name().toLowerCase()).toList(), + List.of(EntityType.SHEEP), + SheepAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "sheared", + Arrays.asList("true", "false"), + List.of(EntityType.SHEEP), + SheepAttributes::setSheared + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + sheep.setColor(DyeColor.byName(value.toLowerCase(), DyeColor.WHITE)); + } + + private static void setSheared(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + boolean sheared = Boolean.parseBoolean(value); + + sheep.setSheared(sheared); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/ShulkerAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/ShulkerAttributes.java new file mode 100644 index 00000000..f753ba04 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/ShulkerAttributes.java @@ -0,0 +1,55 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class ShulkerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.SHULKER), + ShulkerAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "shield", + List.of("open", "closed"), + List.of(EntityType.SHULKER), + ShulkerAttributes::setShield + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.PURPLE); + shulker.setVariant(Optional.of(color)); + } + + private static void setShield(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "closed" -> shulker.setRawPeekAmount(0); + case "open" -> shulker.setRawPeekAmount(Byte.MAX_VALUE); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/SlimeAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/SlimeAttributes.java new file mode 100644 index 00000000..ca95863d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/SlimeAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.monster.Slime; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class SlimeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "size", + new ArrayList<>(), + List.of(EntityType.SLIME), + SlimeAttributes::setSize + )); + + return attributes; + } + + private static void setSize(Npc npc, String value) { + Slime slime = ReflectionHelper.getEntity(npc); + + int size; + try { + size = Integer.parseInt(value); + } catch (NumberFormatException e) { + return; + } + + slime.setSize(size, false); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/SpellCasterAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/SpellCasterAttributes.java new file mode 100644 index 00000000..d6613f80 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/SpellCasterAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.monster.SpellcasterIllager; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Spellcaster; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SpellCasterAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "casting", + Arrays.stream(SpellcasterIllager.IllagerSpell.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Spellcaster.class.isAssignableFrom(type.getEntityClass())) + .toList(), + SpellCasterAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + SpellcasterIllager spellcasterIllager = ReflectionHelper.getEntity(npc); + + SpellcasterIllager.IllagerSpell spell = SpellcasterIllager.IllagerSpell.valueOf(value); + + spellcasterIllager.setIsCastingSpell(spell); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/TextDisplayAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/TextDisplayAttributes.java new file mode 100644 index 00000000..3be3a697 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/TextDisplayAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import io.papermc.paper.adventure.PaperAdventure; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class TextDisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "text", + new ArrayList<>(), + List.of(EntityType.TEXT_DISPLAY), + TextDisplayAttributes::setText + )); + + return attributes; + } + + private static void setText(Npc npc, String value) { + Display.TextDisplay display = ReflectionHelper.getEntity(npc); + + Component text = MiniMessage.miniMessage().deserialize(value); + display.setText(PaperAdventure.asVanilla(text)); + } +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/TropicalFishAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/TropicalFishAttributes.java new file mode 100644 index 00000000..44a485f1 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/TropicalFishAttributes.java @@ -0,0 +1,72 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.animal.TropicalFish; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TropicalFishAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pattern", + Arrays.stream(TropicalFish.Pattern.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPattern + )); + + attributes.add(new NpcAttribute( + "base_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setBaseColor + )); + + attributes.add(new NpcAttribute( + "pattern_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPatternColor + )); + + return attributes; + } + + private static void setPattern(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + TropicalFish.Pattern pattern = TropicalFish.Pattern.valueOf(value.toUpperCase()); + tropicalFish.setVariant(pattern); + } + + private static void setBaseColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), color, tropicalFish.getPatternColor()); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + + private static void setPatternColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), tropicalFish.getBaseColor(), color); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/VexAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/VexAttributes.java new file mode 100644 index 00000000..01b5b331 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/VexAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.monster.Vex; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class VexAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "charging", + List.of("true", "false"), + List.of(EntityType.VEX), + VexAttributes::setCharging + )); + + return attributes; + } + + private static void setCharging(Npc npc, String value) { + Vex vex = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> vex.setIsCharging(true); + case "false" -> vex.setIsCharging(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/VillagerAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/VillagerAttributes.java new file mode 100644 index 00000000..770e0299 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/VillagerAttributes.java @@ -0,0 +1,86 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.entity.npc.VillagerType; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class VillagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "profession", + Arrays.stream(org.bukkit.entity.Villager.Profession.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setProfession + )); + + attributes.add(new NpcAttribute( + "type", + Arrays.stream(org.bukkit.entity.Villager.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setType + )); + + return attributes; + } + + private static void setProfession(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerProfession profession; + switch (value.toUpperCase()) { + case "ARMORER" -> profession = VillagerProfession.ARMORER; + case "BUTCHER" -> profession = VillagerProfession.BUTCHER; + case "CARTOGRAPHER" -> profession = VillagerProfession.CARTOGRAPHER; + case "CLERIC" -> profession = VillagerProfession.CLERIC; + case "FARMER" -> profession = VillagerProfession.FARMER; + case "FISHERMAN" -> profession = VillagerProfession.FISHERMAN; + case "FLETCHER" -> profession = VillagerProfession.FLETCHER; + case "LEATHERWORKER" -> profession = VillagerProfession.LEATHERWORKER; + case "LIBRARIAN" -> profession = VillagerProfession.LIBRARIAN; + case "MASON" -> profession = VillagerProfession.MASON; + case "NITWIT" -> profession = VillagerProfession.NITWIT; + case "SHEPHERD" -> profession = VillagerProfession.SHEPHERD; + case "TOOLSMITH" -> profession = VillagerProfession.TOOLSMITH; + case "WEAPONSMITH" -> profession = VillagerProfession.WEAPONSMITH; + + default -> profession = VillagerProfession.NONE; + } + + villager.setVillagerData(villager.getVillagerData().setProfession(profession)); + } + + private static void setType(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerType type; + switch (value.toUpperCase()) { + case "DESERT" -> type = VillagerType.DESERT; + case "JUNGLE" -> type = VillagerType.JUNGLE; + case "SAVANNA" -> type = VillagerType.SAVANNA; + case "SNOW" -> type = VillagerType.SNOW; + case "SWAMP" -> type = VillagerType.SWAMP; + case "TAIGA" -> type = VillagerType.TAIGA; + + default -> type = VillagerType.PLAINS; + } + + + villager.setVillagerData(villager.getVillagerData().setType(type)); + } + +} diff --git a/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/WolfAttributes.java b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/WolfAttributes.java new file mode 100644 index 00000000..fc5396e8 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/attributes/WolfAttributes.java @@ -0,0 +1,73 @@ +package de.oliver.fancynpcs.v1_20_6.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_20_6.ReflectionHelper; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.Wolf; +import net.minecraft.world.entity.animal.WolfVariant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class WolfAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.WOLF), + WolfAttributes::setPose + )); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.WOLF), + WolfAttributes::setAngry + )); + + attributes.add(new NpcAttribute( + "variant", + List.of("PALE", "SPOTTED", "SNOWY", "BLACK", "ASHEN", "RUSTY", "WOODS", "CHESTNUT", "STRIPED"), + List.of(EntityType.WOLF), + WolfAttributes::setVariant + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> wolf.setInSittingPose(false, false); + case "sitting" -> wolf.setInSittingPose(true, false); + } + } + + private static void setAngry(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + boolean angry = Boolean.parseBoolean(value.toLowerCase()); + + wolf.setRemainingPersistentAngerTime(angry ? 100 : 0); + } + + private static void setVariant(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + Registry registry = wolf.level().registryAccess().registry(Registries.WOLF_VARIANT).get(); + WolfVariant variant = registry.get(ResourceLocation.of(value.toLowerCase(), ':')); + + if (variant != null) { + wolf.setVariant(Holder.direct(variant)); + } + } +} diff --git a/plugins/fancynpcs/implementation_1_21_1/build.gradle.kts b/plugins/fancynpcs/implementation_1_21_1/build.gradle.kts new file mode 100644 index 00000000..41b7ee54 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("java-library") + id("io.papermc.paperweight.userdev") +} + +val minecraftVersion = "1.21.1" + +paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.MOJANG_PRODUCTION + +dependencies { + paperweight.paperDevBundle("$minecraftVersion-R0.1-SNAPSHOT") + + compileOnly(project(":plugins:fancynpcs:api")) + compileOnly("de.oliver:FancyLib:37") + compileOnly("org.lushplugins:ChatColorHandler:5.1.3") +} + + +tasks { + javadoc { + options.encoding = Charsets.UTF_8.name() + } + + compileJava { + options.encoding = Charsets.UTF_8.name() + options.release = 21 + } +} \ No newline at end of file diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/Npc_1_21_1.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/Npc_1_21_1.java new file mode 100644 index 00000000..0f89e5c9 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/Npc_1_21_1.java @@ -0,0 +1,410 @@ +package de.oliver.fancynpcs.v1_21_1; + +import com.google.common.collect.ImmutableList; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.datafixers.util.Pair; +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.events.NpcSpawnEvent; +import de.oliver.fancynpcs.api.utils.NpcEquipmentSlot; +import io.papermc.paper.adventure.PaperAdventure; +import net.minecraft.Optionull; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.RemoteChatSession; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ClientInformation; +import net.minecraft.server.level.ServerEntity; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.*; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.scores.PlayerTeam; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.scores.Team; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.entity.Player; +import org.lushplugins.chatcolorhandler.ModernChatColorHandler; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class Npc_1_21_1 extends Npc { + + private final String localName; + private final UUID uuid; + private Entity npc; + private Display.TextDisplay sittingVehicle; + + public Npc_1_21_1(NpcData data) { + super(data); + + this.localName = generateLocalName(); + this.uuid = UUID.randomUUID(); + } + + @Override + public void create() { + MinecraftServer minecraftServer = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel serverLevel = ((CraftWorld) data.getLocation().getWorld()).getHandle(); + GameProfile gameProfile = new GameProfile(uuid, localName); + + if (data.getType() == org.bukkit.entity.EntityType.PLAYER) { + npc = new ServerPlayer(minecraftServer, serverLevel, new GameProfile(uuid, ""), ClientInformation.createDefault()); + ((ServerPlayer) npc).gameProfile = gameProfile; + } else { + EntityType nmsType = BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(data.getType().getKey())); + EntityType.EntityFactory factory = (EntityType.EntityFactory) ReflectionUtils.getValue(nmsType, "factory"); // EntityType.factory + npc = factory.create(nmsType, serverLevel); + isTeamCreated.clear(); + } + } + + @Override + public void spawn(Player player) { + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc == null) { + return; + } + + if (!data.getLocation().getWorld().getName().equalsIgnoreCase(serverPlayer.level().getWorld().getName())) { + return; + } + + if (data.getSkinData() != null && data.getSkinData().hasTexture()) { + String value = data.getSkinData().getTextureValue(); + String signature = data.getSkinData().getTextureSignature(); + + ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues( + "textures", + ImmutableList.of(new Property("textures", value, signature)) + ); + } + + NpcSpawnEvent spawnEvent = new NpcSpawnEvent(this, player); + spawnEvent.callEvent(); + if (spawnEvent.isCancelled()) { + return; + } + + + if (npc instanceof ServerPlayer npcPlayer) { + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, getEntry(npcPlayer, serverPlayer)); + serverPlayer.connection.send(playerInfoPacket); + + if (data.isSpawnEntity()) { + npc.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + } + } + + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket( + npc.getId(), + npc.getUUID(), + data.getLocation().x(), + data.getLocation().y(), + data.getLocation().z(), + data.getLocation().getPitch(), + data.getLocation().getYaw(), + npc.getType(), + 0, + Vec3.ZERO, + data.getLocation().getYaw() + ); + serverPlayer.connection.send(addEntityPacket); + + isVisibleForPlayer.put(player.getUniqueId(), true); + + + int removeNpcsFromPlayerlistDelay = FancyNpcsPlugin.get().getFancyNpcConfig().getRemoveNpcsFromPlayerlistDelay(); + if (!data.isShowInTab() && removeNpcsFromPlayerlistDelay > 0) { + FancyNpcsPlugin.get().getNpcThread().schedule(() -> { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of(npc.getUUID())); + serverPlayer.connection.send(playerInfoRemovePacket); + }, removeNpcsFromPlayerlistDelay, TimeUnit.MILLISECONDS); + } + + update(player); + } + + @Override + public void remove(Player player) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc instanceof ServerPlayer npcPlayer) { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of((npcPlayer.getUUID()))); + serverPlayer.connection.send(playerInfoRemovePacket); + } + + // remove entity + ClientboundRemoveEntitiesPacket removeEntitiesPacket = new ClientboundRemoveEntitiesPacket(npc.getId()); + serverPlayer.connection.send(removeEntitiesPacket); + + // remove sitting vehicle + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + + isVisibleForPlayer.put(serverPlayer.getUUID(), false); + } + + @Override + public void lookAt(Player player, Location location) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setRot(location.getYaw(), location.getPitch()); + npc.setYHeadRot(location.getYaw()); + npc.setXRot(location.getPitch()); + npc.setYRot(location.getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket(npc); + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (location.getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + } + + @Override + public void update(Player player) { + if (npc == null) { + return; + } + + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + PlayerTeam team = new PlayerTeam(new Scoreboard(), "npc-" + localName); + team.getPlayers().clear(); + team.getPlayers().add(npc instanceof ServerPlayer npcPlayer ? npcPlayer.getGameProfile().getName() : npc.getStringUUID()); + team.setColor(PaperAdventure.asVanilla(data.getGlowingColor())); + if (!data.isCollidable()) { + team.setCollisionRule(Team.CollisionRule.NEVER); + } + + net.kyori.adventure.text.Component displayName = ModernChatColorHandler.translate(data.getDisplayName(), serverPlayer.getBukkitEntity()); + Component vanillaComponent = PaperAdventure.asVanilla(displayName); + if (!(npc instanceof ServerPlayer)) { + npc.setCustomName(vanillaComponent); + npc.setCustomNameVisible(true); + } else { + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } + + if (data.getDisplayName().equalsIgnoreCase("")) { + team.setNameTagVisibility(Team.Visibility.NEVER); + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } else { + team.setNameTagVisibility(Team.Visibility.ALWAYS); + } + + if (npc instanceof ServerPlayer npcPlayer) { + team.setPlayerPrefix(vanillaComponent); + npcPlayer.listName = vanillaComponent; + + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, getEntry(npcPlayer, serverPlayer)); + serverPlayer.connection.send(playerInfoPacket); + } + + boolean isTeamCreatedForPlayer = this.isTeamCreated.getOrDefault(player.getUniqueId(), false); + serverPlayer.connection.send(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, !isTeamCreatedForPlayer)); + isTeamCreated.put(player.getUniqueId(), true); + + npc.setGlowingTag(data.isGlowing()); + + if (data.getEquipment() != null && data.getEquipment().size() > 0) { + List> equipmentList = new ArrayList<>(); + + for (NpcEquipmentSlot slot : data.getEquipment().keySet()) { + equipmentList.add(new Pair<>(EquipmentSlot.byName(slot.toNmsName()), CraftItemStack.asNMSCopy(data.getEquipment().get(slot)))); + } + + ClientboundSetEquipmentPacket setEquipmentPacket = new ClientboundSetEquipmentPacket(npc.getId(), equipmentList); + serverPlayer.connection.send(setEquipmentPacket); + } + + if (npc instanceof ServerPlayer) { + // Enable second layer of skin (https://wiki.vg/Entity_metadata#Player) + npc.getEntityData().set(net.minecraft.world.entity.player.Player.DATA_PLAYER_MODE_CUSTOMISATION, (byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40)); + } + + data.applyAllAttributes(this); + + refreshEntityData(player); + + if (data.isSpawnEntity() && data.getLocation() != null) { + move(player, true); + } + + NpcAttribute playerPoseAttr = FancyNpcsPlugin.get().getAttributeManager().getAttributeByName(org.bukkit.entity.EntityType.PLAYER, "pose"); + if (data.getAttributes().containsKey(playerPoseAttr)) { + String pose = data.getAttributes().get(playerPoseAttr); + + if (pose.equals("sitting")) { + setSitting(serverPlayer); + } else { + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + } + + } + + if (npc instanceof LivingEntity) { + Holder.Reference scaleAttribute = BuiltInRegistries.ATTRIBUTE.getHolder(ResourceLocation.parse("generic.scale")).get(); + AttributeInstance attributeInstance = new AttributeInstance(scaleAttribute, (a) -> { + }); + attributeInstance.setBaseValue(data.getScale()); + + ClientboundUpdateAttributesPacket updateAttributesPacket = new ClientboundUpdateAttributesPacket(npc.getId(), List.of(attributeInstance)); + serverPlayer.connection.send(updateAttributesPacket); + + } + } + + @Override + protected void refreshEntityData(Player player) { + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + SynchedEntityData.DataItem[] itemsById = (SynchedEntityData.DataItem[]) ReflectionUtils.getValue(npc.getEntityData(), "itemsById"); // itemsById + List> entityData = new ArrayList<>(); + for (SynchedEntityData.DataItem dataItem : itemsById) { + entityData.add(dataItem.value()); + } + ClientboundSetEntityDataPacket setEntityDataPacket = new ClientboundSetEntityDataPacket(npc.getId(), entityData); + serverPlayer.connection.send(setEntityDataPacket); + } + + public void move(Player player, boolean swingArm) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setPosRaw(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + npc.setRot(data.getLocation().getYaw(), data.getLocation().getPitch()); + npc.setYHeadRot(data.getLocation().getYaw()); + npc.setXRot(data.getLocation().getPitch()); + npc.setYRot(data.getLocation().getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket(npc); + ReflectionUtils.setValue(teleportEntityPacket, "x", data.getLocation().x()); // 'x' + ReflectionUtils.setValue(teleportEntityPacket, "y", data.getLocation().y()); // 'y' + ReflectionUtils.setValue(teleportEntityPacket, "z", data.getLocation().z()); // 'z' + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (data.getLocation().getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + + if (swingArm && npc instanceof ServerPlayer) { + ClientboundAnimatePacket animatePacket = new ClientboundAnimatePacket(npc, 0); + serverPlayer.connection.send(animatePacket); + } + } + + private ClientboundPlayerInfoUpdatePacket.Entry getEntry(ServerPlayer npcPlayer, ServerPlayer viewer) { + GameProfile profile = npcPlayer.getGameProfile(); + if (data.isMirrorSkin()) { + GameProfile newProfile = new GameProfile(profile.getId(), profile.getName()); + newProfile.getProperties().putAll(viewer.getGameProfile().getProperties()); + profile = newProfile; + } + + return new ClientboundPlayerInfoUpdatePacket.Entry( + npcPlayer.getUUID(), + profile, + data.isShowInTab(), + 69, + npcPlayer.gameMode.getGameModeForPlayer(), + npcPlayer.getTabListDisplayName(), + Optionull.map(npcPlayer.getChatSession(), RemoteChatSession::asData) + ); + } + + public void setSitting(ServerPlayer serverPlayer) { + if (npc == null) { + return; + } + + if (sittingVehicle == null) { + sittingVehicle = new Display.TextDisplay(EntityType.TEXT_DISPLAY, ((CraftWorld) data.getLocation().getWorld()).getHandle()); + } + + sittingVehicle.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + + ServerEntity serverEntity = new ServerEntity(serverPlayer.serverLevel(), sittingVehicle, 0, false, packet -> { + }, Set.of()); + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket(sittingVehicle, serverEntity); + serverPlayer.connection.send(addEntityPacket); + + sittingVehicle.passengers = ImmutableList.of(npc); + + ClientboundSetPassengersPacket packet = new ClientboundSetPassengersPacket(sittingVehicle); + serverPlayer.connection.send(packet); + } + + @Override + public float getEyeHeight() { + return npc.getEyeHeight(); + } + + @Override + public int getEntityId() { + return npc.getId(); + } + + public Entity getNpc() { + return npc; + } +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/ReflectionHelper.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/ReflectionHelper.java new file mode 100644 index 00000000..8e370ee8 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/ReflectionHelper.java @@ -0,0 +1,13 @@ +package de.oliver.fancynpcs.v1_21_1; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import net.minecraft.world.entity.Entity; + +public class ReflectionHelper { + + public static T getEntity(Npc npc) { + return (T) ReflectionUtils.getValue(npc, "npc"); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/AgeableMobAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/AgeableMobAttributes.java new file mode 100644 index 00000000..4c2979d1 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/AgeableMobAttributes.java @@ -0,0 +1,38 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.AgeableMob; +import org.bukkit.entity.Ageable; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AgeableMobAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "baby", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Ageable.class.isAssignableFrom(type.getEntityClass())) + .toList(), + AgeableMobAttributes::setBaby + )); + + return attributes; + } + + private static void setBaby(Npc npc, String value) { + AgeableMob mob = ReflectionHelper.getEntity(npc); + + boolean isBaby = Boolean.parseBoolean(value); + + mob.setBaby(isBaby); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/AllayAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/AllayAttributes.java new file mode 100644 index 00000000..e2c16845 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/AllayAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.animal.allay.Allay; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class AllayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.ALLAY), + AllayAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Allay allay = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + allay.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/ArmadilloAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/ArmadilloAttributes.java new file mode 100644 index 00000000..71d81651 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/ArmadilloAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.animal.armadillo.Armadillo; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class ArmadilloAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("idle", "rolling", "unrolling", "scared"), + List.of(EntityType.ARMADILLO), + ArmadilloAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Armadillo armadillo = ReflectionHelper.getEntity(npc); + + Armadillo.ArmadilloState state = Armadillo.ArmadilloState.valueOf(value.toUpperCase()); + + armadillo.switchToState(state); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/ArmorStandAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/ArmorStandAttributes.java new file mode 100644 index 00000000..b958e94a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/ArmorStandAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class ArmorStandAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "show_arms", + List.of("true", "false"), + List.of(EntityType.ARMOR_STAND), + ArmorStandAttributes::setShowArms + )); + + return attributes; + } + + private static void setShowArms(Npc npc, String value) { + ArmorStand armorStand = ReflectionHelper.getEntity(npc); + + boolean showArms = Boolean.parseBoolean(value.toLowerCase()); + + armorStand.setShowArms(showArms); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/Attributes_1_21_1.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/Attributes_1_21_1.java new file mode 100644 index 00000000..17ffa0a7 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/Attributes_1_21_1.java @@ -0,0 +1,52 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.NpcAttribute; + +import java.util.ArrayList; +import java.util.List; + +public class Attributes_1_21_1 { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.addAll(EntityAttributes.getAllAttributes()); + attributes.addAll(LivingEntityAttributes.getAllAttributes()); + attributes.addAll(AgeableMobAttributes.getAllAttributes()); + attributes.addAll(IllagerAttributes.getAllAttributes()); + attributes.addAll(SpellCasterAttributes.getAllAttributes()); + + attributes.addAll(PlayerAttributes.getAllAttributes()); + attributes.addAll(SheepAttributes.getAllAttributes()); + attributes.addAll(VillagerAttributes.getAllAttributes()); + attributes.addAll(FrogAttributes.getAllAttributes()); + attributes.addAll(HorseAttributes.getAllAttributes()); + attributes.addAll(ParrotAttributes.getAllAttributes()); + attributes.addAll(AxolotlAttributes.getAllAttributes()); + attributes.addAll(TropicalFishAttributes.getAllAttributes()); + attributes.addAll(FoxAttributes.getAllAttributes()); + attributes.addAll(PandaAttributes.getAllAttributes()); + attributes.addAll(GoatAttributes.getAllAttributes()); + attributes.addAll(AllayAttributes.getAllAttributes()); + attributes.addAll(CamelAttributes.getAllAttributes()); + attributes.addAll(RabbitAttributes.getAllAttributes()); + attributes.addAll(PiglinAttributes.getAllAttributes()); + attributes.addAll(CatAttributes.getAllAttributes()); + attributes.addAll(ShulkerAttributes.getAllAttributes()); + attributes.addAll(WolfAttributes.getAllAttributes()); + attributes.addAll(SlimeAttributes.getAllAttributes()); + attributes.addAll(PigAttributes.getAllAttributes()); + attributes.addAll(ArmorStandAttributes.getAllAttributes()); + attributes.addAll(BeeAttributes.getAllAttributes()); + attributes.addAll(VexAttributes.getAllAttributes()); + attributes.addAll(ArmadilloAttributes.getAllAttributes()); + + attributes.addAll(DisplayAttributes.getAllAttributes()); + attributes.addAll(TextDisplayAttributes.getAllAttributes()); + attributes.addAll(BlockDisplayAttributes.getAllAttributes()); + attributes.addAll(InteractionAttributes.getAllAttributes()); + + return attributes; + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/AxolotlAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/AxolotlAttributes.java new file mode 100644 index 00000000..d822fe16 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/AxolotlAttributes.java @@ -0,0 +1,51 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.animal.axolotl.Axolotl; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AxolotlAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Axolotl.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "playing_dead", + List.of("true", "false"), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setPlayingDead + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + Axolotl.Variant variant = Axolotl.Variant.valueOf(value.toUpperCase()); + axolotl.setVariant(variant); + } + + private static void setPlayingDead(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + boolean playingDead = Boolean.parseBoolean(value); + axolotl.setPlayingDead(playingDead); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/BeeAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/BeeAttributes.java new file mode 100644 index 00000000..36644d13 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/BeeAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.animal.Bee; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BeeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setAngry + )); + + attributes.add(new NpcAttribute( + "sting", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setSting + )); + + attributes.add(new NpcAttribute( + "nectar", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setNectar + )); + + attributes.add(new NpcAttribute( + "rolling", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setRolling + )); + + return attributes; + } + + private static void setAngry(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRemainingPersistentAngerTime(1); + case "false" -> bee.setRemainingPersistentAngerTime(0); + } + } + + private static void setSting(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasStung(false); + case "false" -> bee.setHasStung(true); + } + } + + private static void setNectar(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasNectar(true); + case "false" -> bee.setHasNectar(false); + } + } + + private static void setRolling(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRolling(true); + case "false" -> bee.setRolling(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/BlockDisplayAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/BlockDisplayAttributes.java new file mode 100644 index 00000000..3d459a71 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/BlockDisplayAttributes.java @@ -0,0 +1,41 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Display; +import net.minecraft.world.level.block.Block; +import org.bukkit.Material; +import org.bukkit.Registry; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BlockDisplayAttributes { + + private static final List BLOCKS = Registry.MATERIAL.stream().filter(Material::isBlock).map(it -> it.key().value()).toList(); + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "block", + BLOCKS, + List.of(EntityType.BLOCK_DISPLAY), + BlockDisplayAttributes::setBlock + )); + + return attributes; + } + + private static void setBlock(Npc npc, String value) { + Display.BlockDisplay display = ReflectionHelper.getEntity(npc); + + Block block = BuiltInRegistries.BLOCK.get(ResourceLocation.parse(value.toLowerCase())); + + display.setBlockState(block.defaultBlockState()); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/CamelAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/CamelAttributes.java new file mode 100644 index 00000000..fb2c433a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/CamelAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.animal.camel.Camel; +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class CamelAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "dashing"), + List.of(EntityType.CAMEL), + CamelAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Camel camel = ReflectionHelper.getEntity(npc); + + Bukkit.getScheduler().runTask(FancyNpcsPlugin.get().getPlugin(), () -> { + switch (value.toLowerCase()) { + case "standing" -> { + camel.setDashing(false); + camel.standUp(); + } + case "sitting" -> { + camel.setDashing(false); + camel.sitDown(); + } + case "dashing" -> { + camel.standUpInstantly(); + camel.setDashing(true); + } + } + }); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/CatAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/CatAttributes.java new file mode 100644 index 00000000..340d8366 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/CatAttributes.java @@ -0,0 +1,61 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.Cat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class CatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + BuiltInRegistries.CAT_VARIANT.keySet().stream().map(ResourceLocation::getPath).toList(), + List.of(EntityType.CAT), + CatAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.CAT), + CatAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + BuiltInRegistries.CAT_VARIANT.getHolder(ResourceLocation.parse(value.toLowerCase())) + .ifPresent(cat::setVariant); + } + + private static void setPose(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + switch (value.toLowerCase()) { + case "standing" -> { + cat.setInSittingPose(false, false); + cat.setLying(false); + } + case "sleeping" -> { + cat.setInSittingPose(false, false); + cat.setLying(true); + } + case "sitting" -> { + cat.setLying(false); + cat.setOrderedToSit(true); + cat.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/DisplayAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/DisplayAttributes.java new file mode 100644 index 00000000..e787b51a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/DisplayAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class DisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "billboard", + Arrays.stream(org.bukkit.entity.Display.Billboard.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TEXT_DISPLAY, EntityType.BLOCK_DISPLAY, EntityType.ITEM_DISPLAY), + DisplayAttributes::setBillboard + )); + + return attributes; + } + + private static void setBillboard(Npc npc, String value) { + Display display = ReflectionHelper.getEntity(npc); + + Display.BillboardConstraints billboard = Display.BillboardConstraints.valueOf(value.toUpperCase()); + display.setBillboardConstraints(billboard); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/EntityAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/EntityAttributes.java new file mode 100644 index 00000000..85ba6723 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/EntityAttributes.java @@ -0,0 +1,103 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.Entity; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class EntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "on_fire", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnFire + )); + + attributes.add(new NpcAttribute( + "invisible", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setInvisible + )); + + attributes.add(new NpcAttribute( + "silent", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setSilent + )); + + attributes.add(new NpcAttribute( + "shaking", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setShaking + )); + + attributes.add(new NpcAttribute( + "on_ground", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnGround + )); + + /*attributes.add(new NpcAttribute( + "entity_pose", + Arrays.stream(Pose.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setEntityPose + ));*/ + + return attributes; + } + + private static void setOnFire(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onFire = Boolean.parseBoolean(value); + + entity.setSharedFlagOnFire(onFire); + + } + + private static void setInvisible(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean invisible = Boolean.parseBoolean(value); + + entity.setInvisible(invisible); + } + + private static void setSilent(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean silent = Boolean.parseBoolean(value); + + entity.setSilent(silent); + } + + private static void setShaking(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean shaking = Boolean.parseBoolean(value); + + entity.setTicksFrozen(shaking ? entity.getTicksRequiredToFreeze() : 0); + } + + private static void setOnGround(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onGround = Boolean.parseBoolean(value); + + entity.setOnGround(onGround); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/FoxAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/FoxAttributes.java new file mode 100644 index 00000000..af46cd0c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/FoxAttributes.java @@ -0,0 +1,66 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.animal.Fox; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FoxAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "type", + Arrays.stream(Fox.Type.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.FOX), + FoxAttributes::setType + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.FOX), + FoxAttributes::setPose + )); + + return attributes; + } + + private static void setType(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + Fox.Type type = Fox.Type.valueOf(value.toUpperCase()); + fox.setVariant(type); + } + + private static void setPose(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + fox.setIsCrouching(false); + fox.setSleeping(false); + fox.setSitting(false, false); + } + case "sleeping" -> { + fox.setSleeping(true); + fox.setSitting(false, false); + fox.setIsCrouching(false); + } + case "sitting" -> { + fox.setSitting(true, false); + fox.setSleeping(false); + fox.setIsCrouching(false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/FrogAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/FrogAttributes.java new file mode 100644 index 00000000..532c5df7 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/FrogAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.frog.Frog; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class FrogAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + BuiltInRegistries.FROG_VARIANT.keySet().stream().map(ResourceLocation::getPath).toList(), + List.of(EntityType.FROG), + FrogAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Frog frog = ReflectionHelper.getEntity(npc); + BuiltInRegistries.FROG_VARIANT.getHolder(ResourceLocation.parse(value.toLowerCase())) + .ifPresent(frog::setVariant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/GoatAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/GoatAttributes.java new file mode 100644 index 00000000..1ad49a28 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/GoatAttributes.java @@ -0,0 +1,44 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.animal.goat.Goat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class GoatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "horns", + List.of("none", "left", "right", "both"), + List.of(EntityType.GOAT), + GoatAttributes::setHorns + )); + + return attributes; + } + + private static void setHorns(Npc npc, String value) { + Goat goat = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "none" -> goat.removeHorns(); + case "both" -> goat.addHorns(); + case "left" -> { + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, false); + } + case "right" -> { + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/HorseAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/HorseAttributes.java new file mode 100644 index 00000000..efa5b23c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/HorseAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.animal.horse.Horse; +import net.minecraft.world.entity.animal.horse.Markings; +import net.minecraft.world.entity.animal.horse.Variant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class HorseAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "markings", + Arrays.stream(Markings.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setMarkings + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("eating", "rearing", "standing"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && (type == EntityType.HORSE || type == EntityType.DONKEY || + type == EntityType.MULE || type == EntityType.SKELETON_HORSE || type == EntityType.ZOMBIE_HORSE)) + .toList(), + HorseAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Variant variant = Variant.valueOf(value.toUpperCase()); + horse.setVariant(variant); + } + + private static void setMarkings(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Markings markings = Markings.valueOf(value.toUpperCase()); + horse.setVariantAndMarkings(horse.getVariant(), markings); + } + + private static void setPose(Npc npc, String value) { + net.minecraft.world.entity.animal.horse.AbstractHorse horse = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + horse.setEating(false); + horse.setForceStanding(false); + } + case "rearing" -> { + horse.setForceStanding(true); + horse.setEating(false); + } + case "eating" -> { + horse.setForceStanding(false); + horse.setEating(true); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/IllagerAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/IllagerAttributes.java new file mode 100644 index 00000000..6b7e08bd --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/IllagerAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.raid.Raider; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Illager; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class IllagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "celebrating", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Illager.class.isAssignableFrom(type.getEntityClass())) + .toList(), + IllagerAttributes::setCelebrating + )); + + return attributes; + } + + private static void setCelebrating(Npc npc, String value) { + Raider raider = ReflectionHelper.getEntity(npc); + + boolean isCelebrating = Boolean.parseBoolean(value); + + raider.setCelebrating(isCelebrating); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/InteractionAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/InteractionAttributes.java new file mode 100644 index 00000000..28646a6d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/InteractionAttributes.java @@ -0,0 +1,60 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.Interaction; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class InteractionAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "height", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setHeight + )); + + attributes.add(new NpcAttribute( + "width", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setWidth + )); + + return attributes; + } + + private static void setHeight(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float height; + try { + height = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setHeight(height); + } + + private static void setWidth(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float width; + try { + width = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setWidth(width); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/LivingEntityAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/LivingEntityAttributes.java new file mode 100644 index 00000000..933f6e61 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/LivingEntityAttributes.java @@ -0,0 +1,67 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.InteractionHand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class LivingEntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + /*attributes.add(new NpcAttribute( + "hurt", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setHurt + ));*/ + + attributes.add(new NpcAttribute( + "use_item", + List.of("main_hand", "off_hand", "none"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setUseItem + )); + + return attributes; + } + + private static void setHurt(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + boolean isHurt = Boolean.parseBoolean(value); + + if (isHurt) { + livingEntity.hurtDuration = 1; + livingEntity.hurtTime = 1; + livingEntity.hurtMarked = true; + livingEntity.animateHurt(0); + } else { + livingEntity.hurtDuration = 0; + livingEntity.hurtTime = 0; + livingEntity.hurtMarked = false; + } + } + + private static void setUseItem(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + switch (value.toUpperCase()) { + case "NONE" -> livingEntity.stopUsingItem(); + case "MAIN_HAND" -> livingEntity.startUsingItem(InteractionHand.MAIN_HAND, true); + case "OFF_HAND" -> livingEntity.startUsingItem(InteractionHand.OFF_HAND, true); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/PandaAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/PandaAttributes.java new file mode 100644 index 00000000..ca9c50c2 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/PandaAttributes.java @@ -0,0 +1,101 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.animal.Panda; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PandaAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "gene", + Arrays.stream(Panda.Gene.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PANDA), + PandaAttributes::setGene + )); + + attributes.add(new NpcAttribute( + "eating", + List.of("true", "false"), + List.of(EntityType.PANDA), + PandaAttributes::setEating + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "onBack", "rolling"), + List.of(EntityType.PANDA), + PandaAttributes::setPose + )); + + return attributes; + } + + private static void setGene(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + Panda.Gene gene = Panda.Gene.valueOf(value.toUpperCase()); + panda.setMainGene(gene); + } + + private static void setPose(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(false); + } + case "sitting" -> { + panda.roll(false); + panda.setOnBack(false); + setFlag(panda, 8, true); //sitting + } + case "onback" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(true); + } + case "rolling" -> { + setFlag(panda, 8, false); //sitting + panda.setOnBack(false); + panda.roll(true); + } + } + } + + private static void setEating(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + boolean eating = Boolean.parseBoolean(value); + + panda.eat(eating); + } + + private static void setFlag(Panda panda, int mask, boolean value) { + EntityDataAccessor DATA_ID_FLAGS = (EntityDataAccessor) ReflectionUtils.getValue(panda, "DATA_ID_FLAGS"); + + byte b0 = panda.getEntityData().get(DATA_ID_FLAGS); + + if (value) { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 | mask)); + } else { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 & ~mask)); + } + + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/ParrotAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/ParrotAttributes.java new file mode 100644 index 00000000..d3f355a4 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/ParrotAttributes.java @@ -0,0 +1,59 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.animal.Parrot; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ParrotAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Parrot.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PARROT), + ParrotAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.PARROT), + ParrotAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + Parrot.Variant variant = Parrot.Variant.valueOf(value.toUpperCase()); + parrot.setVariant(variant); + } + + private static void setPose(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + parrot.setOrderedToSit(false); + parrot.setInSittingPose(false, false); + } + case "sitting" -> { + parrot.setOrderedToSit(true); + parrot.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/PigAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/PigAttributes.java new file mode 100644 index 00000000..53b5946b --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/PigAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.animal.Pig; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PigAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "has_saddle", + List.of("true", "false"), + List.of(EntityType.PIG), + PigAttributes::setHasSaddle + )); + + return attributes; + } + + private static void setHasSaddle(Npc npc, String value) { + Pig pig = ReflectionHelper.getEntity(npc); + + boolean hasSaddle = Boolean.parseBoolean(value.toLowerCase()); + + pig.steering.setSaddle(hasSaddle); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/PiglinAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/PiglinAttributes.java new file mode 100644 index 00000000..a6fd80b2 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/PiglinAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.monster.piglin.Piglin; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PiglinAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.PIGLIN), + PiglinAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Piglin piglin = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + piglin.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/PlayerAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/PlayerAttributes.java new file mode 100644 index 00000000..9baa88ab --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/PlayerAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.entity.player.Player; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PlayerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "crouching", "sleeping", "swimming", "sitting"), + List.of(EntityType.PLAYER), + PlayerAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Player player = ReflectionHelper.getEntity(npc); + + Pose pose = Pose.valueOf(value.toUpperCase()); + + EntityDataAccessor DATA_POSE = (EntityDataAccessor) ReflectionUtils.getStaticValue(Entity.class, "DATA_POSE"); // DATA_POSE + player.getEntityData().set(DATA_POSE, pose); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/RabbitAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/RabbitAttributes.java new file mode 100644 index 00000000..76cf652e --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/RabbitAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.animal.Rabbit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class RabbitAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Rabbit.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.RABBIT), + RabbitAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Rabbit rabbit = ReflectionHelper.getEntity(npc); + + Rabbit.Variant variant = Rabbit.Variant.valueOf(value.toUpperCase()); + rabbit.setVariant(variant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/SheepAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/SheepAttributes.java new file mode 100644 index 00000000..39b8e272 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/SheepAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.animal.Sheep; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SheepAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "wool_color", + Arrays.stream(DyeColor.values()).map(dyeColor -> dyeColor.name().toLowerCase()).toList(), + List.of(EntityType.SHEEP), + SheepAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "sheared", + Arrays.asList("true", "false"), + List.of(EntityType.SHEEP), + SheepAttributes::setSheared + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + sheep.setColor(DyeColor.byName(value.toLowerCase(), DyeColor.WHITE)); + } + + private static void setSheared(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + boolean sheared = Boolean.parseBoolean(value); + + sheep.setSheared(sheared); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/ShulkerAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/ShulkerAttributes.java new file mode 100644 index 00000000..d0a22d23 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/ShulkerAttributes.java @@ -0,0 +1,55 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class ShulkerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.SHULKER), + ShulkerAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "shield", + List.of("open", "closed"), + List.of(EntityType.SHULKER), + ShulkerAttributes::setShield + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.PURPLE); + shulker.setVariant(Optional.of(color)); + } + + private static void setShield(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "closed" -> shulker.setRawPeekAmount(0); + case "open" -> shulker.setRawPeekAmount(Byte.MAX_VALUE); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/SlimeAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/SlimeAttributes.java new file mode 100644 index 00000000..4819bd3a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/SlimeAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.monster.Slime; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class SlimeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "size", + new ArrayList<>(), + List.of(EntityType.SLIME), + SlimeAttributes::setSize + )); + + return attributes; + } + + private static void setSize(Npc npc, String value) { + Slime slime = ReflectionHelper.getEntity(npc); + + int size; + try { + size = Integer.parseInt(value); + } catch (NumberFormatException e) { + return; + } + + slime.setSize(size, false); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/SpellCasterAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/SpellCasterAttributes.java new file mode 100644 index 00000000..89db6e4f --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/SpellCasterAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.monster.SpellcasterIllager; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Spellcaster; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SpellCasterAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "casting", + Arrays.stream(SpellcasterIllager.IllagerSpell.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Spellcaster.class.isAssignableFrom(type.getEntityClass())) + .toList(), + SpellCasterAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + SpellcasterIllager spellcasterIllager = ReflectionHelper.getEntity(npc); + + SpellcasterIllager.IllagerSpell spell = SpellcasterIllager.IllagerSpell.valueOf(value); + + spellcasterIllager.setIsCastingSpell(spell); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/TextDisplayAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/TextDisplayAttributes.java new file mode 100644 index 00000000..0d8a87c9 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/TextDisplayAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import io.papermc.paper.adventure.PaperAdventure; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class TextDisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "text", + new ArrayList<>(), + List.of(EntityType.TEXT_DISPLAY), + TextDisplayAttributes::setText + )); + + return attributes; + } + + private static void setText(Npc npc, String value) { + Display.TextDisplay display = ReflectionHelper.getEntity(npc); + + Component text = MiniMessage.miniMessage().deserialize(value); + display.setText(PaperAdventure.asVanilla(text)); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/TropicalFishAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/TropicalFishAttributes.java new file mode 100644 index 00000000..55d76920 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/TropicalFishAttributes.java @@ -0,0 +1,72 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.animal.TropicalFish; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TropicalFishAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pattern", + Arrays.stream(TropicalFish.Pattern.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPattern + )); + + attributes.add(new NpcAttribute( + "base_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setBaseColor + )); + + attributes.add(new NpcAttribute( + "pattern_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPatternColor + )); + + return attributes; + } + + private static void setPattern(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + TropicalFish.Pattern pattern = TropicalFish.Pattern.valueOf(value.toUpperCase()); + tropicalFish.setVariant(pattern); + } + + private static void setBaseColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), color, tropicalFish.getPatternColor()); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + + private static void setPatternColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), tropicalFish.getBaseColor(), color); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/VexAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/VexAttributes.java new file mode 100644 index 00000000..16254e60 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/VexAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.world.entity.monster.Vex; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class VexAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "charging", + List.of("true", "false"), + List.of(EntityType.VEX), + VexAttributes::setCharging + )); + + return attributes; + } + + private static void setCharging(Npc npc, String value) { + Vex vex = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> vex.setIsCharging(true); + case "false" -> vex.setIsCharging(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/VillagerAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/VillagerAttributes.java new file mode 100644 index 00000000..6a568ba5 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/VillagerAttributes.java @@ -0,0 +1,54 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.entity.npc.VillagerType; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class VillagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "profession", + BuiltInRegistries.VILLAGER_PROFESSION.keySet().stream().map(ResourceLocation::getPath).toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setProfession + )); + + attributes.add(new NpcAttribute( + "type", + BuiltInRegistries.VILLAGER_TYPE.keySet().stream().map(ResourceLocation::getPath).toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setType + )); + + return attributes; + } + + private static void setProfession(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerProfession profession = BuiltInRegistries.VILLAGER_PROFESSION.get(ResourceLocation.tryParse(value)); + + villager.setVillagerData(villager.getVillagerData().setProfession(profession)); + } + + private static void setType(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerType type = BuiltInRegistries.VILLAGER_TYPE.get(ResourceLocation.tryParse(value)); + + villager.setVillagerData(villager.getVillagerData().setType(type)); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/WolfAttributes.java b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/WolfAttributes.java new file mode 100644 index 00000000..7b240e33 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_1/src/main/java/de/oliver/fancynpcs/v1_21_1/attributes/WolfAttributes.java @@ -0,0 +1,73 @@ +package de.oliver.fancynpcs.v1_21_1.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_1.ReflectionHelper; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.Wolf; +import net.minecraft.world.entity.animal.WolfVariant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class WolfAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.WOLF), + WolfAttributes::setPose + )); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.WOLF), + WolfAttributes::setAngry + )); + + attributes.add(new NpcAttribute( + "variant", + List.of("PALE", "SPOTTED", "SNOWY", "BLACK", "ASHEN", "RUSTY", "WOODS", "CHESTNUT", "STRIPED"), + List.of(EntityType.WOLF), + WolfAttributes::setVariant + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> wolf.setInSittingPose(false, false); + case "sitting" -> wolf.setInSittingPose(true, false); + } + } + + private static void setAngry(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + boolean angry = Boolean.parseBoolean(value.toLowerCase()); + + wolf.setRemainingPersistentAngerTime(angry ? 100 : 0); + } + + private static void setVariant(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + Registry registry = wolf.level().registryAccess().registry(Registries.WOLF_VARIANT).get(); + WolfVariant variant = registry.get(ResourceLocation.parse(value.toLowerCase())); + + if (variant != null) { + wolf.setVariant(Holder.direct(variant)); + } + } +} diff --git a/plugins/fancynpcs/implementation_1_21_3/build.gradle.kts b/plugins/fancynpcs/implementation_1_21_3/build.gradle.kts new file mode 100644 index 00000000..f82b00ce --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("java-library") + id("io.papermc.paperweight.userdev") +} + +val minecraftVersion = "1.21.3" + +paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.MOJANG_PRODUCTION + +dependencies { + paperweight.paperDevBundle("$minecraftVersion-R0.1-SNAPSHOT") + + compileOnly(project(":plugins:fancynpcs:api")) + compileOnly("de.oliver:FancyLib:37") + compileOnly("org.lushplugins:ChatColorHandler:5.1.3") +} + + +tasks { + javadoc { + options.encoding = Charsets.UTF_8.name() + } + + compileJava { + options.encoding = Charsets.UTF_8.name() + options.release = 21 + } +} \ No newline at end of file diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/Npc_1_21_3.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/Npc_1_21_3.java new file mode 100644 index 00000000..561b8981 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/Npc_1_21_3.java @@ -0,0 +1,429 @@ +package de.oliver.fancynpcs.v1_21_3; + +import com.google.common.collect.ImmutableList; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.datafixers.util.Pair; +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.events.NpcSpawnEvent; +import de.oliver.fancynpcs.api.utils.NpcEquipmentSlot; +import io.papermc.paper.adventure.PaperAdventure; +import net.minecraft.Optionull; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.RemoteChatSession; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ClientInformation; +import net.minecraft.server.level.ServerEntity; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.*; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.scores.PlayerTeam; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.scores.Team; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.entity.Player; +import org.lushplugins.chatcolorhandler.ModernChatColorHandler; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class Npc_1_21_3 extends Npc { + + private final String localName; + private final UUID uuid; + private Entity npc; + private Display.TextDisplay sittingVehicle; + + public Npc_1_21_3(NpcData data) { + super(data); + + this.localName = generateLocalName(); + this.uuid = UUID.randomUUID(); + } + + @Override + public void create() { + MinecraftServer minecraftServer = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel serverLevel = ((CraftWorld) data.getLocation().getWorld()).getHandle(); + GameProfile gameProfile = new GameProfile(uuid, localName); + + if (data.getType() == org.bukkit.entity.EntityType.PLAYER) { + npc = new ServerPlayer(minecraftServer, serverLevel, new GameProfile(uuid, ""), ClientInformation.createDefault()); + ((ServerPlayer) npc).gameProfile = gameProfile; + } else { + Optional>> entityTypeReference = BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(data.getType().getKey())); + EntityType nmsType = entityTypeReference.get().value(); // TODO handle empty + EntityType.EntityFactory factory = (EntityType.EntityFactory) ReflectionUtils.getValue(nmsType, "factory"); // EntityType.factory + npc = factory.create(nmsType, serverLevel); + isTeamCreated.clear(); + } + } + + @Override + public void spawn(Player player) { + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc == null) { + return; + } + + if (!data.getLocation().getWorld().getName().equalsIgnoreCase(serverPlayer.level().getWorld().getName())) { + return; + } + + if (data.getSkinData() != null && data.getSkinData().hasTexture()) { + String value = data.getSkinData().getTextureValue(); + String signature = data.getSkinData().getTextureSignature(); + + ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues( + "textures", + ImmutableList.of(new Property("textures", value, signature)) + ); + } + + NpcSpawnEvent spawnEvent = new NpcSpawnEvent(this, player); + spawnEvent.callEvent(); + if (spawnEvent.isCancelled()) { + return; + } + + + if (npc instanceof ServerPlayer npcPlayer) { + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, getEntry(npcPlayer, serverPlayer)); + serverPlayer.connection.send(playerInfoPacket); + + if (data.isSpawnEntity()) { + npc.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + } + } + + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket( + npc.getId(), + npc.getUUID(), + data.getLocation().x(), + data.getLocation().y(), + data.getLocation().z(), + data.getLocation().getPitch(), + data.getLocation().getYaw(), + npc.getType(), + 0, + Vec3.ZERO, + data.getLocation().getYaw() + ); + serverPlayer.connection.send(addEntityPacket); + + isVisibleForPlayer.put(player.getUniqueId(), true); + + + int removeNpcsFromPlayerlistDelay = FancyNpcsPlugin.get().getFancyNpcConfig().getRemoveNpcsFromPlayerlistDelay(); + if (!data.isShowInTab() && removeNpcsFromPlayerlistDelay > 0) { + FancyNpcsPlugin.get().getNpcThread().schedule(() -> { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of(npc.getUUID())); + serverPlayer.connection.send(playerInfoRemovePacket); + }, removeNpcsFromPlayerlistDelay, TimeUnit.MILLISECONDS); + } + + update(player); + } + + @Override + public void remove(Player player) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc instanceof ServerPlayer npcPlayer) { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of((npcPlayer.getUUID()))); + serverPlayer.connection.send(playerInfoRemovePacket); + } + + // remove entity + ClientboundRemoveEntitiesPacket removeEntitiesPacket = new ClientboundRemoveEntitiesPacket(npc.getId()); + serverPlayer.connection.send(removeEntitiesPacket); + + // remove sitting vehicle + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + + isVisibleForPlayer.put(serverPlayer.getUUID(), false); + } + + @Override + public void lookAt(Player player, Location location) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setRot(location.getYaw(), location.getPitch()); + npc.setYHeadRot(location.getYaw()); + npc.setXRot(location.getPitch()); + npc.setYRot(location.getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket( + npc.getId(), + new PositionMoveRotation( + new Vec3(data.getLocation().getX(), data.getLocation().getY(), data.getLocation().getZ()), + Vec3.ZERO, + location.getYaw(), + location.getPitch() + ), + Set.of(), + false + ); + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (location.getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + } + + @Override + public void update(Player player) { + if (npc == null) { + return; + } + + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + PlayerTeam team = new PlayerTeam(new Scoreboard(), "npc-" + localName); + team.getPlayers().clear(); + team.getPlayers().add(npc instanceof ServerPlayer npcPlayer ? npcPlayer.getGameProfile().getName() : npc.getStringUUID()); + team.setColor(PaperAdventure.asVanilla(data.getGlowingColor())); + if (!data.isCollidable()) { + team.setCollisionRule(Team.CollisionRule.NEVER); + } + + net.kyori.adventure.text.Component displayName = ModernChatColorHandler.translate(data.getDisplayName(), serverPlayer.getBukkitEntity()); + Component vanillaComponent = PaperAdventure.asVanilla(displayName); + if (!(npc instanceof ServerPlayer)) { + npc.setCustomName(vanillaComponent); + npc.setCustomNameVisible(true); + } else { + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } + + if (data.getDisplayName().equalsIgnoreCase("")) { + team.setNameTagVisibility(Team.Visibility.NEVER); + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } else { + team.setNameTagVisibility(Team.Visibility.ALWAYS); + } + + if (npc instanceof ServerPlayer npcPlayer) { + team.setPlayerPrefix(vanillaComponent); + npcPlayer.listName = vanillaComponent; + + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, getEntry(npcPlayer, serverPlayer)); + serverPlayer.connection.send(playerInfoPacket); + } + + boolean isTeamCreatedForPlayer = this.isTeamCreated.getOrDefault(player.getUniqueId(), false); + serverPlayer.connection.send(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, !isTeamCreatedForPlayer)); + isTeamCreated.put(player.getUniqueId(), true); + + npc.setGlowingTag(data.isGlowing()); + + if (data.getEquipment() != null && data.getEquipment().size() > 0) { + List> equipmentList = new ArrayList<>(); + + for (NpcEquipmentSlot slot : data.getEquipment().keySet()) { + equipmentList.add(new Pair<>(EquipmentSlot.byName(slot.toNmsName()), CraftItemStack.asNMSCopy(data.getEquipment().get(slot)))); + } + + ClientboundSetEquipmentPacket setEquipmentPacket = new ClientboundSetEquipmentPacket(npc.getId(), equipmentList); + serverPlayer.connection.send(setEquipmentPacket); + } + + if (npc instanceof ServerPlayer) { + // Enable second layer of skin (https://wiki.vg/Entity_metadata#Player) + npc.getEntityData().set(net.minecraft.world.entity.player.Player.DATA_PLAYER_MODE_CUSTOMISATION, (byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40)); + } + + data.applyAllAttributes(this); + + refreshEntityData(player); + + if (data.isSpawnEntity() && data.getLocation() != null) { + move(player, true); + } + + NpcAttribute playerPoseAttr = FancyNpcsPlugin.get().getAttributeManager().getAttributeByName(org.bukkit.entity.EntityType.PLAYER, "pose"); + if (data.getAttributes().containsKey(playerPoseAttr)) { + String pose = data.getAttributes().get(playerPoseAttr); + + if (pose.equals("sitting")) { + setSitting(serverPlayer); + } else { + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + } + + } + + if (npc instanceof LivingEntity) { + Holder.Reference scaleAttribute = BuiltInRegistries.ATTRIBUTE.get(ResourceLocation.parse("minecraft:scale")).get(); + AttributeInstance attributeInstance = new AttributeInstance(scaleAttribute, (a) -> { + }); + attributeInstance.setBaseValue(data.getScale()); + + ClientboundUpdateAttributesPacket updateAttributesPacket = new ClientboundUpdateAttributesPacket(npc.getId(), List.of(attributeInstance)); + serverPlayer.connection.send(updateAttributesPacket); + + } + } + + @Override + protected void refreshEntityData(Player player) { + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + SynchedEntityData.DataItem[] itemsById = (SynchedEntityData.DataItem[]) ReflectionUtils.getValue(npc.getEntityData(), "itemsById"); // itemsById + List> entityData = new ArrayList<>(); + for (SynchedEntityData.DataItem dataItem : itemsById) { + entityData.add(dataItem.value()); + } + ClientboundSetEntityDataPacket setEntityDataPacket = new ClientboundSetEntityDataPacket(npc.getId(), entityData); + serverPlayer.connection.send(setEntityDataPacket); + } + + public void move(Player player, boolean swingArm) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setPosRaw(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + npc.setRot(data.getLocation().getYaw(), data.getLocation().getPitch()); + npc.setYHeadRot(data.getLocation().getYaw()); + npc.setXRot(data.getLocation().getPitch()); + npc.setYRot(data.getLocation().getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket( + npc.getId(), + new PositionMoveRotation( + new Vec3(data.getLocation().getX(), data.getLocation().getY(), data.getLocation().getZ()), + Vec3.ZERO, + data.getLocation().getYaw(), + data.getLocation().getPitch() + ), + Set.of(), + false + ); + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (data.getLocation().getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + + if (swingArm && npc instanceof ServerPlayer) { + ClientboundAnimatePacket animatePacket = new ClientboundAnimatePacket(npc, 0); + serverPlayer.connection.send(animatePacket); + } + } + + private ClientboundPlayerInfoUpdatePacket.Entry getEntry(ServerPlayer npcPlayer, ServerPlayer viewer) { + GameProfile profile = npcPlayer.getGameProfile(); + if (data.isMirrorSkin()) { + GameProfile newProfile = new GameProfile(profile.getId(), profile.getName()); + newProfile.getProperties().putAll(viewer.getGameProfile().getProperties()); + profile = newProfile; + } + + return new ClientboundPlayerInfoUpdatePacket.Entry( + npcPlayer.getUUID(), + profile, + data.isShowInTab(), + 69, + npcPlayer.gameMode.getGameModeForPlayer(), + npcPlayer.getTabListDisplayName(), + -1, + Optionull.map(npcPlayer.getChatSession(), RemoteChatSession::asData) + ); + } + + public void setSitting(ServerPlayer serverPlayer) { + if (npc == null) { + return; + } + + if (sittingVehicle == null) { + sittingVehicle = new Display.TextDisplay(EntityType.TEXT_DISPLAY, ((CraftWorld) data.getLocation().getWorld()).getHandle()); + } + + sittingVehicle.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + + ServerEntity serverEntity = new ServerEntity(serverPlayer.serverLevel(), sittingVehicle, 0, false, packet -> { + }, Set.of()); + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket(sittingVehicle, serverEntity); + serverPlayer.connection.send(addEntityPacket); + + sittingVehicle.passengers = ImmutableList.of(npc); + + ClientboundSetPassengersPacket packet = new ClientboundSetPassengersPacket(sittingVehicle); + serverPlayer.connection.send(packet); + } + + @Override + public float getEyeHeight() { + return npc.getEyeHeight(); + } + + @Override + public int getEntityId() { + return npc.getId(); + } + + public Entity getNpc() { + return npc; + } +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/ReflectionHelper.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/ReflectionHelper.java new file mode 100644 index 00000000..9d2d91e3 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/ReflectionHelper.java @@ -0,0 +1,13 @@ +package de.oliver.fancynpcs.v1_21_3; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import net.minecraft.world.entity.Entity; + +public class ReflectionHelper { + + public static T getEntity(Npc npc) { + return (T) ReflectionUtils.getValue(npc, "npc"); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/AgeableMobAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/AgeableMobAttributes.java new file mode 100644 index 00000000..667b8b49 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/AgeableMobAttributes.java @@ -0,0 +1,38 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.AgeableMob; +import org.bukkit.entity.Ageable; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AgeableMobAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "baby", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Ageable.class.isAssignableFrom(type.getEntityClass())) + .toList(), + AgeableMobAttributes::setBaby + )); + + return attributes; + } + + private static void setBaby(Npc npc, String value) { + AgeableMob mob = ReflectionHelper.getEntity(npc); + + boolean isBaby = Boolean.parseBoolean(value); + + mob.setBaby(isBaby); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/AllayAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/AllayAttributes.java new file mode 100644 index 00000000..51cbd966 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/AllayAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.animal.allay.Allay; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class AllayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.ALLAY), + AllayAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Allay allay = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + allay.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/ArmadilloAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/ArmadilloAttributes.java new file mode 100644 index 00000000..dddb73b5 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/ArmadilloAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.animal.armadillo.Armadillo; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class ArmadilloAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("idle", "rolling", "unrolling", "scared"), + List.of(EntityType.ARMADILLO), + ArmadilloAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Armadillo armadillo = ReflectionHelper.getEntity(npc); + + Armadillo.ArmadilloState state = Armadillo.ArmadilloState.valueOf(value.toUpperCase()); + + armadillo.switchToState(state); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/ArmorStandAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/ArmorStandAttributes.java new file mode 100644 index 00000000..f2457807 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/ArmorStandAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class ArmorStandAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "show_arms", + List.of("true", "false"), + List.of(EntityType.ARMOR_STAND), + ArmorStandAttributes::setShowArms + )); + + return attributes; + } + + private static void setShowArms(Npc npc, String value) { + ArmorStand armorStand = ReflectionHelper.getEntity(npc); + + boolean showArms = Boolean.parseBoolean(value.toLowerCase()); + + armorStand.setShowArms(showArms); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/Attributes_1_21_3.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/Attributes_1_21_3.java new file mode 100644 index 00000000..7f27b7cd --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/Attributes_1_21_3.java @@ -0,0 +1,52 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.NpcAttribute; + +import java.util.ArrayList; +import java.util.List; + +public class Attributes_1_21_3 { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.addAll(EntityAttributes.getAllAttributes()); + attributes.addAll(LivingEntityAttributes.getAllAttributes()); + attributes.addAll(AgeableMobAttributes.getAllAttributes()); + attributes.addAll(IllagerAttributes.getAllAttributes()); + attributes.addAll(SpellCasterAttributes.getAllAttributes()); + + attributes.addAll(PlayerAttributes.getAllAttributes()); + attributes.addAll(SheepAttributes.getAllAttributes()); + attributes.addAll(VillagerAttributes.getAllAttributes()); + attributes.addAll(FrogAttributes.getAllAttributes()); + attributes.addAll(HorseAttributes.getAllAttributes()); + attributes.addAll(ParrotAttributes.getAllAttributes()); + attributes.addAll(AxolotlAttributes.getAllAttributes()); + attributes.addAll(TropicalFishAttributes.getAllAttributes()); + attributes.addAll(FoxAttributes.getAllAttributes()); + attributes.addAll(PandaAttributes.getAllAttributes()); + attributes.addAll(GoatAttributes.getAllAttributes()); + attributes.addAll(AllayAttributes.getAllAttributes()); + attributes.addAll(CamelAttributes.getAllAttributes()); + attributes.addAll(RabbitAttributes.getAllAttributes()); + attributes.addAll(PiglinAttributes.getAllAttributes()); + attributes.addAll(CatAttributes.getAllAttributes()); + attributes.addAll(ShulkerAttributes.getAllAttributes()); + attributes.addAll(WolfAttributes.getAllAttributes()); + attributes.addAll(SlimeAttributes.getAllAttributes()); + attributes.addAll(PigAttributes.getAllAttributes()); + attributes.addAll(ArmorStandAttributes.getAllAttributes()); + attributes.addAll(BeeAttributes.getAllAttributes()); + attributes.addAll(VexAttributes.getAllAttributes()); + attributes.addAll(ArmadilloAttributes.getAllAttributes()); + + attributes.addAll(DisplayAttributes.getAllAttributes()); + attributes.addAll(TextDisplayAttributes.getAllAttributes()); + attributes.addAll(BlockDisplayAttributes.getAllAttributes()); + attributes.addAll(InteractionAttributes.getAllAttributes()); + + return attributes; + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/AxolotlAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/AxolotlAttributes.java new file mode 100644 index 00000000..c2ed1d35 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/AxolotlAttributes.java @@ -0,0 +1,51 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.animal.axolotl.Axolotl; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AxolotlAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Axolotl.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "playing_dead", + List.of("true", "false"), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setPlayingDead + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + Axolotl.Variant variant = Axolotl.Variant.valueOf(value.toUpperCase()); + axolotl.setVariant(variant); + } + + private static void setPlayingDead(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + boolean playingDead = Boolean.parseBoolean(value); + axolotl.setPlayingDead(playingDead); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/BeeAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/BeeAttributes.java new file mode 100644 index 00000000..760a05fe --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/BeeAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.animal.Bee; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BeeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setAngry + )); + + attributes.add(new NpcAttribute( + "sting", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setSting + )); + + attributes.add(new NpcAttribute( + "nectar", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setNectar + )); + + attributes.add(new NpcAttribute( + "rolling", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setRolling + )); + + return attributes; + } + + private static void setAngry(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRemainingPersistentAngerTime(1); + case "false" -> bee.setRemainingPersistentAngerTime(0); + } + } + + private static void setSting(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasStung(false); + case "false" -> bee.setHasStung(true); + } + } + + private static void setNectar(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasNectar(true); + case "false" -> bee.setHasNectar(false); + } + } + + private static void setRolling(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRolling(true); + case "false" -> bee.setRolling(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/BlockDisplayAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/BlockDisplayAttributes.java new file mode 100644 index 00000000..1e59a305 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/BlockDisplayAttributes.java @@ -0,0 +1,41 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Display; +import net.minecraft.world.level.block.Block; +import org.bukkit.Material; +import org.bukkit.Registry; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BlockDisplayAttributes { + + private static final List BLOCKS = Registry.MATERIAL.stream().filter(Material::isBlock).map(it -> it.key().value()).toList(); + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "block", + BLOCKS, + List.of(EntityType.BLOCK_DISPLAY), + BlockDisplayAttributes::setBlock + )); + + return attributes; + } + + private static void setBlock(Npc npc, String value) { + Display.BlockDisplay display = ReflectionHelper.getEntity(npc); + + Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(value.toLowerCase())); + + display.setBlockState(block.defaultBlockState()); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/CamelAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/CamelAttributes.java new file mode 100644 index 00000000..cd413dd9 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/CamelAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.animal.camel.Camel; +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class CamelAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "dashing"), + List.of(EntityType.CAMEL), + CamelAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Camel camel = ReflectionHelper.getEntity(npc); + + Bukkit.getScheduler().runTask(FancyNpcsPlugin.get().getPlugin(), () -> { + switch (value.toLowerCase()) { + case "standing" -> { + camel.setDashing(false); + camel.standUp(); + } + case "sitting" -> { + camel.setDashing(false); + camel.sitDown(); + } + case "dashing" -> { + camel.standUpInstantly(); + camel.setDashing(true); + } + } + }); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/CatAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/CatAttributes.java new file mode 100644 index 00000000..4fb68f14 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/CatAttributes.java @@ -0,0 +1,61 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.Cat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class CatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + BuiltInRegistries.CAT_VARIANT.keySet().stream().map(ResourceLocation::getPath).toList(), + List.of(EntityType.CAT), + CatAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.CAT), + CatAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + BuiltInRegistries.CAT_VARIANT.get(ResourceLocation.parse(value.toLowerCase())) + .ifPresent(cat::setVariant); + } + + private static void setPose(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + switch (value.toLowerCase()) { + case "standing" -> { + cat.setInSittingPose(false, false); + cat.setLying(false); + } + case "sleeping" -> { + cat.setInSittingPose(false, false); + cat.setLying(true); + } + case "sitting" -> { + cat.setLying(false); + cat.setOrderedToSit(true); + cat.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/DisplayAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/DisplayAttributes.java new file mode 100644 index 00000000..8eec999c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/DisplayAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class DisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "billboard", + Arrays.stream(org.bukkit.entity.Display.Billboard.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TEXT_DISPLAY, EntityType.BLOCK_DISPLAY, EntityType.ITEM_DISPLAY), + DisplayAttributes::setBillboard + )); + + return attributes; + } + + private static void setBillboard(Npc npc, String value) { + Display display = ReflectionHelper.getEntity(npc); + + Display.BillboardConstraints billboard = Display.BillboardConstraints.valueOf(value.toUpperCase()); + display.setBillboardConstraints(billboard); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/EntityAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/EntityAttributes.java new file mode 100644 index 00000000..0f853f76 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/EntityAttributes.java @@ -0,0 +1,103 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.Entity; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class EntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "on_fire", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnFire + )); + + attributes.add(new NpcAttribute( + "invisible", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setInvisible + )); + + attributes.add(new NpcAttribute( + "silent", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setSilent + )); + + attributes.add(new NpcAttribute( + "shaking", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setShaking + )); + + attributes.add(new NpcAttribute( + "on_ground", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnGround + )); + + /*attributes.add(new NpcAttribute( + "entity_pose", + Arrays.stream(Pose.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setEntityPose + ));*/ + + return attributes; + } + + private static void setOnFire(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onFire = Boolean.parseBoolean(value); + + entity.setSharedFlagOnFire(onFire); + + } + + private static void setInvisible(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean invisible = Boolean.parseBoolean(value); + + entity.setInvisible(invisible); + } + + private static void setSilent(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean silent = Boolean.parseBoolean(value); + + entity.setSilent(silent); + } + + private static void setShaking(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean shaking = Boolean.parseBoolean(value); + + entity.setTicksFrozen(shaking ? entity.getTicksRequiredToFreeze() : 0); + } + + private static void setOnGround(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onGround = Boolean.parseBoolean(value); + + entity.setOnGround(onGround); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/FoxAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/FoxAttributes.java new file mode 100644 index 00000000..126438e4 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/FoxAttributes.java @@ -0,0 +1,66 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.animal.Fox; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FoxAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "type", + Arrays.stream(Fox.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.FOX), + FoxAttributes::setType + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.FOX), + FoxAttributes::setPose + )); + + return attributes; + } + + private static void setType(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + Fox.Variant type = Fox.Variant.valueOf(value.toUpperCase()); + fox.setVariant(type); + } + + private static void setPose(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + fox.setIsCrouching(false); + fox.setSleeping(false); + fox.setSitting(false, false); + } + case "sleeping" -> { + fox.setSleeping(true); + fox.setSitting(false, false); + fox.setIsCrouching(false); + } + case "sitting" -> { + fox.setSitting(true, false); + fox.setSleeping(false); + fox.setIsCrouching(false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/FrogAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/FrogAttributes.java new file mode 100644 index 00000000..c863e91d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/FrogAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.frog.Frog; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class FrogAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + BuiltInRegistries.FROG_VARIANT.keySet().stream().map(ResourceLocation::getPath).toList(), + List.of(EntityType.FROG), + FrogAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Frog frog = ReflectionHelper.getEntity(npc); + BuiltInRegistries.FROG_VARIANT.get(ResourceLocation.parse(value.toLowerCase())) + .ifPresent(frog::setVariant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/GoatAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/GoatAttributes.java new file mode 100644 index 00000000..12635a31 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/GoatAttributes.java @@ -0,0 +1,44 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.animal.goat.Goat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class GoatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "horns", + List.of("none", "left", "right", "both"), + List.of(EntityType.GOAT), + GoatAttributes::setHorns + )); + + return attributes; + } + + private static void setHorns(Npc npc, String value) { + Goat goat = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "none" -> goat.removeHorns(); + case "both" -> goat.addHorns(); + case "left" -> { + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, false); + } + case "right" -> { + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/HorseAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/HorseAttributes.java new file mode 100644 index 00000000..4af60b33 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/HorseAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.animal.horse.Horse; +import net.minecraft.world.entity.animal.horse.Markings; +import net.minecraft.world.entity.animal.horse.Variant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class HorseAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "markings", + Arrays.stream(Markings.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setMarkings + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("eating", "rearing", "standing"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && (type == EntityType.HORSE || type == EntityType.DONKEY || + type == EntityType.MULE || type == EntityType.SKELETON_HORSE || type == EntityType.ZOMBIE_HORSE)) + .toList(), + HorseAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Variant variant = Variant.valueOf(value.toUpperCase()); + horse.setVariant(variant); + } + + private static void setMarkings(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Markings markings = Markings.valueOf(value.toUpperCase()); + horse.setVariantAndMarkings(horse.getVariant(), markings); + } + + private static void setPose(Npc npc, String value) { + net.minecraft.world.entity.animal.horse.AbstractHorse horse = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + horse.setEating(false); + horse.setForceStanding(false); + } + case "rearing" -> { + horse.setForceStanding(true); + horse.setEating(false); + } + case "eating" -> { + horse.setForceStanding(false); + horse.setEating(true); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/IllagerAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/IllagerAttributes.java new file mode 100644 index 00000000..57f77c01 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/IllagerAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.raid.Raider; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Illager; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class IllagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "celebrating", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Illager.class.isAssignableFrom(type.getEntityClass())) + .toList(), + IllagerAttributes::setCelebrating + )); + + return attributes; + } + + private static void setCelebrating(Npc npc, String value) { + Raider raider = ReflectionHelper.getEntity(npc); + + boolean isCelebrating = Boolean.parseBoolean(value); + + raider.setCelebrating(isCelebrating); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/InteractionAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/InteractionAttributes.java new file mode 100644 index 00000000..30b0bbbe --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/InteractionAttributes.java @@ -0,0 +1,60 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.Interaction; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class InteractionAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "height", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setHeight + )); + + attributes.add(new NpcAttribute( + "width", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setWidth + )); + + return attributes; + } + + private static void setHeight(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float height; + try { + height = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setHeight(height); + } + + private static void setWidth(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float width; + try { + width = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setWidth(width); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/LivingEntityAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/LivingEntityAttributes.java new file mode 100644 index 00000000..6a274265 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/LivingEntityAttributes.java @@ -0,0 +1,67 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.InteractionHand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class LivingEntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + /*attributes.add(new NpcAttribute( + "hurt", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setHurt + ));*/ + + attributes.add(new NpcAttribute( + "use_item", + List.of("main_hand", "off_hand", "none"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setUseItem + )); + + return attributes; + } + + private static void setHurt(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + boolean isHurt = Boolean.parseBoolean(value); + + if (isHurt) { + livingEntity.hurtDuration = 1; + livingEntity.hurtTime = 1; + livingEntity.hurtMarked = true; + livingEntity.animateHurt(0); + } else { + livingEntity.hurtDuration = 0; + livingEntity.hurtTime = 0; + livingEntity.hurtMarked = false; + } + } + + private static void setUseItem(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + switch (value.toUpperCase()) { + case "NONE" -> livingEntity.stopUsingItem(); + case "MAIN_HAND" -> livingEntity.startUsingItem(InteractionHand.MAIN_HAND, true); + case "OFF_HAND" -> livingEntity.startUsingItem(InteractionHand.OFF_HAND, true); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/PandaAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/PandaAttributes.java new file mode 100644 index 00000000..61318193 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/PandaAttributes.java @@ -0,0 +1,101 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.animal.Panda; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PandaAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "gene", + Arrays.stream(Panda.Gene.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PANDA), + PandaAttributes::setGene + )); + + attributes.add(new NpcAttribute( + "eating", + List.of("true", "false"), + List.of(EntityType.PANDA), + PandaAttributes::setEating + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "onBack", "rolling"), + List.of(EntityType.PANDA), + PandaAttributes::setPose + )); + + return attributes; + } + + private static void setGene(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + Panda.Gene gene = Panda.Gene.valueOf(value.toUpperCase()); + panda.setMainGene(gene); + } + + private static void setPose(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(false); + } + case "sitting" -> { + panda.roll(false); + panda.setOnBack(false); + setFlag(panda, 8, true); //sitting + } + case "onback" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(true); + } + case "rolling" -> { + setFlag(panda, 8, false); //sitting + panda.setOnBack(false); + panda.roll(true); + } + } + } + + private static void setEating(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + boolean eating = Boolean.parseBoolean(value); + + panda.eat(eating); + } + + private static void setFlag(Panda panda, int mask, boolean value) { + EntityDataAccessor DATA_ID_FLAGS = (EntityDataAccessor) ReflectionUtils.getValue(panda, "DATA_ID_FLAGS"); + + byte b0 = panda.getEntityData().get(DATA_ID_FLAGS); + + if (value) { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 | mask)); + } else { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 & ~mask)); + } + + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/ParrotAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/ParrotAttributes.java new file mode 100644 index 00000000..0e7cc32d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/ParrotAttributes.java @@ -0,0 +1,59 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.animal.Parrot; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ParrotAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Parrot.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PARROT), + ParrotAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.PARROT), + ParrotAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + Parrot.Variant variant = Parrot.Variant.valueOf(value.toUpperCase()); + parrot.setVariant(variant); + } + + private static void setPose(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + parrot.setOrderedToSit(false); + parrot.setInSittingPose(false, false); + } + case "sitting" -> { + parrot.setOrderedToSit(true); + parrot.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/PigAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/PigAttributes.java new file mode 100644 index 00000000..45c43280 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/PigAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.animal.Pig; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PigAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "has_saddle", + List.of("true", "false"), + List.of(EntityType.PIG), + PigAttributes::setHasSaddle + )); + + return attributes; + } + + private static void setHasSaddle(Npc npc, String value) { + Pig pig = ReflectionHelper.getEntity(npc); + + boolean hasSaddle = Boolean.parseBoolean(value.toLowerCase()); + + pig.steering.setSaddle(hasSaddle); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/PiglinAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/PiglinAttributes.java new file mode 100644 index 00000000..d8b88ce4 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/PiglinAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.monster.piglin.Piglin; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PiglinAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.PIGLIN), + PiglinAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Piglin piglin = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + piglin.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/PlayerAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/PlayerAttributes.java new file mode 100644 index 00000000..8a37bcbb --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/PlayerAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.entity.player.Player; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PlayerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "crouching", "sleeping", "swimming", "sitting"), + List.of(EntityType.PLAYER), + PlayerAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Player player = ReflectionHelper.getEntity(npc); + + Pose pose = Pose.valueOf(value.toUpperCase()); + + EntityDataAccessor DATA_POSE = (EntityDataAccessor) ReflectionUtils.getStaticValue(Entity.class, "DATA_POSE"); // DATA_POSE + player.getEntityData().set(DATA_POSE, pose); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/RabbitAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/RabbitAttributes.java new file mode 100644 index 00000000..a119925d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/RabbitAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.animal.Rabbit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class RabbitAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Rabbit.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.RABBIT), + RabbitAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Rabbit rabbit = ReflectionHelper.getEntity(npc); + + Rabbit.Variant variant = Rabbit.Variant.valueOf(value.toUpperCase()); + rabbit.setVariant(variant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/SheepAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/SheepAttributes.java new file mode 100644 index 00000000..1a469f29 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/SheepAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.animal.Sheep; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SheepAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "wool_color", + Arrays.stream(DyeColor.values()).map(dyeColor -> dyeColor.name().toLowerCase()).toList(), + List.of(EntityType.SHEEP), + SheepAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "sheared", + Arrays.asList("true", "false"), + List.of(EntityType.SHEEP), + SheepAttributes::setSheared + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + sheep.setColor(DyeColor.byName(value.toLowerCase(), DyeColor.WHITE)); + } + + private static void setSheared(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + boolean sheared = Boolean.parseBoolean(value); + + sheep.setSheared(sheared); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/ShulkerAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/ShulkerAttributes.java new file mode 100644 index 00000000..955212d7 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/ShulkerAttributes.java @@ -0,0 +1,55 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class ShulkerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.SHULKER), + ShulkerAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "shield", + List.of("open", "closed"), + List.of(EntityType.SHULKER), + ShulkerAttributes::setShield + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.PURPLE); + shulker.setVariant(Optional.of(color)); + } + + private static void setShield(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "closed" -> shulker.setRawPeekAmount(0); + case "open" -> shulker.setRawPeekAmount(Byte.MAX_VALUE); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/SlimeAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/SlimeAttributes.java new file mode 100644 index 00000000..5dd53d72 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/SlimeAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.monster.Slime; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class SlimeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "size", + new ArrayList<>(), + List.of(EntityType.SLIME), + SlimeAttributes::setSize + )); + + return attributes; + } + + private static void setSize(Npc npc, String value) { + Slime slime = ReflectionHelper.getEntity(npc); + + int size; + try { + size = Integer.parseInt(value); + } catch (NumberFormatException e) { + return; + } + + slime.setSize(size, false); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/SpellCasterAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/SpellCasterAttributes.java new file mode 100644 index 00000000..e3be3b49 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/SpellCasterAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.monster.SpellcasterIllager; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Spellcaster; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SpellCasterAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "casting", + Arrays.stream(SpellcasterIllager.IllagerSpell.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Spellcaster.class.isAssignableFrom(type.getEntityClass())) + .toList(), + SpellCasterAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + SpellcasterIllager spellcasterIllager = ReflectionHelper.getEntity(npc); + + SpellcasterIllager.IllagerSpell spell = SpellcasterIllager.IllagerSpell.valueOf(value); + + spellcasterIllager.setIsCastingSpell(spell); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/TextDisplayAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/TextDisplayAttributes.java new file mode 100644 index 00000000..7c5a632e --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/TextDisplayAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import io.papermc.paper.adventure.PaperAdventure; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class TextDisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "text", + new ArrayList<>(), + List.of(EntityType.TEXT_DISPLAY), + TextDisplayAttributes::setText + )); + + return attributes; + } + + private static void setText(Npc npc, String value) { + Display.TextDisplay display = ReflectionHelper.getEntity(npc); + + Component text = MiniMessage.miniMessage().deserialize(value); + display.setText(PaperAdventure.asVanilla(text)); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/TropicalFishAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/TropicalFishAttributes.java new file mode 100644 index 00000000..ad56cfc2 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/TropicalFishAttributes.java @@ -0,0 +1,72 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.animal.TropicalFish; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TropicalFishAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pattern", + Arrays.stream(TropicalFish.Pattern.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPattern + )); + + attributes.add(new NpcAttribute( + "base_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setBaseColor + )); + + attributes.add(new NpcAttribute( + "pattern_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPatternColor + )); + + return attributes; + } + + private static void setPattern(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + TropicalFish.Pattern pattern = TropicalFish.Pattern.valueOf(value.toUpperCase()); + tropicalFish.setVariant(pattern); + } + + private static void setBaseColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), color, tropicalFish.getPatternColor()); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + + private static void setPatternColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), tropicalFish.getBaseColor(), color); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/VexAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/VexAttributes.java new file mode 100644 index 00000000..ab90c18c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/VexAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.world.entity.monster.Vex; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class VexAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "charging", + List.of("true", "false"), + List.of(EntityType.VEX), + VexAttributes::setCharging + )); + + return attributes; + } + + private static void setCharging(Npc npc, String value) { + Vex vex = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> vex.setIsCharging(true); + case "false" -> vex.setIsCharging(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/VillagerAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/VillagerAttributes.java new file mode 100644 index 00000000..6da30dd6 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/VillagerAttributes.java @@ -0,0 +1,54 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.entity.npc.VillagerType; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class VillagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "profession", + BuiltInRegistries.VILLAGER_PROFESSION.keySet().stream().map(ResourceLocation::getPath).toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setProfession + )); + + attributes.add(new NpcAttribute( + "type", + BuiltInRegistries.VILLAGER_TYPE.keySet().stream().map(ResourceLocation::getPath).toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setType + )); + + return attributes; + } + + private static void setProfession(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerProfession profession = BuiltInRegistries.VILLAGER_PROFESSION.getValue(ResourceLocation.tryParse(value)); + + villager.setVillagerData(villager.getVillagerData().setProfession(profession)); + } + + private static void setType(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerType type = BuiltInRegistries.VILLAGER_TYPE.getValue(ResourceLocation.tryParse(value)); + + villager.setVillagerData(villager.getVillagerData().setType(type)); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/WolfAttributes.java b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/WolfAttributes.java new file mode 100644 index 00000000..47af92ff --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_3/src/main/java/de/oliver/fancynpcs/v1_21_3/attributes/WolfAttributes.java @@ -0,0 +1,73 @@ +package de.oliver.fancynpcs.v1_21_3.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_3.ReflectionHelper; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.Wolf; +import net.minecraft.world.entity.animal.WolfVariant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class WolfAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.WOLF), + WolfAttributes::setPose + )); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.WOLF), + WolfAttributes::setAngry + )); + + attributes.add(new NpcAttribute( + "variant", + List.of("PALE", "SPOTTED", "SNOWY", "BLACK", "ASHEN", "RUSTY", "WOODS", "CHESTNUT", "STRIPED"), + List.of(EntityType.WOLF), + WolfAttributes::setVariant + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> wolf.setInSittingPose(false, false); + case "sitting" -> wolf.setInSittingPose(true, false); + } + } + + private static void setAngry(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + boolean angry = Boolean.parseBoolean(value.toLowerCase()); + + wolf.setRemainingPersistentAngerTime(angry ? 100 : 0); + } + + private static void setVariant(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + Registry registry = wolf.level().registryAccess().lookupOrThrow(Registries.WOLF_VARIANT); + WolfVariant variant = registry.getValue(ResourceLocation.parse(value.toLowerCase())); + + if (variant != null) { + wolf.setVariant(Holder.direct(variant)); + } + } +} diff --git a/plugins/fancynpcs/implementation_1_21_4/build.gradle.kts b/plugins/fancynpcs/implementation_1_21_4/build.gradle.kts new file mode 100644 index 00000000..cc0d6a07 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("java-library") + id("io.papermc.paperweight.userdev") +} + +val minecraftVersion = "1.21.4" + +paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.MOJANG_PRODUCTION + +dependencies { + paperweight.paperDevBundle("$minecraftVersion-R0.1-SNAPSHOT") + + compileOnly(project(":plugins:fancynpcs:api")) + compileOnly("de.oliver:FancyLib:37") + compileOnly("org.lushplugins:ChatColorHandler:5.1.3") +} + + +tasks { + javadoc { + options.encoding = Charsets.UTF_8.name() + } + + compileJava { + options.encoding = Charsets.UTF_8.name() + options.release = 21 + } +} \ No newline at end of file diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/Npc_1_21_4.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/Npc_1_21_4.java new file mode 100644 index 00000000..841293aa --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/Npc_1_21_4.java @@ -0,0 +1,430 @@ +package de.oliver.fancynpcs.v1_21_4; + +import com.google.common.collect.ImmutableList; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.datafixers.util.Pair; +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.events.NpcSpawnEvent; +import de.oliver.fancynpcs.api.utils.NpcEquipmentSlot; +import io.papermc.paper.adventure.PaperAdventure; +import net.minecraft.Optionull; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.RemoteChatSession; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ClientInformation; +import net.minecraft.server.level.ServerEntity; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.*; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.scores.PlayerTeam; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.scores.Team; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.entity.Player; +import org.lushplugins.chatcolorhandler.ModernChatColorHandler; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class Npc_1_21_4 extends Npc { + + private final String localName; + private final UUID uuid; + private Entity npc; + private Display.TextDisplay sittingVehicle; + + public Npc_1_21_4(NpcData data) { + super(data); + + this.localName = generateLocalName(); + this.uuid = UUID.randomUUID(); + } + + @Override + public void create() { + MinecraftServer minecraftServer = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel serverLevel = ((CraftWorld) data.getLocation().getWorld()).getHandle(); + GameProfile gameProfile = new GameProfile(uuid, localName); + + if (data.getType() == org.bukkit.entity.EntityType.PLAYER) { + npc = new ServerPlayer(minecraftServer, serverLevel, new GameProfile(uuid, ""), ClientInformation.createDefault()); + ((ServerPlayer) npc).gameProfile = gameProfile; + } else { + Optional>> entityTypeReference = BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(data.getType().getKey())); + EntityType nmsType = entityTypeReference.get().value(); // TODO handle empty + EntityType.EntityFactory factory = (EntityType.EntityFactory) ReflectionUtils.getValue(nmsType, "factory"); // EntityType.factory + npc = factory.create(nmsType, serverLevel); + isTeamCreated.clear(); + } + } + + @Override + public void spawn(Player player) { + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc == null) { + return; + } + + if (!data.getLocation().getWorld().getName().equalsIgnoreCase(serverPlayer.level().getWorld().getName())) { + return; + } + + if (data.getSkinData() != null && data.getSkinData().hasTexture()) { + String value = data.getSkinData().getTextureValue(); + String signature = data.getSkinData().getTextureSignature(); + + ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues( + "textures", + ImmutableList.of(new Property("textures", value, signature)) + ); + } + + NpcSpawnEvent spawnEvent = new NpcSpawnEvent(this, player); + spawnEvent.callEvent(); + if (spawnEvent.isCancelled()) { + return; + } + + + if (npc instanceof ServerPlayer npcPlayer) { + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, getEntry(npcPlayer, serverPlayer)); + serverPlayer.connection.send(playerInfoPacket); + + if (data.isSpawnEntity()) { + npc.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + } + } + + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket( + npc.getId(), + npc.getUUID(), + data.getLocation().x(), + data.getLocation().y(), + data.getLocation().z(), + data.getLocation().getPitch(), + data.getLocation().getYaw(), + npc.getType(), + 0, + Vec3.ZERO, + data.getLocation().getYaw() + ); + serverPlayer.connection.send(addEntityPacket); + + isVisibleForPlayer.put(player.getUniqueId(), true); + + + int removeNpcsFromPlayerlistDelay = FancyNpcsPlugin.get().getFancyNpcConfig().getRemoveNpcsFromPlayerlistDelay(); + if (!data.isShowInTab() && removeNpcsFromPlayerlistDelay > 0) { + FancyNpcsPlugin.get().getNpcThread().schedule(() -> { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of(npc.getUUID())); + serverPlayer.connection.send(playerInfoRemovePacket); + }, removeNpcsFromPlayerlistDelay, TimeUnit.MILLISECONDS); + } + + update(player); + } + + @Override + public void remove(Player player) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc instanceof ServerPlayer npcPlayer) { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of((npcPlayer.getUUID()))); + serverPlayer.connection.send(playerInfoRemovePacket); + } + + // remove entity + ClientboundRemoveEntitiesPacket removeEntitiesPacket = new ClientboundRemoveEntitiesPacket(npc.getId()); + serverPlayer.connection.send(removeEntitiesPacket); + + // remove sitting vehicle + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + + isVisibleForPlayer.put(serverPlayer.getUUID(), false); + } + + @Override + public void lookAt(Player player, Location location) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setRot(location.getYaw(), location.getPitch()); + npc.setYHeadRot(location.getYaw()); + npc.setXRot(location.getPitch()); + npc.setYRot(location.getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket( + npc.getId(), + new PositionMoveRotation( + new Vec3(data.getLocation().getX(), data.getLocation().getY(), data.getLocation().getZ()), + Vec3.ZERO, + location.getYaw(), + location.getPitch() + ), + Set.of(), + false + ); + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (location.getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + } + + @Override + public void update(Player player) { + if (npc == null) { + return; + } + + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + PlayerTeam team = new PlayerTeam(new Scoreboard(), "npc-" + localName); + team.getPlayers().clear(); + team.getPlayers().add(npc instanceof ServerPlayer npcPlayer ? npcPlayer.getGameProfile().getName() : npc.getStringUUID()); + team.setColor(PaperAdventure.asVanilla(data.getGlowingColor())); + if (!data.isCollidable()) { + team.setCollisionRule(Team.CollisionRule.NEVER); + } + + net.kyori.adventure.text.Component displayName = ModernChatColorHandler.translate(data.getDisplayName(), serverPlayer.getBukkitEntity()); + Component vanillaComponent = PaperAdventure.asVanilla(displayName); + if (!(npc instanceof ServerPlayer)) { + npc.setCustomName(vanillaComponent); + npc.setCustomNameVisible(true); + } else { + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } + + if (data.getDisplayName().equalsIgnoreCase("")) { + team.setNameTagVisibility(Team.Visibility.NEVER); + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } else { + team.setNameTagVisibility(Team.Visibility.ALWAYS); + } + + if (npc instanceof ServerPlayer npcPlayer) { + team.setPlayerPrefix(vanillaComponent); + npcPlayer.listName = vanillaComponent; + + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, getEntry(npcPlayer, serverPlayer)); + serverPlayer.connection.send(playerInfoPacket); + } + + boolean isTeamCreatedForPlayer = this.isTeamCreated.getOrDefault(player.getUniqueId(), false); + serverPlayer.connection.send(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, !isTeamCreatedForPlayer)); + isTeamCreated.put(player.getUniqueId(), true); + + npc.setGlowingTag(data.isGlowing()); + + if (data.getEquipment() != null && data.getEquipment().size() > 0) { + List> equipmentList = new ArrayList<>(); + + for (NpcEquipmentSlot slot : data.getEquipment().keySet()) { + equipmentList.add(new Pair<>(EquipmentSlot.byName(slot.toNmsName()), CraftItemStack.asNMSCopy(data.getEquipment().get(slot)))); + } + + ClientboundSetEquipmentPacket setEquipmentPacket = new ClientboundSetEquipmentPacket(npc.getId(), equipmentList); + serverPlayer.connection.send(setEquipmentPacket); + } + + if (npc instanceof ServerPlayer) { + // Enable second layer of skin (https://wiki.vg/Entity_metadata#Player) + npc.getEntityData().set(net.minecraft.world.entity.player.Player.DATA_PLAYER_MODE_CUSTOMISATION, (byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40)); + } + + data.applyAllAttributes(this); + + refreshEntityData(player); + + if (data.isSpawnEntity() && data.getLocation() != null) { + move(player, true); + } + + NpcAttribute playerPoseAttr = FancyNpcsPlugin.get().getAttributeManager().getAttributeByName(org.bukkit.entity.EntityType.PLAYER, "pose"); + if (data.getAttributes().containsKey(playerPoseAttr)) { + String pose = data.getAttributes().get(playerPoseAttr); + + if (pose.equals("sitting")) { + setSitting(serverPlayer); + } else { + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + } + + } + + if (npc instanceof LivingEntity) { + Holder.Reference scaleAttribute = BuiltInRegistries.ATTRIBUTE.get(ResourceLocation.parse("minecraft:scale")).get(); + AttributeInstance attributeInstance = new AttributeInstance(scaleAttribute, (a) -> { + }); + attributeInstance.setBaseValue(data.getScale()); + + ClientboundUpdateAttributesPacket updateAttributesPacket = new ClientboundUpdateAttributesPacket(npc.getId(), List.of(attributeInstance)); + serverPlayer.connection.send(updateAttributesPacket); + + } + } + + @Override + protected void refreshEntityData(Player player) { + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + SynchedEntityData.DataItem[] itemsById = (SynchedEntityData.DataItem[]) ReflectionUtils.getValue(npc.getEntityData(), "itemsById"); // itemsById + List> entityData = new ArrayList<>(); + for (SynchedEntityData.DataItem dataItem : itemsById) { + entityData.add(dataItem.value()); + } + ClientboundSetEntityDataPacket setEntityDataPacket = new ClientboundSetEntityDataPacket(npc.getId(), entityData); + serverPlayer.connection.send(setEntityDataPacket); + } + + public void move(Player player, boolean swingArm) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setPosRaw(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + npc.setRot(data.getLocation().getYaw(), data.getLocation().getPitch()); + npc.setYHeadRot(data.getLocation().getYaw()); + npc.setXRot(data.getLocation().getPitch()); + npc.setYRot(data.getLocation().getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket( + npc.getId(), + new PositionMoveRotation( + new Vec3(data.getLocation().getX(), data.getLocation().getY(), data.getLocation().getZ()), + Vec3.ZERO, + data.getLocation().getYaw(), + data.getLocation().getPitch() + ), + Set.of(), + false + ); + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (data.getLocation().getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + + if (swingArm && npc instanceof ServerPlayer) { + ClientboundAnimatePacket animatePacket = new ClientboundAnimatePacket(npc, 0); + serverPlayer.connection.send(animatePacket); + } + } + + private ClientboundPlayerInfoUpdatePacket.Entry getEntry(ServerPlayer npcPlayer, ServerPlayer viewer) { + GameProfile profile = npcPlayer.getGameProfile(); + if (data.isMirrorSkin()) { + GameProfile newProfile = new GameProfile(profile.getId(), profile.getName()); + newProfile.getProperties().putAll(viewer.getGameProfile().getProperties()); + profile = newProfile; + } + + return new ClientboundPlayerInfoUpdatePacket.Entry( + npcPlayer.getUUID(), + profile, + data.isShowInTab(), + 69, + npcPlayer.gameMode.getGameModeForPlayer(), + npcPlayer.getTabListDisplayName(), + true, + -1, + Optionull.map(npcPlayer.getChatSession(), RemoteChatSession::asData) + ); + } + + public void setSitting(ServerPlayer serverPlayer) { + if (npc == null) { + return; + } + + if (sittingVehicle == null) { + sittingVehicle = new Display.TextDisplay(EntityType.TEXT_DISPLAY, ((CraftWorld) data.getLocation().getWorld()).getHandle()); + } + + sittingVehicle.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + + ServerEntity serverEntity = new ServerEntity(serverPlayer.serverLevel(), sittingVehicle, 0, false, packet -> { + }, Set.of()); + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket(sittingVehicle, serverEntity); + serverPlayer.connection.send(addEntityPacket); + + sittingVehicle.passengers = ImmutableList.of(npc); + + ClientboundSetPassengersPacket packet = new ClientboundSetPassengersPacket(sittingVehicle); + serverPlayer.connection.send(packet); + } + + @Override + public float getEyeHeight() { + return npc.getEyeHeight(); + } + + @Override + public int getEntityId() { + return npc.getId(); + } + + public Entity getNpc() { + return npc; + } +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/ReflectionHelper.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/ReflectionHelper.java new file mode 100644 index 00000000..1791c481 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/ReflectionHelper.java @@ -0,0 +1,13 @@ +package de.oliver.fancynpcs.v1_21_4; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import net.minecraft.world.entity.Entity; + +public class ReflectionHelper { + + public static T getEntity(Npc npc) { + return (T) ReflectionUtils.getValue(npc, "npc"); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/AgeableMobAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/AgeableMobAttributes.java new file mode 100644 index 00000000..6ed600a5 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/AgeableMobAttributes.java @@ -0,0 +1,38 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.AgeableMob; +import org.bukkit.entity.Ageable; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AgeableMobAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "baby", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Ageable.class.isAssignableFrom(type.getEntityClass())) + .toList(), + AgeableMobAttributes::setBaby + )); + + return attributes; + } + + private static void setBaby(Npc npc, String value) { + AgeableMob mob = ReflectionHelper.getEntity(npc); + + boolean isBaby = Boolean.parseBoolean(value); + + mob.setBaby(isBaby); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/AllayAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/AllayAttributes.java new file mode 100644 index 00000000..eb5e1f81 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/AllayAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.animal.allay.Allay; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class AllayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.ALLAY), + AllayAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Allay allay = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + allay.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/ArmadilloAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/ArmadilloAttributes.java new file mode 100644 index 00000000..c8c77179 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/ArmadilloAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.animal.armadillo.Armadillo; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class ArmadilloAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("idle", "rolling", "unrolling", "scared"), + List.of(EntityType.ARMADILLO), + ArmadilloAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Armadillo armadillo = ReflectionHelper.getEntity(npc); + + Armadillo.ArmadilloState state = Armadillo.ArmadilloState.valueOf(value.toUpperCase()); + + armadillo.switchToState(state); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/ArmorStandAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/ArmorStandAttributes.java new file mode 100644 index 00000000..9cc8fd6c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/ArmorStandAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class ArmorStandAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "show_arms", + List.of("true", "false"), + List.of(EntityType.ARMOR_STAND), + ArmorStandAttributes::setShowArms + )); + + return attributes; + } + + private static void setShowArms(Npc npc, String value) { + ArmorStand armorStand = ReflectionHelper.getEntity(npc); + + boolean showArms = Boolean.parseBoolean(value.toLowerCase()); + + armorStand.setShowArms(showArms); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/Attributes_1_21_4.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/Attributes_1_21_4.java new file mode 100644 index 00000000..988fc9bc --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/Attributes_1_21_4.java @@ -0,0 +1,52 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.NpcAttribute; + +import java.util.ArrayList; +import java.util.List; + +public class Attributes_1_21_4 { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.addAll(EntityAttributes.getAllAttributes()); + attributes.addAll(LivingEntityAttributes.getAllAttributes()); + attributes.addAll(AgeableMobAttributes.getAllAttributes()); + attributes.addAll(IllagerAttributes.getAllAttributes()); + attributes.addAll(SpellCasterAttributes.getAllAttributes()); + + attributes.addAll(PlayerAttributes.getAllAttributes()); + attributes.addAll(SheepAttributes.getAllAttributes()); + attributes.addAll(VillagerAttributes.getAllAttributes()); + attributes.addAll(FrogAttributes.getAllAttributes()); + attributes.addAll(HorseAttributes.getAllAttributes()); + attributes.addAll(ParrotAttributes.getAllAttributes()); + attributes.addAll(AxolotlAttributes.getAllAttributes()); + attributes.addAll(TropicalFishAttributes.getAllAttributes()); + attributes.addAll(FoxAttributes.getAllAttributes()); + attributes.addAll(PandaAttributes.getAllAttributes()); + attributes.addAll(GoatAttributes.getAllAttributes()); + attributes.addAll(AllayAttributes.getAllAttributes()); + attributes.addAll(CamelAttributes.getAllAttributes()); + attributes.addAll(RabbitAttributes.getAllAttributes()); + attributes.addAll(PiglinAttributes.getAllAttributes()); + attributes.addAll(CatAttributes.getAllAttributes()); + attributes.addAll(ShulkerAttributes.getAllAttributes()); + attributes.addAll(WolfAttributes.getAllAttributes()); + attributes.addAll(SlimeAttributes.getAllAttributes()); + attributes.addAll(PigAttributes.getAllAttributes()); + attributes.addAll(ArmorStandAttributes.getAllAttributes()); + attributes.addAll(BeeAttributes.getAllAttributes()); + attributes.addAll(VexAttributes.getAllAttributes()); + attributes.addAll(ArmadilloAttributes.getAllAttributes()); + + attributes.addAll(DisplayAttributes.getAllAttributes()); + attributes.addAll(TextDisplayAttributes.getAllAttributes()); + attributes.addAll(BlockDisplayAttributes.getAllAttributes()); + attributes.addAll(InteractionAttributes.getAllAttributes()); + + return attributes; + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/AxolotlAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/AxolotlAttributes.java new file mode 100644 index 00000000..14cc8a96 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/AxolotlAttributes.java @@ -0,0 +1,51 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.animal.axolotl.Axolotl; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AxolotlAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Axolotl.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "playing_dead", + List.of("true", "false"), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setPlayingDead + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + Axolotl.Variant variant = Axolotl.Variant.valueOf(value.toUpperCase()); + axolotl.setVariant(variant); + } + + private static void setPlayingDead(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + boolean playingDead = Boolean.parseBoolean(value); + axolotl.setPlayingDead(playingDead); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/BeeAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/BeeAttributes.java new file mode 100644 index 00000000..2cbed7d7 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/BeeAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Bee; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BeeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setAngry + )); + + attributes.add(new NpcAttribute( + "sting", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setSting + )); + + attributes.add(new NpcAttribute( + "nectar", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setNectar + )); + + attributes.add(new NpcAttribute( + "rolling", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setRolling + )); + + return attributes; + } + + private static void setAngry(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRemainingPersistentAngerTime(1); + case "false" -> bee.setRemainingPersistentAngerTime(0); + } + } + + private static void setSting(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasStung(false); + case "false" -> bee.setHasStung(true); + } + } + + private static void setNectar(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasNectar(true); + case "false" -> bee.setHasNectar(false); + } + } + + private static void setRolling(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRolling(true); + case "false" -> bee.setRolling(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/BlockDisplayAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/BlockDisplayAttributes.java new file mode 100644 index 00000000..90e6d355 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/BlockDisplayAttributes.java @@ -0,0 +1,41 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Display; +import net.minecraft.world.level.block.Block; +import org.bukkit.Material; +import org.bukkit.Registry; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BlockDisplayAttributes { + + private static final List BLOCKS = Registry.MATERIAL.stream().filter(Material::isBlock).map(it -> it.key().value()).toList(); + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "block", + BLOCKS, + List.of(EntityType.BLOCK_DISPLAY), + BlockDisplayAttributes::setBlock + )); + + return attributes; + } + + private static void setBlock(Npc npc, String value) { + Display.BlockDisplay display = ReflectionHelper.getEntity(npc); + + Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(value.toLowerCase())); + + display.setBlockState(block.defaultBlockState()); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/CamelAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/CamelAttributes.java new file mode 100644 index 00000000..fd58197a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/CamelAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.animal.camel.Camel; +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class CamelAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "dashing"), + List.of(EntityType.CAMEL), + CamelAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Camel camel = ReflectionHelper.getEntity(npc); + + Bukkit.getScheduler().runTask(FancyNpcsPlugin.get().getPlugin(), () -> { + switch (value.toLowerCase()) { + case "standing" -> { + camel.setDashing(false); + camel.standUp(); + } + case "sitting" -> { + camel.setDashing(false); + camel.sitDown(); + } + case "dashing" -> { + camel.standUpInstantly(); + camel.setDashing(true); + } + } + }); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/CatAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/CatAttributes.java new file mode 100644 index 00000000..9617233c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/CatAttributes.java @@ -0,0 +1,61 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.Cat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class CatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + BuiltInRegistries.CAT_VARIANT.keySet().stream().map(ResourceLocation::getPath).toList(), + List.of(EntityType.CAT), + CatAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.CAT), + CatAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + BuiltInRegistries.CAT_VARIANT.get(ResourceLocation.parse(value.toLowerCase())) + .ifPresent(cat::setVariant); + } + + private static void setPose(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + switch (value.toLowerCase()) { + case "standing" -> { + cat.setInSittingPose(false, false); + cat.setLying(false); + } + case "sleeping" -> { + cat.setInSittingPose(false, false); + cat.setLying(true); + } + case "sitting" -> { + cat.setLying(false); + cat.setOrderedToSit(true); + cat.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/DisplayAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/DisplayAttributes.java new file mode 100644 index 00000000..d7be0ccd --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/DisplayAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class DisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "billboard", + Arrays.stream(org.bukkit.entity.Display.Billboard.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TEXT_DISPLAY, EntityType.BLOCK_DISPLAY, EntityType.ITEM_DISPLAY), + DisplayAttributes::setBillboard + )); + + return attributes; + } + + private static void setBillboard(Npc npc, String value) { + Display display = ReflectionHelper.getEntity(npc); + + Display.BillboardConstraints billboard = Display.BillboardConstraints.valueOf(value.toUpperCase()); + display.setBillboardConstraints(billboard); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/EntityAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/EntityAttributes.java new file mode 100644 index 00000000..ad065f35 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/EntityAttributes.java @@ -0,0 +1,103 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.Entity; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class EntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "on_fire", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnFire + )); + + attributes.add(new NpcAttribute( + "invisible", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setInvisible + )); + + attributes.add(new NpcAttribute( + "silent", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setSilent + )); + + attributes.add(new NpcAttribute( + "shaking", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setShaking + )); + + attributes.add(new NpcAttribute( + "on_ground", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnGround + )); + + /*attributes.add(new NpcAttribute( + "entity_pose", + Arrays.stream(Pose.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setEntityPose + ));*/ + + return attributes; + } + + private static void setOnFire(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onFire = Boolean.parseBoolean(value); + + entity.setSharedFlagOnFire(onFire); + + } + + private static void setInvisible(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean invisible = Boolean.parseBoolean(value); + + entity.setInvisible(invisible); + } + + private static void setSilent(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean silent = Boolean.parseBoolean(value); + + entity.setSilent(silent); + } + + private static void setShaking(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean shaking = Boolean.parseBoolean(value); + + entity.setTicksFrozen(shaking ? entity.getTicksRequiredToFreeze() : 0); + } + + private static void setOnGround(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onGround = Boolean.parseBoolean(value); + + entity.setOnGround(onGround); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/FoxAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/FoxAttributes.java new file mode 100644 index 00000000..57dd3c97 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/FoxAttributes.java @@ -0,0 +1,66 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Fox; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FoxAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "type", + Arrays.stream(Fox.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.FOX), + FoxAttributes::setType + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.FOX), + FoxAttributes::setPose + )); + + return attributes; + } + + private static void setType(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + Fox.Variant type = Fox.Variant.valueOf(value.toUpperCase()); + fox.setVariant(type); + } + + private static void setPose(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + fox.setIsCrouching(false); + fox.setSleeping(false); + fox.setSitting(false, false); + } + case "sleeping" -> { + fox.setSleeping(true); + fox.setSitting(false, false); + fox.setIsCrouching(false); + } + case "sitting" -> { + fox.setSitting(true, false); + fox.setSleeping(false); + fox.setIsCrouching(false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/FrogAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/FrogAttributes.java new file mode 100644 index 00000000..6b0a32d4 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/FrogAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.frog.Frog; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class FrogAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + BuiltInRegistries.FROG_VARIANT.keySet().stream().map(ResourceLocation::getPath).toList(), + List.of(EntityType.FROG), + FrogAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Frog frog = ReflectionHelper.getEntity(npc); + BuiltInRegistries.FROG_VARIANT.get(ResourceLocation.parse(value.toLowerCase())) + .ifPresent(frog::setVariant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/GoatAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/GoatAttributes.java new file mode 100644 index 00000000..672d8a54 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/GoatAttributes.java @@ -0,0 +1,44 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.animal.goat.Goat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class GoatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "horns", + List.of("none", "left", "right", "both"), + List.of(EntityType.GOAT), + GoatAttributes::setHorns + )); + + return attributes; + } + + private static void setHorns(Npc npc, String value) { + Goat goat = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "none" -> goat.removeHorns(); + case "both" -> goat.addHorns(); + case "left" -> { + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, false); + } + case "right" -> { + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/HorseAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/HorseAttributes.java new file mode 100644 index 00000000..19a6ad6b --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/HorseAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.animal.horse.Horse; +import net.minecraft.world.entity.animal.horse.Markings; +import net.minecraft.world.entity.animal.horse.Variant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class HorseAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "markings", + Arrays.stream(Markings.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setMarkings + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("eating", "rearing", "standing"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && (type == EntityType.HORSE || type == EntityType.DONKEY || + type == EntityType.MULE || type == EntityType.SKELETON_HORSE || type == EntityType.ZOMBIE_HORSE)) + .toList(), + HorseAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Variant variant = Variant.valueOf(value.toUpperCase()); + horse.setVariant(variant); + } + + private static void setMarkings(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Markings markings = Markings.valueOf(value.toUpperCase()); + horse.setVariantAndMarkings(horse.getVariant(), markings); + } + + private static void setPose(Npc npc, String value) { + net.minecraft.world.entity.animal.horse.AbstractHorse horse = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + horse.setEating(false); + horse.setForceStanding(false); + } + case "rearing" -> { + horse.setForceStanding(true); + horse.setEating(false); + } + case "eating" -> { + horse.setForceStanding(false); + horse.setEating(true); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/IllagerAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/IllagerAttributes.java new file mode 100644 index 00000000..9396617b --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/IllagerAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.raid.Raider; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Illager; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class IllagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "celebrating", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Illager.class.isAssignableFrom(type.getEntityClass())) + .toList(), + IllagerAttributes::setCelebrating + )); + + return attributes; + } + + private static void setCelebrating(Npc npc, String value) { + Raider raider = ReflectionHelper.getEntity(npc); + + boolean isCelebrating = Boolean.parseBoolean(value); + + raider.setCelebrating(isCelebrating); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/InteractionAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/InteractionAttributes.java new file mode 100644 index 00000000..a143e5fd --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/InteractionAttributes.java @@ -0,0 +1,60 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.Interaction; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class InteractionAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "height", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setHeight + )); + + attributes.add(new NpcAttribute( + "width", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setWidth + )); + + return attributes; + } + + private static void setHeight(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float height; + try { + height = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setHeight(height); + } + + private static void setWidth(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float width; + try { + width = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setWidth(width); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/LivingEntityAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/LivingEntityAttributes.java new file mode 100644 index 00000000..ee64b825 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/LivingEntityAttributes.java @@ -0,0 +1,67 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.InteractionHand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class LivingEntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + /*attributes.add(new NpcAttribute( + "hurt", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setHurt + ));*/ + + attributes.add(new NpcAttribute( + "use_item", + List.of("main_hand", "off_hand", "none"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setUseItem + )); + + return attributes; + } + + private static void setHurt(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + boolean isHurt = Boolean.parseBoolean(value); + + if (isHurt) { + livingEntity.hurtDuration = 1; + livingEntity.hurtTime = 1; + livingEntity.hurtMarked = true; + livingEntity.animateHurt(0); + } else { + livingEntity.hurtDuration = 0; + livingEntity.hurtTime = 0; + livingEntity.hurtMarked = false; + } + } + + private static void setUseItem(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + switch (value.toUpperCase()) { + case "NONE" -> livingEntity.stopUsingItem(); + case "MAIN_HAND" -> livingEntity.startUsingItem(InteractionHand.MAIN_HAND, true); + case "OFF_HAND" -> livingEntity.startUsingItem(InteractionHand.OFF_HAND, true); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/PandaAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/PandaAttributes.java new file mode 100644 index 00000000..49d23924 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/PandaAttributes.java @@ -0,0 +1,101 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.animal.Panda; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PandaAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "gene", + Arrays.stream(Panda.Gene.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PANDA), + PandaAttributes::setGene + )); + + attributes.add(new NpcAttribute( + "eating", + List.of("true", "false"), + List.of(EntityType.PANDA), + PandaAttributes::setEating + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "onBack", "rolling"), + List.of(EntityType.PANDA), + PandaAttributes::setPose + )); + + return attributes; + } + + private static void setGene(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + Panda.Gene gene = Panda.Gene.valueOf(value.toUpperCase()); + panda.setMainGene(gene); + } + + private static void setPose(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(false); + } + case "sitting" -> { + panda.roll(false); + panda.setOnBack(false); + setFlag(panda, 8, true); //sitting + } + case "onback" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(true); + } + case "rolling" -> { + setFlag(panda, 8, false); //sitting + panda.setOnBack(false); + panda.roll(true); + } + } + } + + private static void setEating(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + boolean eating = Boolean.parseBoolean(value); + + panda.eat(eating); + } + + private static void setFlag(Panda panda, int mask, boolean value) { + EntityDataAccessor DATA_ID_FLAGS = (EntityDataAccessor) ReflectionUtils.getValue(panda, "DATA_ID_FLAGS"); + + byte b0 = panda.getEntityData().get(DATA_ID_FLAGS); + + if (value) { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 | mask)); + } else { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 & ~mask)); + } + + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/ParrotAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/ParrotAttributes.java new file mode 100644 index 00000000..d0204035 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/ParrotAttributes.java @@ -0,0 +1,59 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Parrot; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ParrotAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Parrot.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PARROT), + ParrotAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.PARROT), + ParrotAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + Parrot.Variant variant = Parrot.Variant.valueOf(value.toUpperCase()); + parrot.setVariant(variant); + } + + private static void setPose(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + parrot.setOrderedToSit(false); + parrot.setInSittingPose(false, false); + } + case "sitting" -> { + parrot.setOrderedToSit(true); + parrot.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/PigAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/PigAttributes.java new file mode 100644 index 00000000..acee320b --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/PigAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Pig; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PigAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "has_saddle", + List.of("true", "false"), + List.of(EntityType.PIG), + PigAttributes::setHasSaddle + )); + + return attributes; + } + + private static void setHasSaddle(Npc npc, String value) { + Pig pig = ReflectionHelper.getEntity(npc); + + boolean hasSaddle = Boolean.parseBoolean(value.toLowerCase()); + + pig.steering.setSaddle(hasSaddle); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/PiglinAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/PiglinAttributes.java new file mode 100644 index 00000000..c630468b --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/PiglinAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.monster.piglin.Piglin; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PiglinAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.PIGLIN), + PiglinAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Piglin piglin = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + piglin.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/PlayerAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/PlayerAttributes.java new file mode 100644 index 00000000..789b5082 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/PlayerAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.entity.player.Player; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PlayerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "crouching", "sleeping", "swimming", "sitting"), + List.of(EntityType.PLAYER), + PlayerAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Player player = ReflectionHelper.getEntity(npc); + + Pose pose = Pose.valueOf(value.toUpperCase()); + + EntityDataAccessor DATA_POSE = (EntityDataAccessor) ReflectionUtils.getStaticValue(Entity.class, "DATA_POSE"); // DATA_POSE + player.getEntityData().set(DATA_POSE, pose); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/RabbitAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/RabbitAttributes.java new file mode 100644 index 00000000..6ff20ab6 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/RabbitAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Rabbit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class RabbitAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Rabbit.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.RABBIT), + RabbitAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Rabbit rabbit = ReflectionHelper.getEntity(npc); + + Rabbit.Variant variant = Rabbit.Variant.valueOf(value.toUpperCase()); + rabbit.setVariant(variant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/SheepAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/SheepAttributes.java new file mode 100644 index 00000000..8deb7f5b --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/SheepAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.animal.Sheep; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SheepAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "wool_color", + Arrays.stream(DyeColor.values()).map(dyeColor -> dyeColor.name().toLowerCase()).toList(), + List.of(EntityType.SHEEP), + SheepAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "sheared", + Arrays.asList("true", "false"), + List.of(EntityType.SHEEP), + SheepAttributes::setSheared + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + sheep.setColor(DyeColor.byName(value.toLowerCase(), DyeColor.WHITE)); + } + + private static void setSheared(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + boolean sheared = Boolean.parseBoolean(value); + + sheep.setSheared(sheared); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/ShulkerAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/ShulkerAttributes.java new file mode 100644 index 00000000..d8c9e88b --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/ShulkerAttributes.java @@ -0,0 +1,55 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class ShulkerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.SHULKER), + ShulkerAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "shield", + List.of("open", "closed"), + List.of(EntityType.SHULKER), + ShulkerAttributes::setShield + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.PURPLE); + shulker.setVariant(Optional.of(color)); + } + + private static void setShield(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "closed" -> shulker.setRawPeekAmount(0); + case "open" -> shulker.setRawPeekAmount(Byte.MAX_VALUE); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/SlimeAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/SlimeAttributes.java new file mode 100644 index 00000000..dc2cedc2 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/SlimeAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.monster.Slime; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class SlimeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "size", + new ArrayList<>(), + List.of(EntityType.SLIME), + SlimeAttributes::setSize + )); + + return attributes; + } + + private static void setSize(Npc npc, String value) { + Slime slime = ReflectionHelper.getEntity(npc); + + int size; + try { + size = Integer.parseInt(value); + } catch (NumberFormatException e) { + return; + } + + slime.setSize(size, false); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/SpellCasterAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/SpellCasterAttributes.java new file mode 100644 index 00000000..d4cf5211 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/SpellCasterAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.monster.SpellcasterIllager; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Spellcaster; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SpellCasterAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "casting", + Arrays.stream(SpellcasterIllager.IllagerSpell.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Spellcaster.class.isAssignableFrom(type.getEntityClass())) + .toList(), + SpellCasterAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + SpellcasterIllager spellcasterIllager = ReflectionHelper.getEntity(npc); + + SpellcasterIllager.IllagerSpell spell = SpellcasterIllager.IllagerSpell.valueOf(value); + + spellcasterIllager.setIsCastingSpell(spell); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/TextDisplayAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/TextDisplayAttributes.java new file mode 100644 index 00000000..866f8802 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/TextDisplayAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import io.papermc.paper.adventure.PaperAdventure; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class TextDisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "text", + new ArrayList<>(), + List.of(EntityType.TEXT_DISPLAY), + TextDisplayAttributes::setText + )); + + return attributes; + } + + private static void setText(Npc npc, String value) { + Display.TextDisplay display = ReflectionHelper.getEntity(npc); + + Component text = MiniMessage.miniMessage().deserialize(value); + display.setText(PaperAdventure.asVanilla(text)); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/TropicalFishAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/TropicalFishAttributes.java new file mode 100644 index 00000000..f3b7bf18 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/TropicalFishAttributes.java @@ -0,0 +1,72 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.animal.TropicalFish; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TropicalFishAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pattern", + Arrays.stream(TropicalFish.Pattern.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPattern + )); + + attributes.add(new NpcAttribute( + "base_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setBaseColor + )); + + attributes.add(new NpcAttribute( + "pattern_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPatternColor + )); + + return attributes; + } + + private static void setPattern(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + TropicalFish.Pattern pattern = TropicalFish.Pattern.valueOf(value.toUpperCase()); + tropicalFish.setVariant(pattern); + } + + private static void setBaseColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), color, tropicalFish.getPatternColor()); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + + private static void setPatternColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getVariant(), tropicalFish.getBaseColor(), color); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/VexAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/VexAttributes.java new file mode 100644 index 00000000..beab257e --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/VexAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.world.entity.monster.Vex; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class VexAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "charging", + List.of("true", "false"), + List.of(EntityType.VEX), + VexAttributes::setCharging + )); + + return attributes; + } + + private static void setCharging(Npc npc, String value) { + Vex vex = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> vex.setIsCharging(true); + case "false" -> vex.setIsCharging(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/VillagerAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/VillagerAttributes.java new file mode 100644 index 00000000..682445a5 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/VillagerAttributes.java @@ -0,0 +1,54 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.entity.npc.VillagerType; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class VillagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "profession", + BuiltInRegistries.VILLAGER_PROFESSION.keySet().stream().map(ResourceLocation::getPath).toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setProfession + )); + + attributes.add(new NpcAttribute( + "type", + BuiltInRegistries.VILLAGER_TYPE.keySet().stream().map(ResourceLocation::getPath).toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setType + )); + + return attributes; + } + + private static void setProfession(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerProfession profession = BuiltInRegistries.VILLAGER_PROFESSION.getValue(ResourceLocation.tryParse(value)); + + villager.setVillagerData(villager.getVillagerData().setProfession(profession)); + } + + private static void setType(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + VillagerType type = BuiltInRegistries.VILLAGER_TYPE.getValue(ResourceLocation.tryParse(value)); + + villager.setVillagerData(villager.getVillagerData().setType(type)); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/WolfAttributes.java b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/WolfAttributes.java new file mode 100644 index 00000000..24565272 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_4/src/main/java/de/oliver/fancynpcs/v1_21_4/attributes/WolfAttributes.java @@ -0,0 +1,73 @@ +package de.oliver.fancynpcs.v1_21_4.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_4.ReflectionHelper; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.Wolf; +import net.minecraft.world.entity.animal.WolfVariant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class WolfAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.WOLF), + WolfAttributes::setPose + )); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.WOLF), + WolfAttributes::setAngry + )); + + attributes.add(new NpcAttribute( + "variant", + List.of("PALE", "SPOTTED", "SNOWY", "BLACK", "ASHEN", "RUSTY", "WOODS", "CHESTNUT", "STRIPED"), + List.of(EntityType.WOLF), + WolfAttributes::setVariant + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> wolf.setInSittingPose(false, false); + case "sitting" -> wolf.setInSittingPose(true, false); + } + } + + private static void setAngry(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + boolean angry = Boolean.parseBoolean(value.toLowerCase()); + + wolf.setRemainingPersistentAngerTime(angry ? 100 : 0); + } + + private static void setVariant(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + Registry registry = wolf.level().registryAccess().lookupOrThrow(Registries.WOLF_VARIANT); + WolfVariant variant = registry.getValue(ResourceLocation.parse(value.toLowerCase())); + + if (variant != null) { + wolf.setVariant(Holder.direct(variant)); + } + } +} diff --git a/plugins/fancynpcs/implementation_1_21_5/build.gradle.kts b/plugins/fancynpcs/implementation_1_21_5/build.gradle.kts new file mode 100644 index 00000000..c079e431 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + id("java-library") + id("io.papermc.paperweight.userdev") +} + +paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.MOJANG_PRODUCTION + +dependencies { + paperweight.paperDevBundle("1.21.5-no-moonrise-SNAPSHOT") + + compileOnly(project(":plugins:fancynpcs:api")) + compileOnly("de.oliver:FancyLib:37") + compileOnly("org.lushplugins:ChatColorHandler:5.1.3") +} + + +tasks { + javadoc { + options.encoding = Charsets.UTF_8.name() + } + + compileJava { + options.encoding = Charsets.UTF_8.name() + options.release = 21 + } +} \ No newline at end of file diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/Npc_1_21_5.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/Npc_1_21_5.java new file mode 100644 index 00000000..64d0ccdd --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/Npc_1_21_5.java @@ -0,0 +1,439 @@ +package de.oliver.fancynpcs.v1_21_5; + +import com.google.common.collect.ImmutableList; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.datafixers.util.Pair; +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.events.NpcSpawnEvent; +import de.oliver.fancynpcs.api.utils.NpcEquipmentSlot; +import io.papermc.paper.adventure.PaperAdventure; +import net.minecraft.Optionull; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.RemoteChatSession; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ClientInformation; +import net.minecraft.server.level.ServerEntity; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.*; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.scores.PlayerTeam; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.scores.Team; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.entity.Player; +import org.lushplugins.chatcolorhandler.ModernChatColorHandler; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class Npc_1_21_5 extends Npc { + + private final String localName; + private final UUID uuid; + private Entity npc; + private Display.TextDisplay sittingVehicle; + + public Npc_1_21_5(NpcData data) { + super(data); + + this.localName = generateLocalName(); + this.uuid = UUID.randomUUID(); + } + + @Override + public void create() { + MinecraftServer minecraftServer = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel serverLevel = ((CraftWorld) data.getLocation().getWorld()).getHandle(); + GameProfile gameProfile = new GameProfile(uuid, localName); + + if (data.getType() == org.bukkit.entity.EntityType.PLAYER) { + npc = new ServerPlayer(minecraftServer, serverLevel, new GameProfile(uuid, ""), ClientInformation.createDefault()); + ((ServerPlayer) npc).gameProfile = gameProfile; + } else { + Optional>> entityTypeReference = BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(data.getType().getKey())); + EntityType nmsType = entityTypeReference.get().value(); // TODO handle empty + EntityType.EntityFactory factory = (EntityType.EntityFactory) ReflectionUtils.getValue(nmsType, "factory"); // EntityType.factory + npc = factory.create(nmsType, serverLevel); + isTeamCreated.clear(); + } + } + + @Override + public void spawn(Player player) { + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc == null) { + return; + } + + if (!data.getLocation().getWorld().getName().equalsIgnoreCase(serverPlayer.level().getWorld().getName())) { + return; + } + + if (data.getSkinData() != null && data.getSkinData().hasTexture()) { + String value = data.getSkinData().getTextureValue(); + String signature = data.getSkinData().getTextureSignature(); + + ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues( + "textures", + ImmutableList.of(new Property("textures", value, signature)) + ); + } + + NpcSpawnEvent spawnEvent = new NpcSpawnEvent(this, player); + spawnEvent.callEvent(); + if (spawnEvent.isCancelled()) { + return; + } + + + if (npc instanceof ServerPlayer npcPlayer) { + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, getEntry(npcPlayer, serverPlayer)); + serverPlayer.connection.send(playerInfoPacket); + + if (data.isSpawnEntity()) { + npc.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + } + } + + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket( + npc.getId(), + npc.getUUID(), + data.getLocation().x(), + data.getLocation().y(), + data.getLocation().z(), + data.getLocation().getPitch(), + data.getLocation().getYaw(), + npc.getType(), + 0, + Vec3.ZERO, + data.getLocation().getYaw() + ); + serverPlayer.connection.send(addEntityPacket); + + isVisibleForPlayer.put(player.getUniqueId(), true); + + + int removeNpcsFromPlayerlistDelay = FancyNpcsPlugin.get().getFancyNpcConfig().getRemoveNpcsFromPlayerlistDelay(); + if (!data.isShowInTab() && removeNpcsFromPlayerlistDelay > 0) { + FancyNpcsPlugin.get().getNpcThread().schedule(() -> { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of(npc.getUUID())); + serverPlayer.connection.send(playerInfoRemovePacket); + }, removeNpcsFromPlayerlistDelay, TimeUnit.MILLISECONDS); + } + + update(player); + } + + @Override + public void remove(Player player) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + if (npc instanceof ServerPlayer npcPlayer) { + ClientboundPlayerInfoRemovePacket playerInfoRemovePacket = new ClientboundPlayerInfoRemovePacket(List.of((npcPlayer.getUUID()))); + serverPlayer.connection.send(playerInfoRemovePacket); + } + + // remove entity + ClientboundRemoveEntitiesPacket removeEntitiesPacket = new ClientboundRemoveEntitiesPacket(npc.getId()); + serverPlayer.connection.send(removeEntitiesPacket); + + // remove sitting vehicle + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + + isVisibleForPlayer.put(serverPlayer.getUUID(), false); + } + + @Override + public void lookAt(Player player, Location location) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setRot(location.getYaw(), location.getPitch()); + npc.setYHeadRot(location.getYaw()); + npc.setXRot(location.getPitch()); + npc.setYRot(location.getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket( + npc.getId(), + new PositionMoveRotation( + new Vec3(data.getLocation().getX(), data.getLocation().getY(), data.getLocation().getZ()), + Vec3.ZERO, + location.getYaw(), + location.getPitch() + ), + Set.of(), + false + ); + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (location.getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + } + + @Override + public void update(Player player) { + if (npc == null) { + return; + } + + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + PlayerTeam team = new PlayerTeam(new Scoreboard(), "npc-" + localName); + team.getPlayers().clear(); + team.getPlayers().add(npc instanceof ServerPlayer npcPlayer ? npcPlayer.getGameProfile().getName() : npc.getStringUUID()); + team.setColor(PaperAdventure.asVanilla(data.getGlowingColor())); + if (!data.isCollidable()) { + team.setCollisionRule(Team.CollisionRule.NEVER); + } + + net.kyori.adventure.text.Component displayName = ModernChatColorHandler.translate(data.getDisplayName(), serverPlayer.getBukkitEntity()); + Component vanillaComponent = PaperAdventure.asVanilla(displayName); + if (!(npc instanceof ServerPlayer)) { + npc.setCustomName(vanillaComponent); + npc.setCustomNameVisible(true); + } else { + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } + + if (data.getDisplayName().equalsIgnoreCase("")) { + team.setNameTagVisibility(Team.Visibility.NEVER); + npc.setCustomName(null); + npc.setCustomNameVisible(false); + } else { + team.setNameTagVisibility(Team.Visibility.ALWAYS); + } + + if (npc instanceof ServerPlayer npcPlayer) { + team.setPlayerPrefix(vanillaComponent); + npcPlayer.listName = vanillaComponent; + + EnumSet actions = EnumSet.noneOf(ClientboundPlayerInfoUpdatePacket.Action.class); + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME); + if (data.isShowInTab()) { + actions.add(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED); + } + + ClientboundPlayerInfoUpdatePacket playerInfoPacket = new ClientboundPlayerInfoUpdatePacket(actions, getEntry(npcPlayer, serverPlayer)); + serverPlayer.connection.send(playerInfoPacket); + } + + boolean isTeamCreatedForPlayer = this.isTeamCreated.getOrDefault(player.getUniqueId(), false); + serverPlayer.connection.send(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, !isTeamCreatedForPlayer)); + isTeamCreated.put(player.getUniqueId(), true); + + npc.setGlowingTag(data.isGlowing()); + + if (data.getEquipment() != null && data.getEquipment().size() > 0) { + List> equipmentList = new ArrayList<>(); + + for (NpcEquipmentSlot slot : data.getEquipment().keySet()) { + equipmentList.add(new Pair<>(EquipmentSlot.byName(slot.toNmsName()), CraftItemStack.asNMSCopy(data.getEquipment().get(slot)))); + } + + ClientboundSetEquipmentPacket setEquipmentPacket = new ClientboundSetEquipmentPacket(npc.getId(), equipmentList); + serverPlayer.connection.send(setEquipmentPacket); + } + + if (npc instanceof ServerPlayer) { + // Enable second layer of skin (https://wiki.vg/Entity_metadata#Player) + npc.getEntityData().set(net.minecraft.world.entity.player.Player.DATA_PLAYER_MODE_CUSTOMISATION, (byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40)); + } + + data.applyAllAttributes(this); + + refreshEntityData(player); + + if (data.isSpawnEntity() && data.getLocation() != null) { + move(player, true); + } + + NpcAttribute playerPoseAttr = FancyNpcsPlugin.get().getAttributeManager().getAttributeByName(org.bukkit.entity.EntityType.PLAYER, "pose"); + if (data.getAttributes().containsKey(playerPoseAttr)) { + String pose = data.getAttributes().get(playerPoseAttr); + + if (pose.equals("sitting")) { + setSitting(serverPlayer); + } else { + if (sittingVehicle != null) { + ClientboundRemoveEntitiesPacket removeSittingVehiclePacket = new ClientboundRemoveEntitiesPacket(sittingVehicle.getId()); + serverPlayer.connection.send(removeSittingVehiclePacket); + } + } + + } + + if (npc instanceof LivingEntity) { + Holder.Reference scaleAttribute = BuiltInRegistries.ATTRIBUTE.get(ResourceLocation.parse("minecraft:scale")).get(); + AttributeInstance attributeInstance = new AttributeInstance(scaleAttribute, (a) -> { + }); + attributeInstance.setBaseValue(data.getScale()); + + ClientboundUpdateAttributesPacket updateAttributesPacket = new ClientboundUpdateAttributesPacket(npc.getId(), List.of(attributeInstance)); + serverPlayer.connection.send(updateAttributesPacket); + + } + } + + @Override + protected void refreshEntityData(Player player) { + if (!isVisibleForPlayer.getOrDefault(player.getUniqueId(), false)) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + SynchedEntityData.DataItem[] itemsById = (SynchedEntityData.DataItem[]) ReflectionUtils.getValue(npc.getEntityData(), "itemsById"); // itemsById + List> entityData = new ArrayList<>(); + for (SynchedEntityData.DataItem dataItem : itemsById) { + entityData.add(dataItem.value()); + } + ClientboundSetEntityDataPacket setEntityDataPacket = new ClientboundSetEntityDataPacket(npc.getId(), entityData); + serverPlayer.connection.send(setEntityDataPacket); + } + + public void move(Player player, boolean swingArm) { + if (npc == null) { + return; + } + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + npc.setPosRaw(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + npc.setRot(data.getLocation().getYaw(), data.getLocation().getPitch()); + npc.setYHeadRot(data.getLocation().getYaw()); + npc.setXRot(data.getLocation().getPitch()); + npc.setYRot(data.getLocation().getYaw()); + + ClientboundTeleportEntityPacket teleportEntityPacket = new ClientboundTeleportEntityPacket( + npc.getId(), + new PositionMoveRotation( + new Vec3(data.getLocation().getX(), data.getLocation().getY(), data.getLocation().getZ()), + Vec3.ZERO, + data.getLocation().getYaw(), + data.getLocation().getPitch() + ), + Set.of(), + false + ); + serverPlayer.connection.send(teleportEntityPacket); + + float angelMultiplier = 256f / 360f; + ClientboundRotateHeadPacket rotateHeadPacket = new ClientboundRotateHeadPacket(npc, (byte) (data.getLocation().getYaw() * angelMultiplier)); + serverPlayer.connection.send(rotateHeadPacket); + + if (swingArm && npc instanceof ServerPlayer) { + ClientboundAnimatePacket animatePacket = new ClientboundAnimatePacket(npc, 0); + serverPlayer.connection.send(animatePacket); + } + } + + private ClientboundPlayerInfoUpdatePacket.Entry getEntry(ServerPlayer npcPlayer, ServerPlayer viewer) { + GameProfile profile = npcPlayer.getGameProfile(); + if (data.isMirrorSkin()) { + GameProfile newProfile = new GameProfile(profile.getId(), profile.getName()); + newProfile.getProperties().putAll(viewer.getGameProfile().getProperties()); + profile = newProfile; + } + + return new ClientboundPlayerInfoUpdatePacket.Entry( + npcPlayer.getUUID(), + profile, + data.isShowInTab(), + 69, + npcPlayer.gameMode.getGameModeForPlayer(), + npcPlayer.getTabListDisplayName(), + true, + -1, + Optionull.map(npcPlayer.getChatSession(), RemoteChatSession::asData) + ); + } + + public void setSitting(ServerPlayer serverPlayer) { + if (npc == null) { + return; + } + + if (sittingVehicle == null) { + sittingVehicle = new Display.TextDisplay(EntityType.TEXT_DISPLAY, ((CraftWorld) data.getLocation().getWorld()).getHandle()); + } + + sittingVehicle.setPos(data.getLocation().x(), data.getLocation().y(), data.getLocation().z()); + + ServerEntity serverEntity = new ServerEntity( + serverPlayer.serverLevel(), + sittingVehicle, + 0, + false, + packet -> { + }, + (p, l) -> { + }, + Set.of() + ); + ClientboundAddEntityPacket addEntityPacket = new ClientboundAddEntityPacket(sittingVehicle, serverEntity); + serverPlayer.connection.send(addEntityPacket); + + sittingVehicle.passengers = ImmutableList.of(npc); + + ClientboundSetPassengersPacket packet = new ClientboundSetPassengersPacket(sittingVehicle); + serverPlayer.connection.send(packet); + } + + @Override + public float getEyeHeight() { + return npc.getEyeHeight(); + } + + @Override + public int getEntityId() { + return npc.getId(); + } + + public Entity getNpc() { + return npc; + } +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/ReflectionHelper.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/ReflectionHelper.java new file mode 100644 index 00000000..1d18d27a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/ReflectionHelper.java @@ -0,0 +1,13 @@ +package de.oliver.fancynpcs.v1_21_5; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import net.minecraft.world.entity.Entity; + +public class ReflectionHelper { + + public static T getEntity(Npc npc) { + return (T) ReflectionUtils.getValue(npc, "npc"); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/AgeableMobAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/AgeableMobAttributes.java new file mode 100644 index 00000000..d40f7c1a --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/AgeableMobAttributes.java @@ -0,0 +1,38 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.AgeableMob; +import org.bukkit.entity.Ageable; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AgeableMobAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "baby", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Ageable.class.isAssignableFrom(type.getEntityClass())) + .toList(), + AgeableMobAttributes::setBaby + )); + + return attributes; + } + + private static void setBaby(Npc npc, String value) { + AgeableMob mob = ReflectionHelper.getEntity(npc); + + boolean isBaby = Boolean.parseBoolean(value); + + mob.setBaby(isBaby); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/AllayAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/AllayAttributes.java new file mode 100644 index 00000000..4f8d6cef --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/AllayAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.animal.allay.Allay; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class AllayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.ALLAY), + AllayAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Allay allay = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + allay.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/ArmadilloAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/ArmadilloAttributes.java new file mode 100644 index 00000000..1089ab54 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/ArmadilloAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.animal.armadillo.Armadillo; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class ArmadilloAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("idle", "rolling", "unrolling", "scared"), + List.of(EntityType.ARMADILLO), + ArmadilloAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Armadillo armadillo = ReflectionHelper.getEntity(npc); + + Armadillo.ArmadilloState state = Armadillo.ArmadilloState.valueOf(value.toUpperCase()); + + armadillo.switchToState(state); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/ArmorStandAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/ArmorStandAttributes.java new file mode 100644 index 00000000..ac3ea0af --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/ArmorStandAttributes.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class ArmorStandAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "show_arms", + List.of("true", "false"), + List.of(EntityType.ARMOR_STAND), + ArmorStandAttributes::setShowArms + )); + + return attributes; + } + + private static void setShowArms(Npc npc, String value) { + ArmorStand armorStand = ReflectionHelper.getEntity(npc); + + boolean showArms = Boolean.parseBoolean(value.toLowerCase()); + + armorStand.setShowArms(showArms); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/Attributes_1_21_5.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/Attributes_1_21_5.java new file mode 100644 index 00000000..43b6c10c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/Attributes_1_21_5.java @@ -0,0 +1,54 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.NpcAttribute; + +import java.util.ArrayList; +import java.util.List; + +public class Attributes_1_21_5 { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.addAll(EntityAttributes.getAllAttributes()); + attributes.addAll(LivingEntityAttributes.getAllAttributes()); + attributes.addAll(AgeableMobAttributes.getAllAttributes()); + attributes.addAll(IllagerAttributes.getAllAttributes()); + attributes.addAll(SpellCasterAttributes.getAllAttributes()); + + attributes.addAll(PlayerAttributes.getAllAttributes()); + attributes.addAll(SheepAttributes.getAllAttributes()); + attributes.addAll(VillagerAttributes.getAllAttributes()); + attributes.addAll(FrogAttributes.getAllAttributes()); + attributes.addAll(HorseAttributes.getAllAttributes()); + attributes.addAll(ParrotAttributes.getAllAttributes()); + attributes.addAll(AxolotlAttributes.getAllAttributes()); + attributes.addAll(TropicalFishAttributes.getAllAttributes()); + attributes.addAll(FoxAttributes.getAllAttributes()); + attributes.addAll(PandaAttributes.getAllAttributes()); + attributes.addAll(GoatAttributes.getAllAttributes()); + attributes.addAll(AllayAttributes.getAllAttributes()); + attributes.addAll(CamelAttributes.getAllAttributes()); + attributes.addAll(RabbitAttributes.getAllAttributes()); + attributes.addAll(PiglinAttributes.getAllAttributes()); + attributes.addAll(CatAttributes.getAllAttributes()); + attributes.addAll(ShulkerAttributes.getAllAttributes()); + attributes.addAll(WolfAttributes.getAllAttributes()); + attributes.addAll(SlimeAttributes.getAllAttributes()); + attributes.addAll(PigAttributes.getAllAttributes()); + attributes.addAll(CowAttributes.getAllAttributes()); + attributes.addAll(ChickenAttributes.getAllAttributes()); + attributes.addAll(ArmorStandAttributes.getAllAttributes()); + attributes.addAll(BeeAttributes.getAllAttributes()); + attributes.addAll(VexAttributes.getAllAttributes()); + attributes.addAll(ArmadilloAttributes.getAllAttributes()); + + attributes.addAll(DisplayAttributes.getAllAttributes()); + attributes.addAll(TextDisplayAttributes.getAllAttributes()); + attributes.addAll(BlockDisplayAttributes.getAllAttributes()); + attributes.addAll(InteractionAttributes.getAllAttributes()); + + return attributes; + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/AxolotlAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/AxolotlAttributes.java new file mode 100644 index 00000000..b4e38e0e --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/AxolotlAttributes.java @@ -0,0 +1,51 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.animal.axolotl.Axolotl; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AxolotlAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Axolotl.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "playing_dead", + List.of("true", "false"), + List.of(EntityType.AXOLOTL), + AxolotlAttributes::setPlayingDead + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + Axolotl.Variant variant = Axolotl.Variant.valueOf(value.toUpperCase()); + axolotl.setVariant(variant); + } + + private static void setPlayingDead(Npc npc, String value) { + Axolotl axolotl = ReflectionHelper.getEntity(npc); + + boolean playingDead = Boolean.parseBoolean(value); + axolotl.setPlayingDead(playingDead); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/BeeAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/BeeAttributes.java new file mode 100644 index 00000000..36f48c18 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/BeeAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.animal.Bee; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BeeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setAngry + )); + + attributes.add(new NpcAttribute( + "sting", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setSting + )); + + attributes.add(new NpcAttribute( + "nectar", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setNectar + )); + + attributes.add(new NpcAttribute( + "rolling", + List.of("true", "false"), + List.of(EntityType.BEE), + BeeAttributes::setRolling + )); + + return attributes; + } + + private static void setAngry(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRemainingPersistentAngerTime(1); + case "false" -> bee.setRemainingPersistentAngerTime(0); + } + } + + private static void setSting(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasStung(false); + case "false" -> bee.setHasStung(true); + } + } + + private static void setNectar(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setHasNectar(true); + case "false" -> bee.setHasNectar(false); + } + } + + private static void setRolling(Npc npc, String value) { + Bee bee = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> bee.setRolling(true); + case "false" -> bee.setRolling(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/BlockDisplayAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/BlockDisplayAttributes.java new file mode 100644 index 00000000..9fd81c6c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/BlockDisplayAttributes.java @@ -0,0 +1,41 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Display; +import net.minecraft.world.level.block.Block; +import org.bukkit.Material; +import org.bukkit.Registry; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class BlockDisplayAttributes { + + private static final List BLOCKS = Registry.MATERIAL.stream().filter(Material::isBlock).map(it -> it.key().value()).toList(); + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "block", + BLOCKS, + List.of(EntityType.BLOCK_DISPLAY), + BlockDisplayAttributes::setBlock + )); + + return attributes; + } + + private static void setBlock(Npc npc, String value) { + Display.BlockDisplay display = ReflectionHelper.getEntity(npc); + + Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(value.toLowerCase())); + + display.setBlockState(block.defaultBlockState()); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/CamelAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/CamelAttributes.java new file mode 100644 index 00000000..204f0f37 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/CamelAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.animal.camel.Camel; +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class CamelAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "dashing"), + List.of(EntityType.CAMEL), + CamelAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Camel camel = ReflectionHelper.getEntity(npc); + + Bukkit.getScheduler().runTask(FancyNpcsPlugin.get().getPlugin(), () -> { + switch (value.toLowerCase()) { + case "standing" -> { + camel.setDashing(false); + camel.standUp(); + } + case "sitting" -> { + camel.setDashing(false); + camel.sitDown(); + } + case "dashing" -> { + camel.standUpInstantly(); + camel.setDashing(true); + } + } + }); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/CatAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/CatAttributes.java new file mode 100644 index 00000000..98654417 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/CatAttributes.java @@ -0,0 +1,82 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.registries.VanillaRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.Cat; +import net.minecraft.world.entity.animal.CatVariant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class CatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + getCatVariantRegistry() + .listElementIds() + .map(id -> id.location().getPath()) + .toList(), + List.of(EntityType.CAT), + CatAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.CAT), + CatAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + + Holder variant = getCatVariantRegistry() + .get(ResourceKey.create( + Registries.CAT_VARIANT, + ResourceLocation.withDefaultNamespace(value.toLowerCase()) + )) + .orElseThrow(); + + cat.setVariant(variant); + } + + private static void setPose(Npc npc, String value) { + final Cat cat = ReflectionHelper.getEntity(npc); + switch (value.toLowerCase()) { + case "standing" -> { + cat.setInSittingPose(false, false); + cat.setLying(false); + } + case "sleeping" -> { + cat.setInSittingPose(false, false); + cat.setLying(true); + } + case "sitting" -> { + cat.setLying(false); + cat.setOrderedToSit(true); + cat.setInSittingPose(true, false); + } + } + } + + private static HolderLookup.RegistryLookup getCatVariantRegistry() { + return VanillaRegistries + .createLookup() + .lookup(Registries.CAT_VARIANT) + .orElseThrow(); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/ChickenAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/ChickenAttributes.java new file mode 100644 index 00000000..7c09a4e0 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/ChickenAttributes.java @@ -0,0 +1,57 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.registries.VanillaRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.Chicken; +import net.minecraft.world.entity.animal.ChickenVariant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class ChickenAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + getChickenVariantRegistry() + .listElementIds() + .map(id -> id.location().getPath()) + .toList(), + List.of(EntityType.CHICKEN), + ChickenAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Chicken cow = ReflectionHelper.getEntity(npc); + + Holder variant = getChickenVariantRegistry() + .get(ResourceKey.create( + Registries.CHICKEN_VARIANT, + ResourceLocation.withDefaultNamespace(value.toLowerCase()) + )) + .orElseThrow(); + + cow.setVariant(variant); + } + + private static HolderLookup.RegistryLookup getChickenVariantRegistry() { + return VanillaRegistries + .createLookup() + .lookup(Registries.CHICKEN_VARIANT) + .orElseThrow(); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/CowAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/CowAttributes.java new file mode 100644 index 00000000..ec2fe9d6 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/CowAttributes.java @@ -0,0 +1,57 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.registries.VanillaRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.Cow; +import net.minecraft.world.entity.animal.CowVariant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class CowAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + getCowVariantRegistry() + .listElementIds() + .map(id -> id.location().getPath()) + .toList(), + List.of(EntityType.COW), + CowAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Cow cow = ReflectionHelper.getEntity(npc); + + Holder variant = getCowVariantRegistry() + .get(ResourceKey.create( + Registries.COW_VARIANT, + ResourceLocation.withDefaultNamespace(value.toLowerCase()) + )) + .orElseThrow(); + + cow.setVariant(variant); + } + + private static HolderLookup.RegistryLookup getCowVariantRegistry() { + return VanillaRegistries + .createLookup() + .lookup(Registries.COW_VARIANT) + .orElseThrow(); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/DisplayAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/DisplayAttributes.java new file mode 100644 index 00000000..95ad6905 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/DisplayAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class DisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "billboard", + Arrays.stream(org.bukkit.entity.Display.Billboard.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TEXT_DISPLAY, EntityType.BLOCK_DISPLAY, EntityType.ITEM_DISPLAY), + DisplayAttributes::setBillboard + )); + + return attributes; + } + + private static void setBillboard(Npc npc, String value) { + Display display = ReflectionHelper.getEntity(npc); + + Display.BillboardConstraints billboard = Display.BillboardConstraints.valueOf(value.toUpperCase()); + display.setBillboardConstraints(billboard); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/EntityAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/EntityAttributes.java new file mode 100644 index 00000000..45f12d2c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/EntityAttributes.java @@ -0,0 +1,103 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.Entity; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class EntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "on_fire", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnFire + )); + + attributes.add(new NpcAttribute( + "invisible", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setInvisible + )); + + attributes.add(new NpcAttribute( + "silent", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setSilent + )); + + attributes.add(new NpcAttribute( + "shaking", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setShaking + )); + + attributes.add(new NpcAttribute( + "on_ground", + List.of("true", "false"), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setOnGround + )); + + /*attributes.add(new NpcAttribute( + "entity_pose", + Arrays.stream(Pose.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()).toList(), + EntityAttributes::setEntityPose + ));*/ + + return attributes; + } + + private static void setOnFire(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onFire = Boolean.parseBoolean(value); + + entity.setSharedFlagOnFire(onFire); + + } + + private static void setInvisible(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean invisible = Boolean.parseBoolean(value); + + entity.setInvisible(invisible); + } + + private static void setSilent(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean silent = Boolean.parseBoolean(value); + + entity.setSilent(silent); + } + + private static void setShaking(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean shaking = Boolean.parseBoolean(value); + + entity.setTicksFrozen(shaking ? entity.getTicksRequiredToFreeze() : 0); + } + + private static void setOnGround(Npc npc, String value) { + Entity entity = ReflectionHelper.getEntity(npc); + + boolean onGround = Boolean.parseBoolean(value); + + entity.setOnGround(onGround); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/FoxAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/FoxAttributes.java new file mode 100644 index 00000000..731c762f --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/FoxAttributes.java @@ -0,0 +1,66 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.animal.Fox; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FoxAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "type", + Arrays.stream(Fox.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.FOX), + FoxAttributes::setType + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sleeping", "sitting"), + List.of(EntityType.FOX), + FoxAttributes::setPose + )); + + return attributes; + } + + private static void setType(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + Fox.Variant type = Fox.Variant.valueOf(value.toUpperCase()); + fox.setVariant(type); + } + + private static void setPose(Npc npc, String value) { + Fox fox = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + fox.setIsCrouching(false); + fox.setSleeping(false); + fox.setSitting(false, false); + } + case "sleeping" -> { + fox.setSleeping(true); + fox.setSitting(false, false); + fox.setIsCrouching(false); + } + case "sitting" -> { + fox.setSitting(true, false); + fox.setSleeping(false); + fox.setIsCrouching(false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/FrogAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/FrogAttributes.java new file mode 100644 index 00000000..c8cebe07 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/FrogAttributes.java @@ -0,0 +1,56 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.registries.VanillaRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.frog.Frog; +import net.minecraft.world.entity.animal.frog.FrogVariant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class FrogAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + getFrogVariantRegistry() + .listElementIds() + .map(id -> id.location().getPath()) + .toList(), + List.of(EntityType.FROG), + FrogAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Frog frog = ReflectionHelper.getEntity(npc); + + Holder variant = getFrogVariantRegistry() + .get(ResourceKey.create( + Registries.FROG_VARIANT, + ResourceLocation.withDefaultNamespace(value.toLowerCase()) + )) + .orElseThrow(); + + frog.setVariant(variant); + } + + private static HolderLookup.RegistryLookup getFrogVariantRegistry() { + return VanillaRegistries + .createLookup() + .lookup(Registries.FROG_VARIANT) + .orElseThrow(); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/GoatAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/GoatAttributes.java new file mode 100644 index 00000000..40452804 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/GoatAttributes.java @@ -0,0 +1,44 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.animal.goat.Goat; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class GoatAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "horns", + List.of("none", "left", "right", "both"), + List.of(EntityType.GOAT), + GoatAttributes::setHorns + )); + + return attributes; + } + + private static void setHorns(Npc npc, String value) { + Goat goat = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "none" -> goat.removeHorns(); + case "both" -> goat.addHorns(); + case "left" -> { + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, false); + } + case "right" -> { + goat.getEntityData().set(Goat.DATA_HAS_RIGHT_HORN, true); + goat.getEntityData().set(Goat.DATA_HAS_LEFT_HORN, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/HorseAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/HorseAttributes.java new file mode 100644 index 00000000..81177589 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/HorseAttributes.java @@ -0,0 +1,84 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.animal.horse.Horse; +import net.minecraft.world.entity.animal.horse.Markings; +import net.minecraft.world.entity.animal.horse.Variant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class HorseAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "markings", + Arrays.stream(Markings.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.HORSE), + HorseAttributes::setMarkings + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("eating", "rearing", "standing"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && (type == EntityType.HORSE || type == EntityType.DONKEY || + type == EntityType.MULE || type == EntityType.SKELETON_HORSE || type == EntityType.ZOMBIE_HORSE)) + .toList(), + HorseAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Variant variant = Variant.valueOf(value.toUpperCase()); + horse.setVariantAndMarkings(variant, horse.getMarkings()); + } + + private static void setMarkings(Npc npc, String value) { + Horse horse = ReflectionHelper.getEntity(npc); + + Markings markings = Markings.valueOf(value.toUpperCase()); + horse.setVariantAndMarkings(horse.getVariant(), markings); + } + + private static void setPose(Npc npc, String value) { + net.minecraft.world.entity.animal.horse.AbstractHorse horse = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + horse.setEating(false); + horse.setForceStanding(false); + } + case "rearing" -> { + horse.setForceStanding(true); + horse.setEating(false); + } + case "eating" -> { + horse.setForceStanding(false); + horse.setEating(true); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/IllagerAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/IllagerAttributes.java new file mode 100644 index 00000000..d43afb51 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/IllagerAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.raid.Raider; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Illager; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class IllagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "celebrating", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Illager.class.isAssignableFrom(type.getEntityClass())) + .toList(), + IllagerAttributes::setCelebrating + )); + + return attributes; + } + + private static void setCelebrating(Npc npc, String value) { + Raider raider = ReflectionHelper.getEntity(npc); + + boolean isCelebrating = Boolean.parseBoolean(value); + + raider.setCelebrating(isCelebrating); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/InteractionAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/InteractionAttributes.java new file mode 100644 index 00000000..65032684 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/InteractionAttributes.java @@ -0,0 +1,60 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.Interaction; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class InteractionAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "height", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setHeight + )); + + attributes.add(new NpcAttribute( + "width", + new ArrayList<>(), + List.of(EntityType.INTERACTION), + InteractionAttributes::setWidth + )); + + return attributes; + } + + private static void setHeight(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float height; + try { + height = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setHeight(height); + } + + private static void setWidth(Npc npc, String value) { + Interaction interaction = ReflectionHelper.getEntity(npc); + + float width; + try { + width = Float.parseFloat(value); + } catch (NumberFormatException e) { + return; + } + + interaction.setWidth(width); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/LivingEntityAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/LivingEntityAttributes.java new file mode 100644 index 00000000..608cd36c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/LivingEntityAttributes.java @@ -0,0 +1,67 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.InteractionHand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class LivingEntityAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + /*attributes.add(new NpcAttribute( + "hurt", + List.of("true", "false"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setHurt + ));*/ + + attributes.add(new NpcAttribute( + "use_item", + List.of("main_hand", "off_hand", "none"), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && LivingEntity.class.isAssignableFrom(type.getEntityClass())) + .toList(), + LivingEntityAttributes::setUseItem + )); + + return attributes; + } + + private static void setHurt(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + boolean isHurt = Boolean.parseBoolean(value); + + if (isHurt) { + livingEntity.hurtDuration = 1; + livingEntity.hurtTime = 1; + livingEntity.hurtMarked = true; + livingEntity.animateHurt(0); + } else { + livingEntity.hurtDuration = 0; + livingEntity.hurtTime = 0; + livingEntity.hurtMarked = false; + } + } + + private static void setUseItem(Npc npc, String value) { + net.minecraft.world.entity.LivingEntity livingEntity = ReflectionHelper.getEntity(npc); + + switch (value.toUpperCase()) { + case "NONE" -> livingEntity.stopUsingItem(); + case "MAIN_HAND" -> livingEntity.startUsingItem(InteractionHand.MAIN_HAND, true); + case "OFF_HAND" -> livingEntity.startUsingItem(InteractionHand.OFF_HAND, true); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/PandaAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/PandaAttributes.java new file mode 100644 index 00000000..0757784b --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/PandaAttributes.java @@ -0,0 +1,101 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.animal.Panda; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PandaAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "gene", + Arrays.stream(Panda.Gene.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PANDA), + PandaAttributes::setGene + )); + + attributes.add(new NpcAttribute( + "eating", + List.of("true", "false"), + List.of(EntityType.PANDA), + PandaAttributes::setEating + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting", "onBack", "rolling"), + List.of(EntityType.PANDA), + PandaAttributes::setPose + )); + + return attributes; + } + + private static void setGene(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + Panda.Gene gene = Panda.Gene.valueOf(value.toUpperCase()); + panda.setMainGene(gene); + } + + private static void setPose(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(false); + } + case "sitting" -> { + panda.roll(false); + panda.setOnBack(false); + setFlag(panda, 8, true); //sitting + } + case "onback" -> { + setFlag(panda, 8, false); //sitting + panda.roll(false); + panda.setOnBack(true); + } + case "rolling" -> { + setFlag(panda, 8, false); //sitting + panda.setOnBack(false); + panda.roll(true); + } + } + } + + private static void setEating(Npc npc, String value) { + Panda panda = ReflectionHelper.getEntity(npc); + + boolean eating = Boolean.parseBoolean(value); + + panda.eat(eating); + } + + private static void setFlag(Panda panda, int mask, boolean value) { + EntityDataAccessor DATA_ID_FLAGS = (EntityDataAccessor) ReflectionUtils.getValue(panda, "DATA_ID_FLAGS"); + + byte b0 = panda.getEntityData().get(DATA_ID_FLAGS); + + if (value) { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 | mask)); + } else { + panda.getEntityData().set(DATA_ID_FLAGS, (byte) (b0 & ~mask)); + } + + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/ParrotAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/ParrotAttributes.java new file mode 100644 index 00000000..bdb093b3 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/ParrotAttributes.java @@ -0,0 +1,59 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.animal.Parrot; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ParrotAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Parrot.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.PARROT), + ParrotAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.PARROT), + ParrotAttributes::setPose + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + Parrot.Variant variant = Parrot.Variant.valueOf(value.toUpperCase()); + parrot.setVariant(variant); + } + + private static void setPose(Npc npc, String value) { + Parrot parrot = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> { + parrot.setOrderedToSit(false); + parrot.setInSittingPose(false, false); + } + case "sitting" -> { + parrot.setOrderedToSit(true); + parrot.setInSittingPose(true, false); + } + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/PigAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/PigAttributes.java new file mode 100644 index 00000000..fa277f7f --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/PigAttributes.java @@ -0,0 +1,76 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.registries.VanillaRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.animal.Pig; +import net.minecraft.world.entity.animal.PigVariant; +import net.minecraft.world.item.Items; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PigAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + getPigVariantRegistry() + .listElementIds() + .map(id -> id.location().getPath()) + .toList(), + List.of(EntityType.PIG), + PigAttributes::setVariant + )); + + attributes.add(new NpcAttribute( + "has_saddle", + List.of("true", "false"), + List.of(EntityType.PIG), + PigAttributes::setHasSaddle + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + final Pig pig = ReflectionHelper.getEntity(npc); + + Holder variant = getPigVariantRegistry() + .get(ResourceKey.create( + Registries.PIG_VARIANT, + ResourceLocation.withDefaultNamespace(value.toLowerCase()) + )) + .orElseThrow(); + + pig.setVariant(variant); + } + + private static void setHasSaddle(Npc npc, String value) { + Pig pig = ReflectionHelper.getEntity(npc); + + boolean hasSaddle = Boolean.parseBoolean(value.toLowerCase()); + + if (hasSaddle) { + pig.setItemSlot(EquipmentSlot.SADDLE, Items.SADDLE.getDefaultInstance()); + } + } + + private static HolderLookup.RegistryLookup getPigVariantRegistry() { + return VanillaRegistries + .createLookup() + .lookup(Registries.PIG_VARIANT) + .orElseThrow(); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/PiglinAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/PiglinAttributes.java new file mode 100644 index 00000000..f577eb2d --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/PiglinAttributes.java @@ -0,0 +1,34 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.monster.piglin.Piglin; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PiglinAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "dancing", + List.of("true", "false"), + List.of(EntityType.PIGLIN), + PiglinAttributes::setDancing + )); + + return attributes; + } + + private static void setDancing(Npc npc, String value) { + Piglin piglin = ReflectionHelper.getEntity(npc); + + boolean dancing = Boolean.parseBoolean(value); + piglin.setDancing(dancing); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/PlayerAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/PlayerAttributes.java new file mode 100644 index 00000000..618a0da4 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/PlayerAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancylib.ReflectionUtils; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.entity.player.Player; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class PlayerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "crouching", "sleeping", "swimming", "sitting"), + List.of(EntityType.PLAYER), + PlayerAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Player player = ReflectionHelper.getEntity(npc); + + Pose pose = Pose.valueOf(value.toUpperCase()); + + EntityDataAccessor DATA_POSE = (EntityDataAccessor) ReflectionUtils.getStaticValue(Entity.class, "DATA_POSE"); // DATA_POSE + player.getEntityData().set(DATA_POSE, pose); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/RabbitAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/RabbitAttributes.java new file mode 100644 index 00000000..c71a2f79 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/RabbitAttributes.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.animal.Rabbit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class RabbitAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "variant", + Arrays.stream(Rabbit.Variant.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.RABBIT), + RabbitAttributes::setVariant + )); + + return attributes; + } + + private static void setVariant(Npc npc, String value) { + Rabbit rabbit = ReflectionHelper.getEntity(npc); + + Rabbit.Variant variant = Rabbit.Variant.valueOf(value.toUpperCase()); + rabbit.setVariant(variant); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/SheepAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/SheepAttributes.java new file mode 100644 index 00000000..88cd4850 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/SheepAttributes.java @@ -0,0 +1,50 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.animal.sheep.Sheep; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SheepAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "wool_color", + Arrays.stream(DyeColor.values()).map(dyeColor -> dyeColor.name().toLowerCase()).toList(), + List.of(EntityType.SHEEP), + SheepAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "sheared", + Arrays.asList("true", "false"), + List.of(EntityType.SHEEP), + SheepAttributes::setSheared + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + sheep.setColor(DyeColor.byName(value.toLowerCase(), DyeColor.WHITE)); + } + + private static void setSheared(Npc npc, String value) { + Sheep sheep = ReflectionHelper.getEntity(npc); + + boolean sheared = Boolean.parseBoolean(value); + + sheep.setSheared(sheared); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/ShulkerAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/ShulkerAttributes.java new file mode 100644 index 00000000..bae4fb3f --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/ShulkerAttributes.java @@ -0,0 +1,54 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ShulkerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.SHULKER), + ShulkerAttributes::setColor + )); + + attributes.add(new NpcAttribute( + "shield", + List.of("open", "closed"), + List.of(EntityType.SHULKER), + ShulkerAttributes::setShield + )); + + return attributes; + } + + private static void setColor(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.PURPLE); + shulker.getEntityData().set(Shulker.DATA_COLOR_ID, (byte) color.getId()); + } + + private static void setShield(Npc npc, String value) { + Shulker shulker = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "closed" -> shulker.setRawPeekAmount(0); + case "open" -> shulker.setRawPeekAmount(Byte.MAX_VALUE); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/SlimeAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/SlimeAttributes.java new file mode 100644 index 00000000..6c1da98c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/SlimeAttributes.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.monster.Slime; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class SlimeAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "size", + new ArrayList<>(), + List.of(EntityType.SLIME), + SlimeAttributes::setSize + )); + + return attributes; + } + + private static void setSize(Npc npc, String value) { + Slime slime = ReflectionHelper.getEntity(npc); + + int size; + try { + size = Integer.parseInt(value); + } catch (NumberFormatException e) { + return; + } + + slime.setSize(size, false); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/SpellCasterAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/SpellCasterAttributes.java new file mode 100644 index 00000000..2d649cf5 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/SpellCasterAttributes.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.monster.SpellcasterIllager; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Spellcaster; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SpellCasterAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "casting", + Arrays.stream(SpellcasterIllager.IllagerSpell.values()).map(Enum::toString).toList(), + Arrays.stream(EntityType.values()) + .filter(type -> type.getEntityClass() != null && Spellcaster.class.isAssignableFrom(type.getEntityClass())) + .toList(), + SpellCasterAttributes::setPose + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + SpellcasterIllager spellcasterIllager = ReflectionHelper.getEntity(npc); + + SpellcasterIllager.IllagerSpell spell = SpellcasterIllager.IllagerSpell.valueOf(value); + + spellcasterIllager.setIsCastingSpell(spell); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/TextDisplayAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/TextDisplayAttributes.java new file mode 100644 index 00000000..99143ed2 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/TextDisplayAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import io.papermc.paper.adventure.PaperAdventure; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.world.entity.Display; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class TextDisplayAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "text", + new ArrayList<>(), + List.of(EntityType.TEXT_DISPLAY), + TextDisplayAttributes::setText + )); + + return attributes; + } + + private static void setText(Npc npc, String value) { + Display.TextDisplay display = ReflectionHelper.getEntity(npc); + + Component text = MiniMessage.miniMessage().deserialize(value); + display.setText(PaperAdventure.asVanilla(text)); + } +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/TropicalFishAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/TropicalFishAttributes.java new file mode 100644 index 00000000..82f823bb --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/TropicalFishAttributes.java @@ -0,0 +1,72 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.animal.TropicalFish; +import net.minecraft.world.item.DyeColor; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TropicalFishAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pattern", + Arrays.stream(TropicalFish.Pattern.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPattern + )); + + attributes.add(new NpcAttribute( + "base_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setBaseColor + )); + + attributes.add(new NpcAttribute( + "pattern_color", + Arrays.stream(DyeColor.values()) + .map(Enum::name) + .toList(), + List.of(EntityType.TROPICAL_FISH), + TropicalFishAttributes::setPatternColor + )); + + return attributes; + } + + private static void setPattern(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + TropicalFish.Pattern pattern = TropicalFish.Pattern.valueOf(value.toUpperCase()); + tropicalFish.setPackedVariant(pattern.getPackedId()); + } + + private static void setBaseColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getPattern(), color, tropicalFish.getPatternColor()); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + + private static void setPatternColor(Npc npc, String value) { + TropicalFish tropicalFish = ReflectionHelper.getEntity(npc); + + DyeColor color = DyeColor.byName(value.toLowerCase(), DyeColor.WHITE); + TropicalFish.Variant variant = new TropicalFish.Variant(tropicalFish.getPattern(), tropicalFish.getBaseColor(), color); + tropicalFish.setPackedVariant(variant.getPackedId()); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/VexAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/VexAttributes.java new file mode 100644 index 00000000..bd13c8db --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/VexAttributes.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.world.entity.monster.Vex; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class VexAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "charging", + List.of("true", "false"), + List.of(EntityType.VEX), + VexAttributes::setCharging + )); + + return attributes; + } + + private static void setCharging(Npc npc, String value) { + Vex vex = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "true" -> vex.setIsCharging(true); + case "false" -> vex.setIsCharging(false); + } + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/VillagerAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/VillagerAttributes.java new file mode 100644 index 00000000..3d083e6c --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/VillagerAttributes.java @@ -0,0 +1,55 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.entity.npc.VillagerType; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class VillagerAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "profession", + BuiltInRegistries.VILLAGER_PROFESSION.keySet().stream().map(ResourceLocation::getPath).toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setProfession + )); + + attributes.add(new NpcAttribute( + "type", + BuiltInRegistries.VILLAGER_TYPE.keySet().stream().map(ResourceLocation::getPath).toList(), + List.of(EntityType.VILLAGER), + VillagerAttributes::setType + )); + + return attributes; + } + + private static void setProfession(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + Holder profession = BuiltInRegistries.VILLAGER_PROFESSION.get(ResourceLocation.tryParse(value)).orElseThrow(); + + villager.setVillagerData(villager.getVillagerData().withProfession(profession)); + } + + private static void setType(Npc npc, String value) { + Villager villager = ReflectionHelper.getEntity(npc); + + Holder type = BuiltInRegistries.VILLAGER_TYPE.get(ResourceLocation.tryParse(value)).orElseThrow(); + + villager.setVillagerData(villager.getVillagerData().withType(type)); + } + +} diff --git a/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/WolfAttributes.java b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/WolfAttributes.java new file mode 100644 index 00000000..81808f98 --- /dev/null +++ b/plugins/fancynpcs/implementation_1_21_5/src/main/java/de/oliver/fancynpcs/v1_21_5/attributes/WolfAttributes.java @@ -0,0 +1,87 @@ +package de.oliver.fancynpcs.v1_21_5.attributes; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_21_5.ReflectionHelper; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.registries.VanillaRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.animal.wolf.Wolf; +import net.minecraft.world.entity.animal.wolf.WolfVariant; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class WolfAttributes { + + public static List getAllAttributes() { + List attributes = new ArrayList<>(); + + attributes.add(new NpcAttribute( + "pose", + List.of("standing", "sitting"), + List.of(EntityType.WOLF), + WolfAttributes::setPose + )); + + attributes.add(new NpcAttribute( + "angry", + List.of("true", "false"), + List.of(EntityType.WOLF), + WolfAttributes::setAngry + )); + + attributes.add(new NpcAttribute( + "variant", + getWolfVariantRegistry() + .listElementIds() + .map(id -> id.location().getPath()) + .toList(), + List.of(EntityType.WOLF), + WolfAttributes::setVariant + )); + + return attributes; + } + + private static void setPose(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + switch (value.toLowerCase()) { + case "standing" -> wolf.setInSittingPose(false, false); + case "sitting" -> wolf.setInSittingPose(true, false); + } + } + + private static void setAngry(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + boolean angry = Boolean.parseBoolean(value.toLowerCase()); + + wolf.setRemainingPersistentAngerTime(angry ? 100 : 0); + } + + private static void setVariant(Npc npc, String value) { + Wolf wolf = ReflectionHelper.getEntity(npc); + + Holder variant = getWolfVariantRegistry() + .get(ResourceKey.create( + Registries.WOLF_VARIANT, + ResourceLocation.withDefaultNamespace(value.toLowerCase()) + )) + .orElseThrow(); + + wolf.setVariant(variant); + } + + private static HolderLookup.RegistryLookup getWolfVariantRegistry() { + return VanillaRegistries + .createLookup() + .lookup(Registries.WOLF_VARIANT) + .orElseThrow(); + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/ActionManagerImpl.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/ActionManagerImpl.java new file mode 100644 index 00000000..36273426 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/ActionManagerImpl.java @@ -0,0 +1,33 @@ +package de.oliver.fancynpcs; + +import de.oliver.fancynpcs.api.actions.ActionManager; +import de.oliver.fancynpcs.api.actions.NpcAction; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class ActionManagerImpl implements ActionManager { + + private final Map actions = new ConcurrentHashMap<>(); + + @Override + public void registerAction(NpcAction action) { + actions.put(action.getName(), action); + } + + @Override + public NpcAction getActionByName(String name) { + return actions.getOrDefault(name, null); + } + + @Override + public void unregisterAction(NpcAction action) { + actions.remove(action.getName()); + } + + @Override + public List getAllActions() { + return List.copyOf(actions.values()); + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/AttributeManagerImpl.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/AttributeManagerImpl.java new file mode 100644 index 00000000..2b13304b --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/AttributeManagerImpl.java @@ -0,0 +1,71 @@ +package de.oliver.fancynpcs; + +import de.oliver.fancynpcs.api.AttributeManager; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.v1_19_4.attributes.Attributes_1_19_4; +import de.oliver.fancynpcs.v1_20_1.attributes.Attributes_1_20_1; +import de.oliver.fancynpcs.v1_20_2.attributes.Attributes_1_20_2; +import de.oliver.fancynpcs.v1_20_4.attributes.Attributes_1_20_4; +import de.oliver.fancynpcs.v1_20_6.attributes.Attributes_1_20_5; +import de.oliver.fancynpcs.v1_21_1.attributes.Attributes_1_21_1; +import de.oliver.fancynpcs.v1_21_3.attributes.Attributes_1_21_3; +import de.oliver.fancynpcs.v1_21_4.attributes.Attributes_1_21_4; +import de.oliver.fancynpcs.v1_21_5.attributes.Attributes_1_21_5; +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.List; + +public class AttributeManagerImpl implements AttributeManager { + + private List attributes; + + public AttributeManagerImpl() { + this.attributes = new ArrayList<>(); + init(); + } + + private void init() { + String mcVersion = Bukkit.getMinecraftVersion(); + switch (mcVersion) { + case "1.21.5" -> attributes = Attributes_1_21_5.getAllAttributes(); + case "1.21.4" -> attributes = Attributes_1_21_4.getAllAttributes(); + case "1.21.2", "1.21.3" -> attributes = Attributes_1_21_3.getAllAttributes(); + case "1.21", "1.21.1" -> attributes = Attributes_1_21_1.getAllAttributes(); + case "1.20.5", "1.20.6" -> attributes = Attributes_1_20_5.getAllAttributes(); + case "1.20.3", "1.20.4" -> attributes = Attributes_1_20_4.getAllAttributes(); + case "1.20.2" -> attributes = Attributes_1_20_2.getAllAttributes(); + case "1.20.1", "1.20" -> attributes = Attributes_1_20_1.getAllAttributes(); + case "1.19.4" -> attributes = Attributes_1_19_4.getAllAttributes(); + } + } + + @Override + public NpcAttribute getAttributeByName(EntityType type, String name) { + for (NpcAttribute attribute : attributes) { + if (attribute.getTypes().contains(type) && attribute.getName().equalsIgnoreCase(name)) { + return attribute; + } + } + + return null; + } + + @Override + public void registerAttribute(NpcAttribute attribute) { + attributes.add(attribute); + } + + @Override + public List getAllAttributes() { + return attributes; + } + + @Override + public List getAllAttributesForEntityType(EntityType type) { + return attributes.stream() + .filter(attribute -> attribute.getTypes().contains(type)) + .toList(); + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/FancyNpcs.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/FancyNpcs.java new file mode 100644 index 00000000..969f7c4f --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/FancyNpcs.java @@ -0,0 +1,589 @@ +package de.oliver.fancynpcs; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import de.oliver.fancyanalytics.api.FancyAnalyticsAPI; +import de.oliver.fancyanalytics.api.events.Event; +import de.oliver.fancyanalytics.api.metrics.MetricSupplier; +import de.oliver.fancyanalytics.logger.ExtendedFancyLogger; +import de.oliver.fancyanalytics.logger.LogLevel; +import de.oliver.fancyanalytics.logger.appender.Appender; +import de.oliver.fancyanalytics.logger.appender.ConsoleAppender; +import de.oliver.fancyanalytics.logger.appender.JsonAppender; +import de.oliver.fancylib.FancyLib; +import de.oliver.fancylib.Metrics; +import de.oliver.fancylib.VersionConfig; +import de.oliver.fancylib.featureFlags.FeatureFlag; +import de.oliver.fancylib.featureFlags.FeatureFlagConfig; +import de.oliver.fancylib.serverSoftware.ServerSoftware; +import de.oliver.fancylib.serverSoftware.schedulers.BukkitScheduler; +import de.oliver.fancylib.serverSoftware.schedulers.FancyScheduler; +import de.oliver.fancylib.serverSoftware.schedulers.FoliaScheduler; +import de.oliver.fancylib.translations.Language; +import de.oliver.fancylib.translations.TextConfig; +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancylib.versionFetcher.MasterVersionFetcher; +import de.oliver.fancylib.versionFetcher.VersionFetcher; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.NpcManager; +import de.oliver.fancynpcs.api.actions.types.*; +import de.oliver.fancynpcs.api.skins.SkinManager; +import de.oliver.fancynpcs.commands.CloudCommandManager; +import de.oliver.fancynpcs.listeners.*; +import de.oliver.fancynpcs.skins.SkinManagerImpl; +import de.oliver.fancynpcs.skins.SkinUtils; +import de.oliver.fancynpcs.skins.cache.SkinCacheFile; +import de.oliver.fancynpcs.skins.cache.SkinCacheMemory; +import de.oliver.fancynpcs.tracker.TurnToPlayerTracker; +import de.oliver.fancynpcs.tracker.VisibilityTracker; +import de.oliver.fancynpcs.utils.OldSkinCacheMigrator; +import de.oliver.fancynpcs.v1_19_4.Npc_1_19_4; +import de.oliver.fancynpcs.v1_19_4.PacketReader_1_19_4; +import de.oliver.fancynpcs.v1_20.PacketReader_1_20; +import de.oliver.fancynpcs.v1_20_1.Npc_1_20_1; +import de.oliver.fancynpcs.v1_20_2.Npc_1_20_2; +import de.oliver.fancynpcs.v1_20_4.Npc_1_20_4; +import de.oliver.fancynpcs.v1_20_6.Npc_1_20_6; +import de.oliver.fancynpcs.v1_21_1.Npc_1_21_1; +import de.oliver.fancynpcs.v1_21_3.Npc_1_21_3; +import de.oliver.fancynpcs.v1_21_4.Npc_1_21_4; +import de.oliver.fancynpcs.v1_21_5.Npc_1_21_5; +import org.apache.maven.artifact.versioning.ComparableVersion; +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +import static java.util.concurrent.CompletableFuture.supplyAsync; + +public class FancyNpcs extends JavaPlugin implements FancyNpcsPlugin { + + public static final FeatureFlag PLAYER_NPCS_FEATURE_FLAG = new FeatureFlag("player-npcs", "Every player can only manage the npcs they have created", false); + public static final FeatureFlag USE_NATIVE_THREADS_FEATURE_FLAG = new FeatureFlag("use-native-threads", "Use native threads instead of virtual threads.", false); + public static final FeatureFlag ENABLE_DEBUG_MODE_FEATURE_FLAG = new FeatureFlag("enable-debug-mode", "Enable debug mode", false); + + private static FancyNpcs instance; + private final ExtendedFancyLogger fancyLogger; + private final ScheduledExecutorService npcThread; + private final FancyScheduler scheduler; + private final FancyNpcsConfigImpl config; + private final VersionConfig versionConfig; + private final FeatureFlagConfig featureFlagConfig; + private final VersionFetcher versionFetcher; + private final FancyAnalyticsAPI fancyAnalytics; + private CloudCommandManager commandManager; + private TextConfig textConfig; + private Translator translator; + private Function npcAdapter; + private NpcManagerImpl npcManager; + private AttributeManagerImpl attributeManager; + private SkinManagerImpl skinManager; + private ActionManagerImpl actionManager; + private VisibilityTracker visibilityTracker; + private boolean usingPlotSquared; + + public FancyNpcs() { + instance = this; + + Appender consoleAppender = new ConsoleAppender("[{loggerName}] ({threadName}) {logLevel}: {message}"); + String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date(System.currentTimeMillis())); + File logsFile = new File("plugins/FancyNpcs/logs/FN-logs-" + date + ".txt"); + if (!logsFile.exists()) { + try { + logsFile.getParentFile().mkdirs(); + logsFile.createNewFile(); + } catch (Exception e) { + e.printStackTrace(); + } + } + JsonAppender jsonAppender = new JsonAppender(false, false, true, logsFile.getPath()); + this.fancyLogger = new ExtendedFancyLogger("FancyNpcs", LogLevel.INFO, List.of(consoleAppender, jsonAppender), new ArrayList<>()); + + this.npcThread = Executors.newSingleThreadScheduledExecutor( + new ThreadFactoryBuilder() + .setNameFormat("FancyNpcs-Npcs") + .build() + ); + this.scheduler = ServerSoftware.isFolia() + ? new FoliaScheduler(instance) + : new BukkitScheduler(instance); + this.config = new FancyNpcsConfigImpl(); + this.versionFetcher = new MasterVersionFetcher(getName()); + this.versionConfig = new VersionConfig(this, versionFetcher); + + fancyAnalytics = new FancyAnalyticsAPI("ca2baf32-1fd2-4baa-a38a-f12ed8ab24a4", "Y7EP2jJjYWExZjdmMDkwNTQ5ZmRbIGUI"); + fancyAnalytics.getConfig().setDisableLogging(true); + + this.featureFlagConfig = new FeatureFlagConfig(this); + } + + public static FancyNpcs getInstance() { + return instance; + } + + @Override + public void onLoad() { + // Load feature flags + featureFlagConfig.addFeatureFlag(PLAYER_NPCS_FEATURE_FLAG); + featureFlagConfig.addFeatureFlag(USE_NATIVE_THREADS_FEATURE_FLAG); + featureFlagConfig.addFeatureFlag(ENABLE_DEBUG_MODE_FEATURE_FLAG); + featureFlagConfig.load(); + + if (ENABLE_DEBUG_MODE_FEATURE_FLAG.isEnabled()) { + fancyLogger.setCurrentLevel(LogLevel.DEBUG); + } + + String mcVersion = Bukkit.getMinecraftVersion(); + + switch (mcVersion) { + case "1.21.5" -> npcAdapter = Npc_1_21_5::new; + case "1.21.4" -> npcAdapter = Npc_1_21_4::new; + case "1.21.2", "1.21.3" -> npcAdapter = Npc_1_21_3::new; + case "1.21", "1.21.1" -> npcAdapter = Npc_1_21_1::new; + case "1.20.5", "1.20.6" -> npcAdapter = Npc_1_20_6::new; + case "1.20.3", "1.20.4" -> npcAdapter = Npc_1_20_4::new; + case "1.20.2" -> npcAdapter = Npc_1_20_2::new; + case "1.20.1", "1.20" -> npcAdapter = Npc_1_20_1::new; + case "1.19.4" -> npcAdapter = Npc_1_19_4::new; + default -> npcAdapter = null; + } + + npcManager = new NpcManagerImpl(this, npcAdapter); + + PluginManager pluginManager = Bukkit.getPluginManager(); + + if (npcAdapter == null) { + fancyAnalytics.sendEvent(new Event("pluginLoadingWithUnsupportedVersion") + .withProperty("version", mcVersion) + .withProperty("pluginVersion", getPluginMeta().getVersion()) + ); + + fancyLogger.error("Unsupported minecraft server version."); + getLogger().warning("--------------------------------------------------"); + getLogger().warning("Unsupported minecraft server version."); + getLogger().warning("This plugin only supports 1.19.4 - 1.21.4"); + getLogger().warning("Disabling the FancyNpcs plugin."); + getLogger().warning("--------------------------------------------------"); + pluginManager.disablePlugin(this); + return; + } + } + + @Override + public void onEnable() { + if (npcAdapter == null) { + return; + } + + new FancyLib(instance); // Initialize FancyLib + + String mcVersion = Bukkit.getMinecraftVersion(); + + config.reload(); + + attributeManager = new AttributeManagerImpl(); + actionManager = new ActionManagerImpl(); + actionManager.registerAction(new MessageAction()); + actionManager.registerAction(new PlayerCommandAction()); + actionManager.registerAction(new PlayerCommandAsOpAction()); + actionManager.registerAction(new ConsoleCommandAction()); + actionManager.registerAction(new SendToServerAction()); + actionManager.registerAction(new WaitAction()); + actionManager.registerAction(new ExecuteRandomActionAction()); + actionManager.registerAction(new BlockUntilDoneAction()); + actionManager.registerAction(new NeedPermissionAction()); + actionManager.registerAction(new PlaySoundAction()); + + skinManager = new SkinManagerImpl(new SkinCacheFile(), new SkinCacheMemory()); + OldSkinCacheMigrator.migrate(); + + textConfig = new TextConfig("#E33239", "#AD1D23", "#81E366", "#E3CA66", "#E36666", ""); + translator = new Translator(textConfig); + translator.loadLanguages(getDataFolder().getAbsolutePath()); + final Language selectedLanguage = translator.getLanguages().stream() + .filter(language -> language.getLanguageName().equals(config.getLanguage())) + .findFirst().orElse(translator.getFallbackLanguage()); + translator.setSelectedLanguage(selectedLanguage); + + versionConfig.load(); + + final ComparableVersion currentVersion = new ComparableVersion(versionConfig.getVersion()); + supplyAsync(getVersionFetcher()::fetchNewestVersion) + .thenApply(Objects::requireNonNull) + .whenComplete((newest, error) -> { + if (error != null || newest.compareTo(currentVersion) <= 0) { + return; // could not get the newest version or already on latest + } + + fancyLogger.warn("You are not using the latest version of the FancyNpcs plugin."); + getLogger().warning(""" + + ------------------------------------------------------- + You are not using the latest version of the FancyNpcs plugin. + Please update to the newest version (%s). + %s + ------------------------------------------------------- + """.formatted(newest, getVersionFetcher().getDownloadUrl())); + }); + + if (!ServerSoftware.isPaper()) { + fancyLogger.warn("You are not using Paper as server software."); + getLogger().warning("--------------------------------------------------"); + getLogger().warning("It is recommended to use Paper as server software."); + getLogger().warning("Because you are not using paper, the plugin"); + getLogger().warning("might not work correctly."); + getLogger().warning("--------------------------------------------------"); + } + + registerMetrics(); + checkIfPluginVersionUpdated(); + + PluginManager pluginManager = Bukkit.getPluginManager(); + usingPlotSquared = pluginManager.isPluginEnabled("PlotSquared"); + + // register listeners + pluginManager.registerEvents(new PlayerJoinListener(), instance); + pluginManager.registerEvents(new PlayerQuitListener(), instance); + pluginManager.registerEvents(new PlayerTeleportListener(), instance); + pluginManager.registerEvents(new PlayerChangedWorldListener(), instance); + pluginManager.registerEvents(skinManager, instance); + + // use packet injection method + switch (mcVersion) { + case "1.19.4" -> pluginManager.registerEvents(new PacketReader_1_19_4(), instance); + case "1.20" -> pluginManager.registerEvents(new PacketReader_1_20(), instance); + default -> pluginManager.registerEvents(new PlayerUseUnknownEntityListener(), instance); + } + + if (PLAYER_NPCS_FEATURE_FLAG.isEnabled()) { + pluginManager.registerEvents(new PlayerNpcsListener(), instance); + } + + // using bungee plugin channel + getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); + + // load config + scheduler.runTaskLater(null, 20L * 5, () -> npcManager.loadNpcs()); + + visibilityTracker = new VisibilityTracker(); + + npcThread.scheduleAtFixedRate(new TurnToPlayerTracker(), 0, 50, TimeUnit.MILLISECONDS); + npcThread.scheduleAtFixedRate(visibilityTracker, 0, (config.getNpcUpdateVisibilityInterval() * 50L), TimeUnit.MILLISECONDS); + + int autosaveInterval = config.getAutoSaveInterval(); + if (config.isEnableAutoSave() && config.getAutoSaveInterval() > 0) { + scheduler.runTaskTimerAsynchronously(60L * 20L, autosaveInterval * 60L * 20L, () -> npcManager.saveNpcs(false)); + } + + int npcUpdateInterval = config.getNpcUpdateInterval(); + npcThread.scheduleAtFixedRate(() -> { + List npcs = new ArrayList<>(npcManager.getAllNpcs()); + for (Npc npc : npcs) { + boolean skinUpdated = npc.getData().getSkinData() != null && + !npc.getData().getSkinData().getIdentifier().isEmpty() && + SkinUtils.isPlaceholder(npc.getData().getSkinData().getIdentifier()); + + boolean displayNameUpdated = npc.getData().getDisplayName() != null && + !npc.getData().getDisplayName().isEmpty() && + SkinUtils.isPlaceholder(npc.getData().getDisplayName()); + + if (skinUpdated || displayNameUpdated) { + npc.removeForAll(); + npc.create(); + npc.spawnForAll(); + } + } + }, 3, npcUpdateInterval, TimeUnit.MINUTES); + + // Creating new instance of CloudCommandManager and registering all needed components. + // NOTE: Brigadier is disabled by default. More detailed information about that can be found in CloudCommandManager class. + if (config.isRegisterCommands()) { + commandManager = new CloudCommandManager(this, false) + .registerArguments() + .registerExceptionHandlers() + .registerCommands(); + } else { + getLogger().warning("Commands and related components have not been registered. This can be changed by setting 'register_commands' to true, and restarting the server."); + } + + fancyLogger.info("FancyNpcs (" + versionConfig.getVersion() + ") has been enabled."); + } + + @Override + public void onDisable() { + getServer().getMessenger().unregisterOutgoingPluginChannel(this); + if (npcManager != null) { + npcManager.saveNpcs(true); + } + + fancyLogger.info("FancyNpcs has been disabled."); + } + + private void registerMetrics() { + boolean isDevelopmentBuild = !versionConfig.getBuild().equalsIgnoreCase("undefined"); + + Metrics metrics = new Metrics(this, 17543); + metrics.addCustomChart(new Metrics.SingleLineChart("total_npcs", () -> npcManager.getAllNpcs().size())); + metrics.addCustomChart(new Metrics.SimplePie("update_notifications", () -> config.isMuteVersionNotification() ? "No" : "Yes")); + metrics.addCustomChart(new Metrics.SimplePie("using_development_build", () -> isDevelopmentBuild ? "Yes" : "No")); + + fancyAnalytics.registerMinecraftPluginMetrics(instance); + fancyAnalytics.getExceptionHandler().registerLogger(getLogger()); + fancyAnalytics.getExceptionHandler().registerLogger(Bukkit.getLogger()); + fancyAnalytics.getExceptionHandler().registerLogger(fancyLogger); + + fancyAnalytics.registerStringMetric(new MetricSupplier<>("commit_hash", () -> versionConfig.getHash().substring(0, 7))); + + + fancyAnalytics.registerStringMetric(new MetricSupplier<>("server_size", () -> { + long onlinePlayers = Bukkit.getOnlinePlayers().size(); + + if (onlinePlayers == 0) { + return "empty"; + } + + if (onlinePlayers <= 25) { + return "small"; + } + + if (onlinePlayers <= 100) { + return "medium"; + } + + if (onlinePlayers <= 500) { + return "large"; + } + + return "very_large"; + })); + + fancyAnalytics.registerNumberMetric(new MetricSupplier<>("amount_npcs", () -> (double) npcManager.getAllNpcs().size())); + fancyAnalytics.registerStringMetric(new MetricSupplier<>("enabled_update_notifications", () -> config.isMuteVersionNotification() ? "false" : "true")); + fancyAnalytics.registerStringMetric(new MetricSupplier<>("enabled_player_npcs_fflag", () -> PLAYER_NPCS_FEATURE_FLAG.isEnabled() ? "true" : "false")); + fancyAnalytics.registerStringMetric(new MetricSupplier<>("using_development_build", () -> isDevelopmentBuild ? "true" : "false")); + fancyAnalytics.registerStringMetric(new MetricSupplier<>("language", () -> translator.getSelectedLanguage().getLanguageCode())); + + fancyAnalytics.registerNumberMetric(new MetricSupplier<>("avg_interaction_cooldown", () -> { + double sum = 0; + int count = 0; + for (Npc npc : npcManager.getAllNpcs()) { + if (npc.getData().getInteractionCooldown() > 0) { + sum += npc.getData().getInteractionCooldown(); + count++; + } + } + + if (count == 0) { + return 0.0; + } + + return sum / count; + })); + + fancyAnalytics.registerNumberMetric(new MetricSupplier<>("amount_npcs_interaction_cooldown_longer_than_5min", () -> { + long count = npcManager.getAllNpcs().stream() + .filter(npc -> npc.getData().getInteractionCooldown() > 300) + .count(); + + return (double) count; + })); + + fancyAnalytics.registerNumberMetric(new MetricSupplier<>("amount_non_persistent_npcs", () -> { + long count = npcManager.getAllNpcs().stream() + .filter(npc -> !npc.isSaveToFile()) + .count(); + + return (double) count; + })); + + fancyAnalytics.registerNumberMetric(new MetricSupplier<>("amount_not_player_npcs", () -> { + long count = npcManager.getAllNpcs().stream() + .filter(npc -> npc.getData().getType() != EntityType.PLAYER) + .count(); + + return (double) count; + })); + + fancyAnalytics.registerStringArrayMetric(new MetricSupplier<>("npc_type", () -> { + return npcManager.getAllNpcs().stream() + .map(npc -> npc.getData().getType().name()) + .toArray(String[]::new); + })); + + + fancyAnalytics.registerNumberMetric(new MetricSupplier<>("amount_npcs_having_attributes", () -> { + long count = npcManager.getAllNpcs().stream() + .filter(npc -> !npc.getData().getAttributes().isEmpty()) + .count(); + + return (double) count; + })); + + fancyAnalytics.registerNumberMetric(new MetricSupplier<>("amount_npc_actions", () -> { + long count = 0; + + for (Npc npc : npcManager.getAllNpcs()) { + count += npc.getData().getActions().values().size(); + } + + return (double) count; + })); + + + fancyAnalytics.initialize(); + } + + private void checkIfPluginVersionUpdated() { + String currentVersion = versionConfig.getVersion(); + String lastVersion = "N/A"; + + File versionFile = new File(getDataFolder(), "version.yml"); + if (!versionFile.exists()) { + try { + Files.write(versionFile.toPath(), currentVersion.getBytes()); + } catch (IOException e) { + fancyLogger.warn("Could not write version file."); + return; + } + } else { + try { + lastVersion = new String(Files.readAllBytes(versionFile.toPath())); + } catch (IOException e) { + fancyLogger.warn("Could not read version file."); + return; + } + } + + if (!lastVersion.equals(currentVersion)) { + fancyLogger.info("Plugin has been updated from version " + lastVersion + " to " + currentVersion + "."); + fancyAnalytics.sendEvent( + new Event("PluginVersionUpdated") + .withProperty("from", lastVersion) + .withProperty("to", currentVersion) + ); + + try { + Files.write(versionFile.toPath(), currentVersion.getBytes()); + } catch (IOException e) { + fancyLogger.warn("Could not write version file."); + } + } + } + + @Override + public Thread newThread(String name, Runnable runnable) { + if (USE_NATIVE_THREADS_FEATURE_FLAG.isEnabled()) { + return new Thread(runnable, name); + } + + return Thread.ofVirtual().name(name).unstarted(runnable); + } + + public ExtendedFancyLogger getFancyLogger() { + return fancyLogger; + } + + @Override + public ScheduledExecutorService getNpcThread() { + return npcThread; + } + + @Override + public Function getNpcAdapter() { + return npcAdapter; + } + + @Override + public FancyScheduler getScheduler() { + return scheduler; + } + + public NpcManagerImpl getNpcManagerImpl() { + return npcManager; + } + + @Override + public NpcManager getNpcManager() { + return npcManager; + } + + @Override + public AttributeManagerImpl getAttributeManager() { + return attributeManager; + } + + @Override + public SkinManager getSkinManager() { + return skinManager; + } + + public SkinManagerImpl getSkinManagerImpl() { + return skinManager; + } + + @Override + public ActionManagerImpl getActionManager() { + return actionManager; + } + + @Override + public FancyNpcsConfigImpl getFancyNpcConfig() { + return config; + } + + public VersionConfig getVersionConfig() { + return versionConfig; + } + + public CloudCommandManager getCommandManager() { + return commandManager; + } + + @Override + public Translator getTranslator() { + return translator; + } + + public TextConfig getTextConfig() { + return textConfig; + } + + public FeatureFlagConfig getFeatureFlagConfig() { + return featureFlagConfig; + } + + public VersionFetcher getVersionFetcher() { + return versionFetcher; + } + + public FancyAnalyticsAPI getFancyAnalytics() { + return fancyAnalytics; + } + + public VisibilityTracker getVisibilityTracker() { + return visibilityTracker; + } + + public boolean isUsingPlotSquared() { + return usingPlotSquared; + } + + @Override + public JavaPlugin getPlugin() { + return instance; + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/FancyNpcsConfigImpl.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/FancyNpcsConfigImpl.java new file mode 100644 index 00000000..7e2cf672 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/FancyNpcsConfigImpl.java @@ -0,0 +1,231 @@ +package de.oliver.fancynpcs; + +import de.oliver.fancylib.ConfigHelper; +import de.oliver.fancynpcs.api.FancyNpcsConfig; +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.*; +import java.util.stream.Collectors; + +public class FancyNpcsConfigImpl implements FancyNpcsConfig { + + /** + * Currently active/selected language. + */ + private String language; + + /** + * Whether invisible NPCs should not be sent to the player. + */ + private boolean skipInvisibleNpcs; + + /** + * Indicates whether interaction cooldown messages are disabled. + */ + private boolean disabledInteractionCooldownMessage; + + /** + * Indicates whether version notifications are muted. + */ + private boolean muteVersionNotification; + + /** + * Indicates whether autosave is enabled. + */ + private boolean enableAutoSave; + + /** + * The interval at which autosave is performed. + */ + private int autoSaveInterval; + + /** + * The interval at which the NPC is updated. + * Only if the skin or displayName is a placeholder. + */ + private int npcUpdateInterval; + + /** + * The interval at which NPC visibility is updated. In ticks. + */ + private int npcUpdateVisibilityInterval; + + /** + * Indicates whether commands should be registered. + *

+ * This is useful for users who want to use the plugin's API only. + */ + private boolean registerCommands; + + /** + * The distance at which NPCs turn to the player. + */ + private int turnToPlayerDistance; + + /** + * Indicates whether direction of NPC should be reset when leaving their range. + */ + private boolean turnToPlayerResetToInitialDirection; + + /** + * The distance at which NPCs are visible. + */ + private int visibilityDistance; + + /** + * The delay in ticks to remove NPCs from the player list. + * Increase this value if you have problems with skins not loading correctly when joining or switching worlds. + */ + private int removeNpcsFromPlayerlistDelay; + + /** + * The API key for the MineSkin API. + */ + private String mineskinApiKey; + + /** + * The commands that are blocked for NPCs in the message. + */ + private List blockedCommands; + + /** + * The maximum number of NPCs per permission. (for the 'player-npcs' feature flag only) + */ + private Map maxNpcsPerPermission; + + public void reload() { + FancyNpcs.getInstance().reloadConfig(); + FileConfiguration config = FancyNpcs.getInstance().getConfig(); + + language = (String) ConfigHelper.getOrDefault(config, "language", "default"); + config.setInlineComments("language", List.of("Language to use for translatable messages.")); + + skipInvisibleNpcs = (boolean) ConfigHelper.getOrDefault(config, "skip_invisible_npcs", true); + config.setInlineComments("skip_invisible_npcs", List.of("Whether invisible NPCs should not be sent to the player.")); + + disabledInteractionCooldownMessage = (boolean) ConfigHelper.getOrDefault(config, "disable_interaction_cooldown_message", false); + config.setInlineComments("disable_interaction_cooldown_message", List.of("Whether interaction cooldown messages are disabled.")); + + muteVersionNotification = (boolean) ConfigHelper.getOrDefault(config, "mute_version_notification", false); + config.setInlineComments("mute_version_notification", List.of("Whether version notifications are muted.")); + + enableAutoSave = (boolean) ConfigHelper.getOrDefault(config, "enable_autosave", true); + config.setInlineComments("enable_autosave", List.of("Whether autosave is enabled.")); + + autoSaveInterval = (int) ConfigHelper.getOrDefault(config, "autosave_interval", 15); + config.setInlineComments("autosave_interval", List.of("The interval at which autosave is performed in minutes.")); + + npcUpdateInterval = (int) ConfigHelper.getOrDefault(config, "npc_update_interval", 30); + config.setInlineComments("npc_update_skin_interval", List.of("The interval at which the NPC is updated (in minutes). Only if the skin or displayName is a placeholder.")); + + npcUpdateVisibilityInterval = (int) ConfigHelper.getOrDefault(config, "npc_update_visibility_interval", 20); + config.setInlineComments("npc_update_visibility_interval", List.of("The interval at which the NPC visibility is updated (in ticks).")); + + registerCommands = (boolean) ConfigHelper.getOrDefault(config, "register_commands", true); + config.setInlineComments("register_commands", List.of("Whether the plugin should register its commands.")); + + turnToPlayerDistance = (int) ConfigHelper.getOrDefault(config, "turn_to_player_distance", 5); + config.setInlineComments("turn_to_player_distance", List.of("The distance at which NPCs turn to the player.")); + + turnToPlayerResetToInitialDirection = (boolean) ConfigHelper.getOrDefault(config, "turn_to_player_reset_to_initial_direction", false); + config.setInlineComments("turn_to_player_reset_to_initial_direction", List.of("Whether direction of NPC should be reset when leaving their turning range.")); + + visibilityDistance = (int) ConfigHelper.getOrDefault(config, "visibility_distance", 20); + config.setInlineComments("visibility_distance", List.of("The distance at which NPCs are visible.")); + + mineskinApiKey = (String) ConfigHelper.getOrDefault(config, "mineskin_api_key", ""); + config.setInlineComments("mineskin_api_key", List.of("The API key for the MineSkin API. This will be used to load skins faster. You can get an API key at https://mineskin.org/account.")); + + removeNpcsFromPlayerlistDelay = (int) ConfigHelper.getOrDefault(config, "remove_npcs_from_playerlist_delay", 2000); + config.setInlineComments("remove_npcs_from_playerlist_delay", List.of("The delay in milliseconds to remove NPCs from the player list. Increase this value if you have problems with skins not loading correctly when joining or switching worlds. You can set it to -1, if you don't have any npcs using the show_in_tab feature.")); + + blockedCommands = (List) ConfigHelper.getOrDefault(config, "blocked_commands", Arrays.asList("op", "ban")); + config.setInlineComments("blocked_commands", List.of("The commands that are blocked for NPCs in the message.")); + + + if (!config.isSet("max-npcs")) { + List> entries = new ArrayList<>(); + entries.add(Map.of("fancynpcs.max-npcs.5", 5)); + entries.add(Map.of("fancynpcs.max-npcs.10", 10)); + config.set("max-npcs", entries); + config.setInlineComments("max-npcs", List.of("The maximum number of NPCs per permission. (for the 'player-npcs' feature flag only)")); + this.maxNpcsPerPermission = new HashMap<>(); + this.maxNpcsPerPermission.put("fancynpcs.max-npcs.5", 5); + this.maxNpcsPerPermission.put("fancynpcs.max-npcs.10", 10); + } else { + this.maxNpcsPerPermission = config.getMapList("max-npcs").stream() + .flatMap(map -> map.entrySet().stream()) + .collect(Collectors.toMap(entry -> (String) entry.getKey(), entry -> (Integer) entry.getValue())); + } + + FancyNpcs.getInstance().saveConfig(); + } + + public String getLanguage() { + return language; + } + + public boolean isSkipInvisibleNpcs() { + return skipInvisibleNpcs; + } + + public boolean isInteractionCooldownMessageDisabled() { + return disabledInteractionCooldownMessage; + } + + public boolean isMuteVersionNotification() { + return muteVersionNotification; + } + + public boolean isEnableAutoSave() { + return enableAutoSave; + } + + public int getAutoSaveInterval() { + return autoSaveInterval; + } + + public int getNpcUpdateInterval() { + return npcUpdateInterval; + } + + public int getNpcUpdateVisibilityInterval() { + return npcUpdateVisibilityInterval; + } + + public boolean isRegisterCommands() { + return registerCommands; + } + + public boolean isTurnToPlayerResetToInitialDirection() { + return turnToPlayerResetToInitialDirection; + } + + public int getTurnToPlayerDistance() { + return turnToPlayerDistance; + } + + public int getVisibilityDistance() { + return visibilityDistance; + } + + public int getRemoveNpcsFromPlayerlistDelay() { + return removeNpcsFromPlayerlistDelay; + } + + public String getMineSkinApiKey() { + if (mineskinApiKey.isEmpty()) { + return null; + } + + return mineskinApiKey; + } + + public List getBlockedCommands() { + return blockedCommands; + } + + public Map getMaxNpcsPerPermission() { + return maxNpcsPerPermission; + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/NpcManagerImpl.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/NpcManagerImpl.java new file mode 100644 index 00000000..099642f3 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/NpcManagerImpl.java @@ -0,0 +1,474 @@ +package de.oliver.fancynpcs; + +import de.oliver.fancyanalytics.logger.ExtendedFancyLogger; +import de.oliver.fancylib.serverSoftware.ServerSoftware; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.NpcManager; +import de.oliver.fancynpcs.api.actions.ActionTrigger; +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.api.events.NpcsLoadedEvent; +import de.oliver.fancynpcs.api.skins.SkinData; +import de.oliver.fancynpcs.api.utils.NpcEquipmentSlot; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.WorldCreator; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.ApiStatus; + +import java.io.File; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +public class NpcManagerImpl implements NpcManager { + + private final JavaPlugin plugin; + private final ExtendedFancyLogger logger; + private final Function npcAdapter; + private final File npcConfigFile; + private final Map npcs; // npc id -> npc + private boolean isLoaded; + + public NpcManagerImpl(JavaPlugin plugin, Function npcAdapter) { + this.plugin = plugin; + this.logger = FancyNpcs.getInstance().getFancyLogger(); + this.npcAdapter = npcAdapter; + npcs = new ConcurrentHashMap<>(); + npcConfigFile = new File("plugins" + File.separator + "FancyNpcs" + File.separator + "npcs.yml"); + isLoaded = false; + } + + public void registerNpc(Npc npc) { + if (!FancyNpcs.PLAYER_NPCS_FEATURE_FLAG.isEnabled() && npcs.values().stream().anyMatch(npc1 -> npc1.getData().getName().equals(npc.getData().getName()))) { + throw new IllegalStateException("An NPC with this name already exists"); + } else { + npcs.put(npc.getData().getId(), npc); + } + } + + public void removeNpc(Npc npc) { + npcs.remove(npc.getData().getId()); + + YamlConfiguration npcConfig = YamlConfiguration.loadConfiguration(npcConfigFile); + npcConfig.set("npcs." + npc.getData().getId(), null); + try { + npcConfig.save(npcConfigFile); + } catch (IOException e) { + logger.error("Could not save npc config file"); + logger.error(e); + } + } + + @ApiStatus.Internal + @Override + public Npc getNpc(int entityId) { + for (Npc npc : npcs.values()) { + if (npc.getEntityId() == entityId) { + return npc; + } + } + + return null; + } + + @Override + public Npc getNpc(String name) { + for (Npc npc : npcs.values()) { + if (npc.getData().getName().equalsIgnoreCase(name)) { + return npc; + } + } + + return null; + } + + @Override + public Npc getNpcById(String id) { + for (Npc npc : npcs.values()) { + if (npc.getData().getId().equals(id)) { + return npc; + } + } + + return null; + } + + @Override + public Npc getNpc(String name, UUID creator) { + for (Npc npc : npcs.values()) { + if (npc.getData().getCreator().equals(creator) && npc.getData().getName().equalsIgnoreCase(name)) { + return npc; + } + } + + return null; + } + + public Collection getAllNpcs() { + return npcs.values(); + } + + public void saveNpcs(boolean force) { + if (!isLoaded) { + return; + } + + if (!npcConfigFile.exists()) { + try { + npcConfigFile.createNewFile(); + } catch (IOException e) { + logger.error("Could not create npc config file"); + logger.error(e); + return; + } + } + + YamlConfiguration npcConfig = YamlConfiguration.loadConfiguration(npcConfigFile); + + for (Npc npc : npcs.values()) { + if (!npc.isSaveToFile()) { + continue; + } + + boolean shouldSave = force || npc.isDirty(); + if (!shouldSave) { + continue; + } + + NpcData data = npc.getData(); + + npcConfig.set("npcs." + data.getId() + ".message", null); //TODO: remove in when new interaction system is added + npcConfig.set("npcs." + data.getId() + ".playerCommand", null); //TODO: remove in when new interaction system is added + npcConfig.set("npcs." + data.getId() + ".serverCommand", null); //TODO: remove in when new interaction system is added + npcConfig.set("npcs." + data.getId() + ".mirrorSkin", null); //TODO: remove in next version + npcConfig.set("npcs." + data.getId() + ".skin.value", null); //TODO: remove in next version + npcConfig.set("npcs." + data.getId() + ".skin.signature", null); //TODO: remove in next version + + npcConfig.set("npcs." + data.getId() + ".name", data.getName()); + npcConfig.set("npcs." + data.getId() + ".creator", data.getCreator().toString()); + npcConfig.set("npcs." + data.getId() + ".displayName", data.getDisplayName()); + npcConfig.set("npcs." + data.getId() + ".type", data.getType().name()); + npcConfig.set("npcs." + data.getId() + ".location.world", data.getLocation().getWorld().getName()); + npcConfig.set("npcs." + data.getId() + ".location.x", data.getLocation().getX()); + npcConfig.set("npcs." + data.getId() + ".location.y", data.getLocation().getY()); + npcConfig.set("npcs." + data.getId() + ".location.z", data.getLocation().getZ()); + npcConfig.set("npcs." + data.getId() + ".location.yaw", data.getLocation().getYaw()); + npcConfig.set("npcs." + data.getId() + ".location.pitch", data.getLocation().getPitch()); + npcConfig.set("npcs." + data.getId() + ".showInTab", data.isShowInTab()); + npcConfig.set("npcs." + data.getId() + ".spawnEntity", data.isSpawnEntity()); + npcConfig.set("npcs." + data.getId() + ".collidable", data.isCollidable()); + npcConfig.set("npcs." + data.getId() + ".glowing", data.isGlowing()); + npcConfig.set("npcs." + data.getId() + ".glowingColor", data.getGlowingColor().toString()); + npcConfig.set("npcs." + data.getId() + ".turnToPlayer", data.isTurnToPlayer()); + npcConfig.set("npcs." + data.getId() + ".messages", null); + npcConfig.set("npcs." + data.getId() + ".playerCommands", null); + npcConfig.set("npcs." + data.getId() + ".serverCommands", null); + npcConfig.set("npcs." + data.getId() + ".sendMessagesRandomly", null); + npcConfig.set("npcs." + data.getId() + ".interactionCooldown", data.getInteractionCooldown()); + npcConfig.set("npcs." + data.getId() + ".scale", data.getScale()); + npcConfig.set("npcs." + data.getId() + ".visibility_distance", data.getVisibilityDistance()); + + if (data.getSkinData() != null) { + npcConfig.set("npcs." + data.getId() + ".skin.identifier", data.getSkinData().getIdentifier()); + npcConfig.set("npcs." + data.getId() + ".skin.variant", data.getSkinData().getVariant().name()); + } else { + npcConfig.set("npcs." + data.getId() + ".skin.identifier", null); + } + npcConfig.set("npcs." + data.getId() + ".skin.mirrorSkin", data.isMirrorSkin()); + + if (data.getEquipment() != null) { + for (Map.Entry entry : data.getEquipment().entrySet()) { + npcConfig.set("npcs." + data.getId() + ".equipment." + entry.getKey().name(), entry.getValue()); + } + } + + for (NpcAttribute attribute : FancyNpcs.getInstance().getAttributeManager().getAllAttributesForEntityType(data.getType())) { + String value = data.getAttributes().getOrDefault(attribute, null); + npcConfig.set("npcs." + data.getId() + ".attributes." + attribute.getName(), value); + } + + npcConfig.set("npcs." + data.getId() + ".actions", null); + for (Map.Entry> entry : npc.getData().getActions().entrySet()) { + for (NpcAction.NpcActionData actionData : entry.getValue()) { + if (actionData == null) { + continue; + } + + npcConfig.set("npcs." + data.getId() + ".actions." + entry.getKey().name() + "." + actionData.order() + ".action", actionData.action().getName()); + npcConfig.set("npcs." + data.getId() + ".actions." + entry.getKey().name() + "." + actionData.order() + ".value", actionData.value()); + } + } + + npc.setDirty(false); + } + + try { + npcConfig.save(npcConfigFile); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void loadNpcs() { + npcs.clear(); + YamlConfiguration npcConfig = YamlConfiguration.loadConfiguration(npcConfigFile); + + if (!npcConfig.isConfigurationSection("npcs")) { + isLoaded = true; + return; + } + + for (String id : npcConfig.getConfigurationSection("npcs").getKeys(false)) { + String name = npcConfig.getString("npcs." + id + ".name"); + if (name == null) name = id; + + String creatorStr = npcConfig.getString("npcs." + id + ".creator"); + UUID creator = creatorStr == null ? null : UUID.fromString(creatorStr); + + String displayName = npcConfig.getString("npcs." + id + ".displayName", ""); + EntityType type = EntityType.valueOf(npcConfig.getString("npcs." + id + ".type", "PLAYER").toUpperCase()); + + Location location = null; + + try { + location = npcConfig.getLocation("npcs." + id + ".location"); + } catch (Exception ignored) { + logger.warn("Could not load location for npc '" + id + "'"); + } + + if (location == null) { + String worldName = npcConfig.getString("npcs." + id + ".location.world"); + World world = Bukkit.getWorld(worldName); + + if (world == null) { + world = (!ServerSoftware.isFolia()) ? new WorldCreator(worldName).createWorld() : null; + } + + if (world == null) { + logger.info("Could not load npc '" + id + "', because the world '" + worldName + "' is not loaded"); + continue; + } + + double x = npcConfig.getDouble("npcs." + id + ".location.x"); + double y = npcConfig.getDouble("npcs." + id + ".location.y"); + double z = npcConfig.getDouble("npcs." + id + ".location.z"); + float yaw = (float) npcConfig.getDouble("npcs." + id + ".location.yaw"); + float pitch = (float) npcConfig.getDouble("npcs." + id + ".location.pitch"); + + location = new Location(world, x, y, z, yaw, pitch); + } + + SkinData skin = null; + String skinIdentifier = npcConfig.getString("npcs." + id + ".skin.identifier", npcConfig.getString("npcs." + id + ".skin.uuid", "")); + String skinVariantStr = npcConfig.getString("npcs." + id + ".skin.variant", SkinData.SkinVariant.AUTO.name()); + SkinData.SkinVariant skinVariant = SkinData.SkinVariant.valueOf(skinVariantStr); + if (!skinIdentifier.isEmpty()) { + skin = FancyNpcs.getInstance().getSkinManagerImpl().getByIdentifier(skinIdentifier, skinVariant); + } + + + if (npcConfig.isSet("npcs." + id + ".skin.value") && npcConfig.isSet("npcs." + id + ".skin.signature")) { + // using old skin system --> take backup + takeBackup(npcConfig); + + String value = npcConfig.getString("npcs." + id + ".skin.value"); + String signature = npcConfig.getString("npcs." + id + ".skin.signature"); + + if (value != null && !value.isEmpty() && signature != null && !signature.isEmpty()) { + SkinData oldSkin = new SkinData(skinIdentifier, SkinData.SkinVariant.AUTO, value, signature); + FancyNpcs.getInstance().getSkinManagerImpl().getFileCache().addSkin(oldSkin); + FancyNpcs.getInstance().getSkinManagerImpl().getMemCache().addSkin(oldSkin); + } + } + + boolean oldMirrorSkin = npcConfig.getBoolean("npcs." + id + ".mirrorSkin"); //TODO: remove in next version + boolean mirrorSkin = oldMirrorSkin || npcConfig.getBoolean("npcs." + id + ".skin.mirrorSkin"); + + boolean showInTab = npcConfig.getBoolean("npcs." + id + ".showInTab"); + boolean spawnEntity = npcConfig.getBoolean("npcs." + id + ".spawnEntity"); + boolean collidable = npcConfig.getBoolean("npcs." + id + ".collidable", true); + boolean glowing = npcConfig.getBoolean("npcs." + id + ".glowing"); + NamedTextColor glowingColor = NamedTextColor.NAMES.value(npcConfig.getString("npcs." + id + ".glowingColor", "white")); + boolean turnToPlayer = npcConfig.getBoolean("npcs." + id + ".turnToPlayer"); + + Map> actions = new ConcurrentHashMap<>(); + + //TODO: remove these fields next version + boolean sendMessagesRandomly = npcConfig.getBoolean("npcs." + id + ".sendMessagesRandomly", false); + List playerCommands = npcConfig.getStringList("npcs." + id + ".playerCommands"); + List messages = npcConfig.getStringList("npcs." + id + ".messages"); + List serverCommands = npcConfig.getStringList("npcs." + id + ".serverCommands"); + + List migrateActionList = new ArrayList<>(); + int actionOrder = 0; + + for (String playerCommand : playerCommands) { + migrateActionList.add(new NpcAction.NpcActionData(++actionOrder, FancyNpcs.getInstance().getActionManager().getActionByName("player_command"), playerCommand)); + } + + for (String serverCommand : serverCommands) { + migrateActionList.add(new NpcAction.NpcActionData(++actionOrder, FancyNpcs.getInstance().getActionManager().getActionByName("console_command"), serverCommand)); + } + + if (sendMessagesRandomly && !messages.isEmpty()) { + migrateActionList.add(new NpcAction.NpcActionData(++actionOrder, FancyNpcs.getInstance().getActionManager().getActionByName("execute_random_action"), "")); + } + + for (String message : messages) { + migrateActionList.add(new NpcAction.NpcActionData(++actionOrder, FancyNpcs.getInstance().getActionManager().getActionByName("message"), message)); + } + + if (!migrateActionList.isEmpty()) { + takeBackup(npcConfig); + actions.put(ActionTrigger.ANY_CLICK, migrateActionList); + } + + ConfigurationSection actiontriggerSection = npcConfig.getConfigurationSection("npcs." + id + ".actions"); + if (actiontriggerSection != null) { + actiontriggerSection.getKeys(false).forEach(trigger -> { + ActionTrigger actionTrigger = ActionTrigger.getByName(trigger); + if (actionTrigger == null) { + logger.warn("Could not find action trigger: " + trigger); + return; + } + + List actionList = new ArrayList<>(); + ConfigurationSection actionsSection = npcConfig.getConfigurationSection("npcs." + id + ".actions." + trigger); + if (actionsSection != null) { + actionsSection.getKeys(false).forEach(order -> { + String actionName = npcConfig.getString("npcs." + id + ".actions." + trigger + "." + order + ".action"); + String value = npcConfig.getString("npcs." + id + ".actions." + trigger + "." + order + ".value"); + NpcAction action = FancyNpcs.getInstance().getActionManager().getActionByName(actionName); + if (action == null) { + logger.warn("Could not find action: " + actionName); + return; + } + + try { + actionList.add(new NpcAction.NpcActionData(Integer.parseInt(order), action, value)); + } catch (NumberFormatException e) { + logger.warn("Could not parse order: " + order); + } + }); + + actions.put(actionTrigger, actionList); + } + }); + } + + //TODO: add migration for sendMessagesRandomly + + float interactionCooldown = (float) npcConfig.getDouble("npcs." + id + ".interactionCooldown", 0); + float scale = (float) npcConfig.getDouble("npcs." + id + ".scale", 1); + int visibilityDistance = npcConfig.getInt("npcs." + id + ".visibility_distance", -1); + + Map attributes = new HashMap<>(); + if (npcConfig.isConfigurationSection("npcs." + id + ".attributes")) { + for (String attrName : npcConfig.getConfigurationSection("npcs." + id + ".attributes").getKeys(false)) { + NpcAttribute attribute = FancyNpcs.getInstance().getAttributeManager().getAttributeByName(type, attrName); + if (attribute == null) { + logger.warn("Could not find attribute: " + attrName); + continue; + } + + String value = npcConfig.getString("npcs." + id + ".attributes." + attrName); + if (!attribute.isValidValue(value)) { + logger.warn("Invalid value for attribute: " + attrName); + continue; + } + + attributes.put(attribute, value); + } + } + + NpcData data = new NpcData( + id, + name, + creator, + displayName, + skin, + location, + showInTab, + spawnEntity, + collidable, + glowing, + glowingColor, + type, + new HashMap<>(), + turnToPlayer, + null, + actions, + interactionCooldown, + scale, + visibilityDistance, + attributes, + mirrorSkin + ); + Npc npc = npcAdapter.apply(data); + + if (npcConfig.isConfigurationSection("npcs." + id + ".equipment")) { + for (String equipmentSlotStr : npcConfig.getConfigurationSection("npcs." + id + ".equipment").getKeys(false)) { + NpcEquipmentSlot equipmentSlot = NpcEquipmentSlot.parse(equipmentSlotStr); + ItemStack item = npcConfig.getItemStack("npcs." + id + ".equipment." + equipmentSlotStr); + npc.getData().addEquipment(equipmentSlot, item); + } + } + + npc.create(); + registerNpc(npc); + } + + isLoaded = true; + new NpcsLoadedEvent().callEvent(); + } + + public void reloadNpcs() { + Collection npcCopy = new ArrayList<>(getAllNpcs()); + npcs.clear(); + for (Npc npc : npcCopy) { + npc.removeForAll(); + } + + loadNpcs(); + } + + private void takeBackup(YamlConfiguration npcConfig) { + String folderPath = "plugins" + File.separator + "FancyNpcs" + File.separator + "/backups"; + File backupDir = new File(folderPath); + if (!backupDir.exists()) { + backupDir.mkdirs(); + } + + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + String backupFileName = "npcs-" + formatter.format(now) + ".yml"; + File backupFile = new File(folderPath + File.separator + backupFileName); + if (backupFile.exists()) { + backupFile.delete(); + } + + try { + backupFile.createNewFile(); + } catch (IOException e) { + logger.error("Could not create backup file for NPCs"); + } + + try { + npcConfig.save(backupFile); + } catch (IOException e) { + logger.error("Could not save backup file for NPCs"); + } + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/CloudCommandManager.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/CloudCommandManager.java new file mode 100644 index 00000000..533e43d9 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/CloudCommandManager.java @@ -0,0 +1,220 @@ +package de.oliver.fancynpcs.commands; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancylib.translations.message.Message; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.commands.arguments.ActionTriggerArgument; +import de.oliver.fancynpcs.commands.arguments.ActionTypeArgument; +import de.oliver.fancynpcs.commands.arguments.LocationArgument; +import de.oliver.fancynpcs.commands.arguments.NpcArgument; +import de.oliver.fancynpcs.commands.exceptions.ReplyingParseException; +import de.oliver.fancynpcs.commands.npc.*; +import de.oliver.fancynpcs.utils.GlowingColor; +import io.leangen.geantyref.TypeToken; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; +import org.incendo.cloud.annotations.AnnotationParser; +import org.incendo.cloud.bukkit.CloudBukkitCapabilities; +import org.incendo.cloud.bukkit.parser.WorldParser; +import org.incendo.cloud.bukkit.parser.location.LocationParser; +import org.incendo.cloud.component.CommandComponent; +import org.incendo.cloud.exception.ArgumentParseException; +import org.incendo.cloud.exception.InvalidCommandSenderException; +import org.incendo.cloud.exception.InvalidSyntaxException; +import org.incendo.cloud.exception.NoPermissionException; +import org.incendo.cloud.exception.handling.ExceptionHandlerRegistration; +import org.incendo.cloud.exception.parsing.NumberParseException; +import org.incendo.cloud.exception.parsing.ParserException; +import org.incendo.cloud.execution.ExecutionCoordinator; +import org.incendo.cloud.paper.LegacyPaperCommandManager; +import org.incendo.cloud.parser.standard.BooleanParser; +import org.incendo.cloud.parser.standard.EnumParser; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +import static org.incendo.cloud.exception.handling.ExceptionHandler.unwrappingHandler; + +// DEV NOTES: +// - For the time being, due to the reasons below, Brigadier integration should be OFF by default: +// a) Argument pop-ups don't work properly, they don't appear most of the time. +// b) Suggestions are supplied per-whitespace, and not per-argument. +public final class CloudCommandManager { + + private final @NotNull FancyNpcs plugin; + + private final @NotNull LegacyPaperCommandManager commandManager; + private final @NotNull AnnotationParser annotationParser; + + public CloudCommandManager(final @NotNull FancyNpcs plugin, final boolean isBrigadier) { + this.plugin = plugin; + // Creating instance of Cloud's LegacyPaperCommandManager, which is used for anything command-related. + this.commandManager = LegacyPaperCommandManager.createNative(plugin, ExecutionCoordinator.simpleCoordinator()); + // Registering Brigadier, if available. + if (isBrigadier && commandManager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) + commandManager.registerBrigadier(); + // Creating instance of AnnotationParser, which is used for parsing and registering commands. + this.annotationParser = new AnnotationParser<>(commandManager, CommandSender.class); + } + + /** + * Registers arguments (parsers and suggestion providers) to the {@link LegacyPaperCommandManager}. + */ + public @NotNull CloudCommandManager registerArguments() { + annotationParser.parse(NpcArgument.INSTANCE); + annotationParser.parse(LocationArgument.INSTANCE); + annotationParser.parse(ActionTriggerArgument.INSTANCE); + annotationParser.parse(ActionTypeArgument.INSTANCE); + return this; + } + + /** + * Registers exception handlers to the {@link LegacyPaperCommandManager}. + */ + public @NotNull CloudCommandManager registerExceptionHandlers() { + final Translator translator = plugin.getTranslator(); + // Unwrapping some causes of ArgumentParseException to be handled in standalone exception handlers. + commandManager.exceptionController().registerHandler(ArgumentParseException.class, unwrappingHandler(NumberParseException.class)); + commandManager.exceptionController().registerHandler(ArgumentParseException.class, unwrappingHandler(BooleanParser.BooleanParseException.class)); + commandManager.exceptionController().registerHandler(ArgumentParseException.class, unwrappingHandler(EnumParser.EnumParseException.class)); + commandManager.exceptionController().registerHandler(ArgumentParseException.class, unwrappingHandler(WorldParser.WorldParseException.class)); + commandManager.exceptionController().registerHandler(ArgumentParseException.class, unwrappingHandler(ReplyingParseException.class)); + // Overriding some default handlers to send specialized messages. + commandManager.exceptionController().registerHandler(NoPermissionException.class, (exceptionContext) -> { + translator.translate("command_missing_permissions").send(exceptionContext.context().sender()); + }); + // DEV NOTE: No need to compare sender types until we decide to make a console-only command. Should get the job done for the time being. + commandManager.exceptionController().registerHandler(InvalidCommandSenderException.class, (exceptionContext) -> { + translator.translate("command_player_only").send(exceptionContext.context().sender()); + }); + commandManager.exceptionController().registerHandler(NumberParseException.class, (exceptionContext) -> { + translator.translate("command_invalid_number") + .replaceStripped("input", exceptionContext.exception().input()) + .replace("min", exceptionContext.exception().range().min().toString()) + .replace("max", exceptionContext.exception().range().max().toString()) + .send(exceptionContext.context().sender()); + }); + commandManager.exceptionController().registerHandler(BooleanParser.BooleanParseException.class, (exceptionContext) -> { + translator.translate("command_invalid_boolean") + .replaceStripped("input", exceptionContext.exception().input()) + .send(exceptionContext.context().sender()); + }); + commandManager.exceptionController().registerHandler(WorldParser.WorldParseException.class, (exceptionContext) -> { + translator.translate("command_invalid_world") + .replaceStripped("input", exceptionContext.exception().input()) + .send(exceptionContext.context().sender()); + }); + // DEV NOTE: Temporary solution until https://github.com/Incendo/cloud-minecraft/pull/70 is merged. + commandManager.exceptionController().register(ExceptionHandlerRegistration.builder(TypeToken.get(ArgumentParseException.class)) + .exceptionFilter(exception -> exception.getCause() instanceof ParserException parserException && parserException.argumentParserClass() == LocationParser.class) + .exceptionHandler(exceptionContext -> { + final ParserException exception = (ParserException) exceptionContext.exception().getCause(); + final String input = exception.captionVariables()[0].value(); // Should never throw. + translator.translate("command_invalid_location") + .replaceStripped("input", !input.isBlank() ? input : "N/A") // Under certain conditions, input is not passed to the exception. + .send(exceptionContext.context().sender()); + }).build() + ); + commandManager.exceptionController().registerHandler(EnumParser.EnumParseException.class, (exceptionContext) -> { + String translationKey = "command_invalid_enum_generic"; + // Comparing exception enum class and choosing specialized messages. + if (exceptionContext.exception().enumClass() == ListCMD.SortType.class) + translationKey = "command_invalid_list_sort_type"; + else if (exceptionContext.exception().enumClass() == NearbyCMD.SortType.class) + translationKey = "command_invalid_nearby_sort_type"; + else if (exceptionContext.exception().enumClass() == EntityType.class) + translationKey = "command_invalid_entity_type"; + else if (exceptionContext.exception().enumClass() == GlowingColor.class) + translationKey = "command_invalid_glowing_color"; + // Sending error message to the sender. In case no specialized message has been found, a generic one is used instead. + translator.translate(translationKey) + .replaceStripped("input", exceptionContext.exception().input()) + .replace("enum", exceptionContext.exception().enumClass().getSimpleName().toLowerCase()) + .send(exceptionContext.context().sender()); + }); + // ReplyingParseException is thrown from custom argument types and is handled there. + commandManager.exceptionController().registerHandler(ReplyingParseException.class, context -> context.exception().runnable().run()); + // InvalidSyntaxException is thrown when user specified syntax don't match any command. + commandManager.exceptionController().registerHandler(InvalidSyntaxException.class, (exceptionContext) -> { + // Creating a StringBuilder which is then appended with (known/existing) command literals. + final StringBuilder translationKeyBuilder = new StringBuilder("command_syntax."); + // Iterating over current command chain and appending literals, as described above. + exceptionContext.exception().currentChain().stream() + .filter(c -> c.type() == CommandComponent.ComponentType.LITERAL) + .forEach(literal -> translationKeyBuilder.append(literal.name()).append(' ')); + // Trimming input (last character ends up being blank) and replacing whitespaces with underscores, as that's how translations are defined inside the language file. + final String translationKey = translationKeyBuilder.toString().trim().replace(' ', '_'); + // Getting the message, it's not finished as there we need to handle fallback language etc. + final @Nullable Message message = Optional.ofNullable(plugin.getTranslator().getSelectedLanguage().getMessage(translationKey)) + .orElse(plugin.getTranslator().getFallbackLanguage().getMessage(translationKey)); + // "Fall-backing" to generic syntax error, if no specialized syntax message has been defined in the language file. + if (message == null) { + plugin.getTranslator().translate("command_invalid_syntax_generic") + .replace("syntax", exceptionContext.exception().correctSyntax()) + .send(exceptionContext.context().sender()); + return; + } + message.send(exceptionContext.context().sender()); + }); + // Returning this instance of CloudCommandManager to keep "builder-like" flow. + return this; + } + + /** + * Registers plugin commands to the {@link LegacyPaperCommandManager}. + */ + public @NotNull CloudCommandManager registerCommands() { + annotationParser.parse(AttributeCMD.INSTANCE); + annotationParser.parse(CollidableCMD.INSTANCE); + annotationParser.parse(CopyCMD.INSTANCE); + annotationParser.parse(CreateCMD.INSTANCE); + annotationParser.parse(DisplayNameCMD.INSTANCE); + annotationParser.parse(EquipmentCMD.INSTANCE); + annotationParser.parse(FancyNpcsCMD.INSTANCE); + annotationParser.parse(FixCMD.INSTANCE); + annotationParser.parse(GlowingCMD.INSTANCE); + annotationParser.parse(InfoCMD.INSTANCE); + annotationParser.parse(InteractionCooldownCMD.INSTANCE); + annotationParser.parse(ListCMD.INSTANCE); + annotationParser.parse(MoveHereCMD.INSTANCE); + annotationParser.parse(MoveToCMD.INSTANCE); + annotationParser.parse(NearbyCMD.INSTANCE); + annotationParser.parse(HelpCMD.INSTANCE); + annotationParser.parse(RemoveCMD.INSTANCE); + annotationParser.parse(ShowInTabCMD.INSTANCE); + annotationParser.parse(SkinCMD.INSTANCE); + annotationParser.parse(TeleportCMD.INSTANCE); + annotationParser.parse(TurnToPlayerCMD.INSTANCE); + annotationParser.parse(TypeCMD.INSTANCE); + annotationParser.parse(ActionCMD.INSTANCE); + annotationParser.parse(VisibilityDistanceCMD.INSTANCE); + + if (FancyNpcs.ENABLE_DEBUG_MODE_FEATURE_FLAG.isEnabled()) { + annotationParser.parse(FancyNpcsDebugCMD.INSTANCE); + } + + String mcVersion = Bukkit.getMinecraftVersion(); + if (mcVersion.equals("1.20.5") || mcVersion.equals("1.20.6") || mcVersion.equals("1.21") || mcVersion.equals("1.21.1") || mcVersion.equals("1.21.2") || mcVersion.equals("1.21.3") || mcVersion.equals("1.21.4") || mcVersion.equals("1.21.5")) { + annotationParser.parse(ScaleCMD.INSTANCE); + } + + return this; + } + + /** + * Returns the internal {@link LegacyPaperCommandManager} associated with this instance of {@link CloudCommandManager}. + */ + public @NotNull LegacyPaperCommandManager getCommandManager() { + return commandManager; + } + + /** + * Returns the internal {@link AnnotationParser} associated with this instance of {@link CloudCommandManager}. + */ + public @NotNull AnnotationParser getAnnotationParser() { + return annotationParser; + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/FancyNpcsCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/FancyNpcsCMD.java new file mode 100644 index 00000000..6c150d80 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/FancyNpcsCMD.java @@ -0,0 +1,89 @@ +package de.oliver.fancynpcs.commands; + +import de.oliver.fancylib.translations.Language; +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancylib.translations.message.SimpleMessage; +import de.oliver.fancynpcs.FancyNpcs; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; +import org.jetbrains.annotations.NotNull; + +public final class FancyNpcsCMD { + + public static final FancyNpcsCMD INSTANCE = new FancyNpcsCMD(); + + private final FancyNpcs plugin = FancyNpcs.getInstance(); + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + private FancyNpcsCMD() { + } + + @Command("fancynpcs version") + @Permission("fancynpcs.command.fancynpcs.version") + public void onVersion(final CommandSender sender) { + plugin.getVersionConfig().checkVersionAndDisplay(sender, false); + } + + @Command("fancynpcs reload") + @Permission("fancynpcs.command.fancynpcs.reload") + public void onReload(final CommandSender sender) { + // Reloading all defined languages. + translator.loadLanguages(plugin.getDataFolder().getAbsolutePath()); + // Reloading plugin configuration. + plugin.getFancyNpcConfig().reload(); + // Getting the selected language from configuration. Defaults to fallback language. + final Language selectedLanguage = translator.getLanguages().stream() + .filter(language -> language.getLanguageName().equals(plugin.getFancyNpcConfig().getLanguage())) + .findFirst().orElse(translator.getFallbackLanguage()); + translator.setSelectedLanguage(selectedLanguage); + // Reloading all NPCs. + // NOTE: This sometimes creates duplicated NPCs on the client-side. + plugin.getNpcManagerImpl().reloadNpcs(); + // Sending success message to the sender. + translator.translate("fancynpcs_reload_success").send(sender); + } + + @Command("fancynpcs save") + @Permission("fancynpcs.command.fancynpcs.save") + public void onSave(final CommandSender sender) { + plugin.getNpcManagerImpl().saveNpcs(true); + translator.translate("fancynpcs_save_success").send(sender); + } + + // NOTE: In the future, if there is more than a few feature flags, we might consider listing entries automatically by iterating, just like in 'list' sub-command. + @Command("fancynpcs feature_flags") + @Permission("fancynpcs.command.fancynpcs.feature_flags") + public void onFeatureFlags(final CommandSender sender) { + translator.translate("fancynpcs_feature_flags_header").send(sender); + translator.translate("fancynpcs_feature_flags_entry") + .replace("number", "1") + .replace("name", "Player NPCs") + .replace("id", FancyNpcs.PLAYER_NPCS_FEATURE_FLAG.getName()) + .replace("state", getTranslatedState(FancyNpcs.PLAYER_NPCS_FEATURE_FLAG.isEnabled())) + .send(sender); + translator.translate("fancynpcs_feature_flags_entry") + .replace("number", "2") + .replace("name", "Native threads") + .replace("id", FancyNpcs.USE_NATIVE_THREADS_FEATURE_FLAG.getName()) + .replace("state", getTranslatedState(FancyNpcs.USE_NATIVE_THREADS_FEATURE_FLAG.isEnabled())) + .send(sender); + translator.translate("fancynpcs_feature_flags_entry") + .replace("number", "3") + .replace("name", "Debug mode") + .replace("id", FancyNpcs.ENABLE_DEBUG_MODE_FEATURE_FLAG.getName()) + .replace("state", getTranslatedState(FancyNpcs.ENABLE_DEBUG_MODE_FEATURE_FLAG.isEnabled())) + .send(sender); + translator.translate("fancynpcs_feature_flags_footer") + .replace("count", "2") + .replace("count_formatted", "· · 3") + .replace("total", String.valueOf(FancyNpcs.getInstance().getNpcManager().getAllNpcs().size())) + .replace("total_formatted", "· · 3") + .send(sender); + } + + // NOTE: Might need to be improved later down the line, should get work done for now. + private @NotNull String getTranslatedState(final boolean bool) { + return (bool) ? ((SimpleMessage) translator.translate("enabled")).getMessage() : ((SimpleMessage) translator.translate("disabled")).getMessage(); + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/FancyNpcsDebugCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/FancyNpcsDebugCMD.java new file mode 100644 index 00000000..5b4be8cb --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/FancyNpcsDebugCMD.java @@ -0,0 +1,75 @@ +package de.oliver.fancynpcs.commands; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.skins.mineskin.MineSkinQueue; +import de.oliver.fancynpcs.skins.mojang.MojangQueue; +import de.oliver.fancynpcs.tests.FancyNpcsTests; +import org.bukkit.entity.Player; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; + +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Date; + +public final class FancyNpcsDebugCMD { + + public static final FancyNpcsDebugCMD INSTANCE = new FancyNpcsDebugCMD(); + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + private FancyNpcsDebugCMD() { + } + + @Command("fancynpcs run_tests") + @Permission("fancynpcs.command.fancynpcs.run_tests") + public void onTest(final Player player) { + FancyNpcsTests tests = new FancyNpcsTests(); + boolean tested = tests.runAllTests(player); + + if (tested) { + translator.translate("fancynpcs_test_success") + .replace("player", player.getName()) + .replace("time", dateTimeFormatter.format(new Date().toInstant().atZone(ZoneId.of("Europe/Berlin")))) + .replace("count", String.valueOf(tests.getTestCount())) + .send(player); + } else { + translator.translate("fancynpcs_test_failure") + .replace("player", player.getName()) + .replace("time", dateTimeFormatter.format(new Date().toInstant().atZone(ZoneId.of("Europe/Berlin")))) + .send(player); + } + } + + @Command("fancynpcs skin_system restart_schedulers") + @Permission("fancynpcs.command.fancynpcs.skin_system.restart_schedulers") + public void onSkinSchedulerRestart(final Player player) { + MineSkinQueue.get().getScheduler().cancel(true); + MojangQueue.get().getScheduler().cancel(true); + + MineSkinQueue.get().run(); + MojangQueue.get().run(); + + translator.translate("fancynpcs_skin_system_restart_schedulers_success").send(player); + } + + @Command("fancynpcs skin_system clear_queues") + @Permission("fancynpcs.command.fancynpcs.skin_system.clear_queues") + public void onClearSkinQueues(final Player player) { + MineSkinQueue.get().clear(); + MojangQueue.get().clear(); + + translator.translate("fancynpcs_skin_system_clear_queues_success").send(player); + } + + @Command("fancynpcs skin_system clear_cache") + @Permission("fancynpcs.command.fancynpcs.skin_system.clear_cache") + public void onInvalidateCache(final Player player) { + FancyNpcs.getInstance().getSkinManagerImpl().getMemCache().clear(); + FancyNpcs.getInstance().getSkinManagerImpl().getFileCache().clear(); + + translator.translate("fancynpcs_skin_system_clear_cache_success").send(player); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/arguments/ActionTriggerArgument.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/arguments/ActionTriggerArgument.java new file mode 100644 index 00000000..5b1dd93f --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/arguments/ActionTriggerArgument.java @@ -0,0 +1,41 @@ +package de.oliver.fancynpcs.commands.arguments; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.actions.ActionTrigger; +import de.oliver.fancynpcs.commands.exceptions.ReplyingParseException; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotations.parser.Parser; +import org.incendo.cloud.annotations.suggestion.Suggestions; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class ActionTriggerArgument { + + public static final ActionTriggerArgument INSTANCE = new ActionTriggerArgument(); + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + private ActionTriggerArgument() { + } + + @Parser(name = "", suggestions = "action_trigger") + public ActionTrigger parse(final CommandContext context, final CommandInput input) { + final String value = input.readString(); + final @Nullable ActionTrigger trigger = ActionTrigger.getByName(value); + if (trigger == null) + throw ReplyingParseException.replying(() -> translator.translate("command_invalid_action_trigger").replaceStripped("input", value).send(context.sender())); + return trigger; + } + + @Suggestions("action_trigger") + public List suggestions(final CommandContext context, final CommandInput input) { + return List.of( + ActionTrigger.ANY_CLICK.name().toLowerCase(), + ActionTrigger.LEFT_CLICK.name().toLowerCase(), + ActionTrigger.RIGHT_CLICK.name().toLowerCase() + ); + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/arguments/ActionTypeArgument.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/arguments/ActionTypeArgument.java new file mode 100644 index 00000000..a330b255 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/arguments/ActionTypeArgument.java @@ -0,0 +1,41 @@ +package de.oliver.fancynpcs.commands.arguments; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.commands.exceptions.ReplyingParseException; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotations.parser.Parser; +import org.incendo.cloud.annotations.suggestion.Suggestions; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.jetbrains.annotations.Nullable; + +public class ActionTypeArgument { + + public static final ActionTypeArgument INSTANCE = new ActionTypeArgument(); + private final static FancyNpcs PLUGIN = FancyNpcs.getInstance(); + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + private ActionTypeArgument() { + } + + @Parser(name = "", suggestions = "action_type") + public NpcAction parse(final CommandContext context, final CommandInput input) { + final String value = input.readString(); + final @Nullable NpcAction action = PLUGIN.getActionManager().getActionByName(value); + if (action == null) + throw ReplyingParseException.replying(() -> translator.translate("command_invalid_action_type").replaceStripped("input", value).send(context.sender())); + return action; + } + + @Suggestions("action_type") + public Iterable suggestions(final CommandContext context, final CommandInput input) { + return PLUGIN.getActionManager() + .getAllActions() + .stream() + .map(NpcAction::getName) + .toList(); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/arguments/LocationArgument.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/arguments/LocationArgument.java new file mode 100644 index 00000000..01dec3fd --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/arguments/LocationArgument.java @@ -0,0 +1,37 @@ +package de.oliver.fancynpcs.commands.arguments; + +import org.bukkit.FluidCollisionMode; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.util.RayTraceResult; +import org.incendo.cloud.annotations.suggestion.Suggestions; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; + +import java.text.DecimalFormat; +import java.util.Collections; +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +public enum LocationArgument { + INSTANCE; // SINGLETON + + private static final DecimalFormat COORDS_FORMAT = new DecimalFormat("#.##"); + + @Suggestions("relative_location") + public List suggestLocation(final CommandContext context, final CommandInput input) { + if (context.sender() instanceof Player player) { + final @Nullable RayTraceResult raytrace = player.rayTraceBlocks(32.0, FluidCollisionMode.ALWAYS); + if (raytrace != null) + return List.of( + COORDS_FORMAT.format(raytrace.getHitPosition().getX()) + " " + + COORDS_FORMAT.format(raytrace.getHitPosition().getY()) + " " + + COORDS_FORMAT.format(raytrace.getHitPosition().getZ()), + "~ ~ ~" + ); + return List.of("~ ~ ~"); + } + return Collections.emptyList(); + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/arguments/NpcArgument.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/arguments/NpcArgument.java new file mode 100644 index 00000000..1211e0b3 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/arguments/NpcArgument.java @@ -0,0 +1,86 @@ +package de.oliver.fancynpcs.commands.arguments; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcManager; +import de.oliver.fancynpcs.commands.exceptions.ReplyingParseException; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.checkerframework.checker.units.qual.C; +import org.incendo.cloud.annotations.parser.Parser; +import org.incendo.cloud.annotations.suggestion.Suggestions; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.UUID; + +public class NpcArgument { + + public static final NpcArgument INSTANCE = new NpcArgument(); + + private final Translator translator; + private final NpcManager npcManager; + + private NpcArgument() { + this.translator = FancyNpcs.getInstance().getTranslator(); + this.npcManager = FancyNpcs.getInstance().getNpcManager(); + } + + /** + * Returns {@code true} if provided {@link String} can be converted to a valid {@link UUID}. Otherwise {@code false} is returned. + */ + private static boolean isUUID(final @NotNull String string) { + try { + UUID.fromString(string); + return true; + } catch (final IllegalArgumentException e) { + return false; + } + } + + /** + * Default parser for {@link Npc} argument. + */ + @Parser(name = "", suggestions = "npc") + public @NotNull Npc parse(final CommandContext context, final CommandInput input) { + final String value = input.readString(); + + Npc npc; + if (FancyNpcs.PLAYER_NPCS_FEATURE_FLAG.isEnabled() && context.sender() instanceof Player playerSender) { + npc = npcManager.getNpc(value, playerSender.getUniqueId()); + } else { + + if (isUUID(value)) { + npc = npcManager.getNpcById(value); + } else { + npc = npcManager.getNpc(value); + } + } + + if (npc == null) { + throw ReplyingParseException.replying(() -> translator.translate("command_invalid_npc").replaceStripped("input", value).send(context.sender())); + } + + return npc; + } + + /** + * Suggestions provider for {@link Npc} argument. + */ + @Suggestions("npc") // NOTE: Consider caching, might not be necessary but should be kept in mind. + public List suggestions(final CommandContext context, final CommandInput input) { + if (FancyNpcs.PLAYER_NPCS_FEATURE_FLAG.isEnabled() && context.sender() instanceof Player playerSender) { + return npcManager.getAllNpcs().stream() + .filter(npc -> npc.getData().getCreator().equals(playerSender.getUniqueId())) + .map(npc -> npc.getData().getName()) + .toList(); + } else { + return npcManager.getAllNpcs().stream() + .map(npc -> npc.getData().getName()) + .toList(); + } + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/exceptions/ReplyingParseException.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/exceptions/ReplyingParseException.java new file mode 100644 index 00000000..79452da0 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/exceptions/ReplyingParseException.java @@ -0,0 +1,21 @@ +package de.oliver.fancynpcs.commands.exceptions; + +import org.jetbrains.annotations.NotNull; + +public final class ReplyingParseException extends RuntimeException { + + private final @NotNull Runnable runnable; + + private ReplyingParseException(final @NotNull Runnable runnable) { + this.runnable = runnable; + } + + public static ReplyingParseException replying(final Runnable runnable) { + return new ReplyingParseException(runnable); + } + + public @NotNull Runnable runnable() { + return runnable; + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/ActionCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/ActionCMD.java new file mode 100644 index 00000000..9a9dc5d6 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/ActionCMD.java @@ -0,0 +1,304 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.actions.ActionTrigger; +import de.oliver.fancynpcs.api.actions.NpcAction; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotation.specifier.Greedy; +import org.incendo.cloud.annotations.Argument; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; +import org.incendo.cloud.annotations.suggestion.Suggestions; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public enum ActionCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command("npc action add [value]") + @Permission("fancynpcs.command.npc.action.add") + public void onActionAdd( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull ActionTrigger trigger, + final @NotNull NpcAction actionType, + final @Nullable @Greedy String value + ) { + if (actionType.requiresValue() && (value == null || value.isEmpty())) { + translator + .translate("npc_action_requires_value") + .send(sender); + return; + } + + List currentActions = npc.getData().getActions().getOrDefault(trigger, new ArrayList<>()); + + npc.getData().addAction(trigger, currentActions.size() + 1, actionType, value); + translator + .translate("npc_action_add_success") + .replaceStripped("total", String.valueOf(npc.getData().getActions(trigger).size())) + .send(sender); + } + + @Command("npc action add_before [value]") + @Permission("fancynpcs.command.npc.action.addBefore") + public void onActionAddBefore( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull ActionTrigger trigger, + final @Argument(suggestions = "ActionCMD/number_range") int index, + final @NotNull NpcAction actionType, + final @Nullable @Greedy String value + ) { + if (actionType.requiresValue() && (value == null || value.isEmpty())) { + translator + .translate("npc_action_requires_value") + .send(sender); + return; + } + + List currentActions = npc.getData().getActions(trigger); + currentActions.add(Math.clamp(index - 1, 0, currentActions.size()), new NpcAction.NpcActionData(index, actionType, value)); + + npc.getData().setActions(trigger, reorderActions(currentActions)); + translator + .translate("npc_action_add_before_success") + .replaceStripped("number", String.valueOf(index)) + .replaceStripped("total", String.valueOf(npc.getData().getActions(trigger).size())) + .send(sender); + } + + @Command("npc action add_after [value]") + @Permission("fancynpcs.command.npc.action.addAfter") + public void onActionAddAfter( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull ActionTrigger trigger, + final @Argument(suggestions = "ActionCMD/number_range") int index, + final @NotNull NpcAction actionType, + final @Nullable @Greedy String value + ) { + if (actionType.requiresValue() && (value == null || value.isEmpty())) { + translator + .translate("npc_action_requires_value") + .send(sender); + return; + } + + List currentActions = npc.getData().getActions(trigger); + currentActions.add(Math.clamp(index, 0, currentActions.size() + 1), new NpcAction.NpcActionData(index, actionType, value)); + + npc.getData().setActions(trigger, reorderActions(currentActions)); + translator + .translate("npc_action_add_after_success") + .replaceStripped("number", String.valueOf(index)) + .replaceStripped("total", String.valueOf(npc.getData().getActions(trigger).size())) + .send(sender); + } + + @Command("npc action set [value]") + @Permission("fancynpcs.command.npc.action.set") + public void onActionSet( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull ActionTrigger trigger, + final @NotNull @Argument(suggestions = "ActionCMD/number_range") Integer number, + final @NotNull NpcAction actionType, + final @Nullable @Greedy String value + ) { + if (actionType.requiresValue() && (value == null || value.isEmpty())) { + translator + .translate("npc_action_requires_value") + .send(sender); + return; + } + + List currentActions = npc.getData().getActions(trigger); + if (number < 1 || number > currentActions.size()) { + translator + .translate("npc_action_set_failure") + .replaceStripped("number", String.valueOf(number)) + .send(sender); + return; + } + + currentActions.set(number - 1, new NpcAction.NpcActionData(number, actionType, value)); + npc.getData().setActions(trigger, currentActions); + translator + .translate("npc_action_set_success") + .replaceStripped("number", String.valueOf(number)) + .replaceStripped("total", String.valueOf(npc.getData().getActions(trigger).size())) + .send(sender); + } + + @Command("npc action remove ") + @Permission("fancynpcs.command.npc.action.remove") + public void onActionRemove( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull ActionTrigger trigger, + final @Argument(suggestions = "ActionCMD/number_range") int number + ) { + List currentActions = npc.getData().getActions(trigger); + if (number < 1 || number > currentActions.size()) { + translator + .translate("npc_action_remove_failure") + .replaceStripped("number", String.valueOf(number)) + .send(sender); + return; + } + + currentActions.remove(number - 1); + + npc.getData().setActions(trigger, reorderActions(currentActions)); + translator + .translate("npc_action_remove_success") + .replaceStripped("number", String.valueOf(number)) + .replaceStripped("total", String.valueOf(npc.getData().getActions(trigger).size())) + .send(sender); + } + + @Command("npc action move_up ") + @Permission("fancynpcs.command.npc.action.moveUp") + public void onActionMoveUp( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull ActionTrigger trigger, + final @Argument(suggestions = "ActionCMD/number_range") int number + ) { + List currentActions = npc.getData().getActions(trigger); + if (number <= 1) { + translator + .translate("npc_action_move_up_failure") + .replaceStripped("number", String.valueOf(number)) + .send(sender); + return; + } + + NpcAction.NpcActionData action = currentActions.get(number - 1); + currentActions.remove(number - 1); + currentActions.add(number - 2, action); + + npc.getData().setActions(trigger, reorderActions(currentActions)); + translator + .translate("npc_action_move_up_success") + .replaceStripped("number", String.valueOf(number)) + .send(sender); + } + + @Command("npc action move_down ") + @Permission("fancynpcs.command.npc.action.moveDown") + public void onActionMoveDown( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull ActionTrigger trigger, + final @Argument(suggestions = "ActionCMD/number_range") int number + ) { + List currentActions = npc.getData().getActions(trigger); + if (number >= currentActions.size()) { + translator + .translate("npc_action_move_down_failure") + .replaceStripped("number", String.valueOf(number)) + .send(sender); + return; + } + + NpcAction.NpcActionData action = currentActions.get(number - 1); + currentActions.remove(number - 1); + currentActions.add(number, action); + + npc.getData().setActions(trigger, reorderActions(currentActions)); + translator + .translate("npc_action_move_down_success") + .replaceStripped("number", String.valueOf(number)) + .send(sender); + } + + + @Command("npc action clear") + @Permission("fancynpcs.command.npc.action.clear") + public void onActionClear( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull ActionTrigger trigger + ) { + npc.getData().setActions(trigger, new ArrayList<>()); + translator + .translate("npc_action_clear_success") + .send(sender); + } + + @Command("npc action list") + @Permission("fancynpcs.command.npc.action.list") + public void onActionList( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull ActionTrigger trigger + ) { + List actions = npc.getData().getActions(trigger); + if (actions.isEmpty()) { + translator + .translate("npc_action_list_failure_empty") + .send(sender); + return; + } + + translator + .translate("npc_action_list_header") + .replaceStripped("trigger", trigger.name()) + .send(sender); + + for (int i = 0; i < actions.size(); i++) { + NpcAction.NpcActionData action = actions.get(i); + translator + .translate("npc_action_list_entry") + .replaceStripped("number", String.valueOf(action.order())) + .replaceStripped("action", action.action().getName()) + .replaceStripped("value", action.value() != null ? action.value() : "") + .send(sender); + } + + translator + .translate("npc_action_list_footer") + .replaceStripped("total", String.valueOf(actions.size())) + .send(sender); + } + + private List reorderActions(List actions) { + List newActions = new ArrayList<>(); + for (int i = 0; i < actions.size(); i++) { + NpcAction.NpcActionData a = actions.get(i); + newActions.add(new NpcAction.NpcActionData(i + 1, a.action(), a.value())); + } + return newActions; + } + + + /* PARSERS AND SUGGESTIONS */ + + @Suggestions("ActionCMD/number_range") + public List suggestNumber(final CommandContext context, final CommandInput input) { + final Npc npc = context.getOrDefault("npc", null); + final ActionTrigger trigger = context.getOrDefault("trigger", null); + + if (npc == null || trigger == null) return Collections.emptyList(); + + List suggestions = new ArrayList<>(); + for (int i = 0; i < npc.getData().getActions(trigger).size(); i++) { + suggestions.add(String.valueOf(i + 1)); + } + + return suggestions; + } +} \ No newline at end of file diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/AttributeCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/AttributeCMD.java new file mode 100644 index 00000000..2b2068e2 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/AttributeCMD.java @@ -0,0 +1,112 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.AttributeManager; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.api.events.NpcModifyEvent; +import de.oliver.fancynpcs.commands.exceptions.ReplyingParseException; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotations.Argument; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; +import org.incendo.cloud.annotations.parser.Parser; +import org.incendo.cloud.annotations.suggestion.Suggestions; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; + +import java.util.List; + +import org.jetbrains.annotations.NotNull; + +public enum AttributeCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + private final AttributeManager attributeManager = FancyNpcs.getInstance().getAttributeManager(); + + @Command("npc attribute set ") + @Permission("fancynpcs.command.npc.attribute.set") + public void onAttributeSet( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull NpcAttribute attribute, + final @NotNull @Argument(parserName = "AttributeCMD/attribute_value") String attributeValue + ) { + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.ATTRIBUTE, new Object[]{attribute, attributeValue}, sender).callEvent()) { + npc.getData().addAttribute(attribute, attributeValue); + npc.updateForAll(); + translator.translate("npc_attribute_set").replace("attribute", attribute.getName()).replaceStripped("value", attributeValue.toLowerCase()).send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } + + @Command("npc attribute list") + @Permission("fancynpcs.command.npc.attribute.list") + public void onAttributeList( + final @NotNull CommandSender sender, + final @NotNull Npc npc + ) { + // Sending error message if the list is empty. + if (npc.getData().getAttributes().isEmpty()) { + translator.translate("npc_attribute_list_failure_empty").send(sender); + return; + } + translator.translate("npc_attribute_list_header").send(sender); + // Iterating over all attributes set on this NPC and sending them to the sender. + npc.getData().getAttributes().forEach((attribute, value) -> { + translator.translate("npc_attribute_list_entry") + .replace("attribute", attribute.getName()) + .replace("value", value) + .send(sender); + }); + translator.translate("npc_attribute_list_footer").send(sender); + } + + /* PARSERS AND SUGGESTIONS */ + + // This parser does not specify a name, making it default parser for the returned type. + @Parser(name = "", suggestions = "AttributeCMD/attribute") + public NpcAttribute parseAttribute(final CommandContext context, final CommandInput input) { + // Getting the 'npc' argument that should already exist within the command context. + final Npc npc = context.get("npc"); + // Reading the string, which is supposed to be an attribute name. + final String value = input.readString(); + // Getting the NpcAttribute from the name and npc type. + final NpcAttribute attribute = attributeManager.getAttributeByName(npc.getData().getType(), value); + // Throwing exception when non-existent attribute has been provided. + if (attribute == null) + throw ReplyingParseException.replying(() -> translator.translate("command_invalid_attribute").replaceStripped("input", value).send(context.sender())); + // Otherwise, returning the attribute from the parser. + return attribute; + } + + @Parser(name = "AttributeCMD/attribute_value", suggestions = "AttributeCMD/attribute_value") + public String parseAttributeValue(final CommandContext context, final CommandInput input) { + // Getting the 'attribute' argument that should already exist within the command context. + final NpcAttribute attribute = context.get("attribute"); + // Reading the string, which is supposed to be an attribute name. + final String value = input.read(input.remainingLength()); + // Sending error message if attribute is null or cannot accept provided value. + if (!attribute.isValidValue(value)) + throw ReplyingParseException.replying(() -> translator.translate("command_invalid_attribute_value").replaceStripped("input", value).send(context.sender())); + // Otherwise, returning the attribute from the parser. + return value; + } + + @Suggestions("AttributeCMD/attribute") + public List suggestAttribute(final CommandContext context, final CommandInput input) { + final Npc npc = context.getOrDefault("npc", null); + return attributeManager.getAllAttributesForEntityType(npc.getData().getType()).stream().map(NpcAttribute::getName).toList(); + } + + @Suggestions("AttributeCMD/attribute_value") + public List suggestAttributeValue(final CommandContext context, final CommandInput input) { + final Npc npc = context.get("npc"); + final NpcAttribute attribute = context.get("attribute"); + return attributeManager.getAttributeByName(npc.getData().getType(), attribute.getName()).getPossibleValues(); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/CollidableCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/CollidableCMD.java new file mode 100644 index 00000000..14e755e0 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/CollidableCMD.java @@ -0,0 +1,38 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcModifyEvent; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public enum CollidableCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command("npc collidable [state]") + @Permission("fancynpcs.command.npc.collidable") + public void onCollidable( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @Nullable Boolean state + ) { + // Finalizing the state. If no state has been specified, the current one is inverted. + final boolean finalState = (state == null) ? !npc.getData().isCollidable() : state; + // Calling the event and updating the state if not cancelled. + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.COLLIDABLE, finalState, sender).callEvent()) { + npc.getData().setCollidable(finalState); + npc.removeForAll(); + npc.spawnForAll(); + translator.translate(finalState ? "npc_collidable_set_true" : "npc_collidable_set_false").replace("npc", npc.getData().getName()).send(sender); + return; + } + translator.translate("command_npc_modification_cancelled").send(sender); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/CopyCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/CopyCMD.java new file mode 100644 index 00000000..8d3aad9e --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/CopyCMD.java @@ -0,0 +1,71 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.events.NpcCreateEvent; +import org.bukkit.entity.Player; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +// TO-DO: Console support with --position and --world parameter flags. +public enum CopyCMD { + INSTANCE; // SINGLETON + + private static final Pattern NPC_NAME_PATTERN = Pattern.compile("^[A-Za-z0-9/_-]*$"); + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command(value = "npc copy ", requiredSender = Player.class) + @Permission("fancynpcs.command.npc.copy") + public void onCopy( + final @NotNull Player sender, + final @NotNull Npc npc, + final @NotNull String name + ) { + // Sending error message if name does not match configured pattern. + if (!NPC_NAME_PATTERN.matcher(name).find()) { + translator.translate("npc_create_failure_invalid_name").replaceStripped("name", name).send(sender); + return; + } + // Creating a copy of an NPC and all it's data. The only different thing is it's UUID. + final Npc copied = FancyNpcs.getInstance().getNpcAdapter().apply( + new NpcData( + UUID.randomUUID().toString(), + name, + sender.getUniqueId(), + npc.getData().getDisplayName(), + npc.getData().getSkinData(), + sender.getLocation().clone(), + npc.getData().isShowInTab(), + npc.getData().isSpawnEntity(), + npc.getData().isCollidable(), + npc.getData().isGlowing(), + npc.getData().getGlowingColor(), + npc.getData().getType(), + new ConcurrentHashMap<>(npc.getData().getEquipment()), + npc.getData().isTurnToPlayer(), + npc.getData().getOnClick(), + new ConcurrentHashMap<>(npc.getData().getActions()), + npc.getData().getInteractionCooldown(), + npc.getData().getScale(), + npc.getData().getVisibilityDistance(), + new ConcurrentHashMap<>(npc.getData().getAttributes()), + npc.getData().isMirrorSkin() + )); + // Calling the event and creating + registering copied NPC if not cancelled. + if (new NpcCreateEvent(copied, sender).callEvent()) { + copied.create(); + FancyNpcs.getInstance().getNpcManagerImpl().registerNpc(copied); + copied.spawnForAll(); + translator.translate("npc_copy_success").replace("npc", npc.getData().getName()).replace("new_npc", copied.getData().getName()).send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/CreateCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/CreateCMD.java new file mode 100644 index 00000000..462241cf --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/CreateCMD.java @@ -0,0 +1,81 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.events.NpcCreateEvent; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Flag; +import org.incendo.cloud.annotations.Permission; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; +import java.util.regex.Pattern; + +public enum CreateCMD { + INSTANCE; // SINGLETON + + private static final Pattern NPC_NAME_PATTERN = Pattern.compile("^[A-Za-z0-9/_-]*$"); + private static final UUID EMPTY_UUID = new UUID(0, 0); + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command("npc create ") + @Permission("fancynpcs.command.npc.create") + public void onCreate( + final @NotNull CommandSender sender, + final @NotNull String name, + final @Nullable @Flag("type") EntityType type, + final @Nullable @Flag(value = "location", suggestions = "relative_location") Location location, + final @Nullable @Flag("world") World world + ) { + // Sending error message if name does not match configured pattern. + if (!NPC_NAME_PATTERN.matcher(name).find()) { + translator.translate("npc_create_failure_invalid_name").replaceStripped("name", name).send(sender); + return; + } + // Getting the NPC creator unique identifier. The UUID is always empty (all zeroes) for non-player senders. + final UUID creator = (sender instanceof Player player) ? player.getUniqueId() : EMPTY_UUID; + // Sending error message if NPC with such name already exist. + if (FancyNpcs.PLAYER_NPCS_FEATURE_FLAG.isEnabled() && FancyNpcs.getInstance().getNpcManager().getNpc(name, creator) != null || !FancyNpcs.PLAYER_NPCS_FEATURE_FLAG.isEnabled() && FancyNpcs.getInstance().getNpcManager().getNpc(name) != null) { + translator.translate("npc_create_failure_already_exists").replace("npc", FancyNpcs.getInstance().getNpcManager().getNpc(name).getData().getName()).send(sender); + return; + } + // Sending error message if sender is console and location has not been specified. + if (sender instanceof ConsoleCommandSender && location == null) { + translator.translate("npc_create_failure_must_specify_location").send(sender); + return; + } + // Sending error message if sender is console and world has not been specified. + if (sender instanceof ConsoleCommandSender && world == null) { + translator.translate("npc_create_failure_must_specify_world").send(sender); + return; + } + // Finalizing Location argument. This argument is optional and defaults to player's current location. + final Location finalLocation = (location == null && sender instanceof Player player) ? player.getLocation() : location; + // Updating World of the Location argument if '--world' flag has been specified. + if (world != null) + finalLocation.setWorld(world); + // Creating new NPC and applying data. + final Npc npc = FancyNpcs.getInstance().getNpcAdapter().apply(new NpcData(name, creator, finalLocation)); + // Setting the type of NPC. Flag '--type' is optional and defaults to EntityType.PLAYER. + npc.getData().setType(type != null ? type : EntityType.PLAYER); + // Calling the event and creating NPC if not cancelled. + if (new NpcCreateEvent(npc, sender).callEvent()) { + npc.create(); + FancyNpcs.getInstance().getNpcManagerImpl().registerNpc(npc); + npc.spawnForAll(); + translator.translate("npc_create_success").replace("npc", name).send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/DisplayNameCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/DisplayNameCMD.java new file mode 100644 index 00000000..20e896ae --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/DisplayNameCMD.java @@ -0,0 +1,94 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcModifyEvent; +import org.lushplugins.chatcolorhandler.ModernChatColorHandler; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentIteratorType; +import net.kyori.adventure.text.event.ClickEvent; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotation.specifier.Greedy; +import org.incendo.cloud.annotations.Argument; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; +import org.incendo.cloud.annotations.suggestion.Suggestions; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.jetbrains.annotations.NotNull; + +public enum DisplayNameCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + // Storing in a static variable to avoid re-creating the array each time suggestion is requested. + private static final List NONE_SUGGESTIONS = List.of("@none"); + + @Command("npc displayname ") + @Permission("fancynpcs.command.npc.displayname") + public void onDisplayName( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull @Argument(suggestions = "DisplayNameCMD/none") @Greedy String name + ) { + // Finalizing the name. In case input is '@none', it gets replaced with '' for backwards compatibility. + final String finalName = name.equalsIgnoreCase("@none") ? "" : name; + // Sending error message in case banned command has been found in the input. + if (hasBlockedCommands(finalName)) { + translator.translate("command_input_contains_blocked_command").send(sender); + return; + } + // Calling the event and updating the state if not cancelled. + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.DISPLAY_NAME, finalName, sender).callEvent()) { + npc.getData().setDisplayName(finalName); + npc.updateForAll(); + translator.translate(finalName.equalsIgnoreCase("") ? "npc_displayname_set_empty" : "npc_displayname_set_name") + .replace("npc", npc.getData().getName()) + .replace("name", finalName) + .send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } + + /* PARSERS AND SUGGESTIONS */ + + @Suggestions("DisplayNameCMD/none") + public List suggestNone(final CommandContext sender, CommandInput input) { + return NONE_SUGGESTIONS; + } + + /* UTILITY METHODS */ + + /** Returns {@code true} if specified component contains blocked command, {@code false} otherwise. */ + private boolean hasBlockedCommands(final @NotNull String message) { + // Converting message to a Component. + final Component component = ModernChatColorHandler.translate(message); + // Getting the list of all blocked commands. + final List blockedCommands = FancyNpcs.getInstance().getFancyNpcConfig().getBlockedCommands(); + // Iterating over all elements of the component. + return StreamSupport.stream(component.iterable(ComponentIteratorType.DEPTH_FIRST).spliterator(), false).anyMatch(it -> { + final ClickEvent event = it.clickEvent(); + // We only care about click events with run_command as an action. Continuing if not found. + if (event == null || event.action() != ClickEvent.Action.RUN_COMMAND) + return false; + // Iterating over list of blocked commands... + for (final String blockedCommand : blockedCommands) { + // Transforming the command to a base command with trailed whitespaces and slashes. This also removes namespaced part from the beginning of the command. + final String transformedBaseCommand = blockedCommand.replace('/', ' ').strip().split(" ")[0].replaceAll(".*?:+", ""); + // Comparing click event value with the transformed base command. Returning the result. + if (event.value().replace('/', ' ').strip().split(" ")[0].replaceAll(".*?:+", "").equalsIgnoreCase(transformedBaseCommand)) + return true; + } + // Returning false as no blocked commands has been found. + return false; + }); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/EquipmentCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/EquipmentCMD.java new file mode 100644 index 00000000..2b1434a7 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/EquipmentCMD.java @@ -0,0 +1,178 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancylib.translations.message.SimpleMessage; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcModifyEvent; +import de.oliver.fancynpcs.api.utils.NpcEquipmentSlot; +import de.oliver.fancynpcs.commands.exceptions.ReplyingParseException; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.incendo.cloud.annotations.Argument; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; +import org.incendo.cloud.annotations.parser.Parser; +import org.incendo.cloud.annotations.suggestion.Suggestions; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.StreamSupport; + +public enum EquipmentCMD { + INSTANCE; // SINGLETON + + // Storing in a static variable to avoid re-creating the array each time suggestion is requested. + private static final List SLOT_SUGGESTIONS = Arrays.stream(NpcEquipmentSlot.values()).map(slot -> slot.name().toLowerCase()).toList(); + // Replace with Registry#stream after dropping 1.19.4 support. + private static final List MATERIAL_SUGGESTIONS = StreamSupport.stream(Registry.MATERIAL.spliterator(), false).filter(Material::isItem).map(material -> material.key().asString()).toList(); + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command("npc equipment set ") + @Permission("fancynpcs.command.npc.equipment.set") + public void onEquipmentSet( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull NpcEquipmentSlot slot, + final @NotNull @Argument(parserName = "EquipmentCMD/item") ItemStack item + ) { + // Calling the event and updating equipment if not cancelled. + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.EQUIPMENT, new Object[]{slot, item}, sender).callEvent()) { + npc.getData().addEquipment(slot, item); + npc.updateForAll(); + translator.translate(item.getType() != Material.AIR ? "npc_equipment_set_item" : "npc_equipment_set_empty") + .replace("npc", npc.getData().getName()) + .replace("slot", getTranslatedSlot(slot)) + .addTagResolver(Placeholder.component("item", (item.getType() != Material.AIR) ? item.displayName().hoverEvent(item.asHoverEvent()) : Component.empty())) + .send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } + + @Command("npc equipment clear") + @Permission("fancynpcs.command.npc.equipment.clear") + public void onEquipmentClear( + final @NotNull CommandSender sender, + final @NotNull Npc npc + ) { + // Calling the event and clearing equipment if not cancelled. + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.EQUIPMENT, null, sender).callEvent()) { + // Entries must be set to null manually because clearing the map would prevent equipment from being updated. (Npc#update checks if map is empty) + for (final NpcEquipmentSlot slot : NpcEquipmentSlot.values()) + npc.getData().getEquipment().put(slot, new ItemStack(Material.AIR)); + npc.updateForAll(); + translator.translate("npc_equipment_clear_success").replace("npc", npc.getData().getName()).send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } + + @Command("npc equipment list") + @Permission("fancynpcs.command.npc.equipment.list") + public void onEquipmentList( + final @NotNull CommandSender sender, + final @NotNull Npc npc + ) { + // Sending error message if the list is empty or all items are Material.AIR. + if (npc.getData().getEquipment().isEmpty() || npc.getData().getEquipment().values().stream().allMatch(item -> item == null || item.getType() == Material.AIR)) { + translator.translate("npc_equipment_list_failure_empty").send(sender); + return; + } + translator.translate("npc_equipment_list_header").send(sender); + // Iterating over all equipment slots of this NPC and sending them to the sender. + npc.getData().getEquipment().forEach((slot, item) -> { + // Skipping null entries and Material.AIR, no need to display that. + if (item == null || item.getType() == Material.AIR) + return; + translator.translate("npc_equipment_list_entry") + .replace("slot", getTranslatedSlot(slot)) + .addTagResolver(Placeholder.component("item", item.displayName().hoverEvent(item.asHoverEvent()))) + .send(sender); + }); + translator.translate("npc_equipment_list_footer").send(sender); + } + + /* PARSERS AND SUGGESTIONS */ + + // This parser does not specify a name, making it default parser for the returned type. + @Parser(name = "", suggestions = "EquipmentCMD/slot") + public NpcEquipmentSlot parseSlot(final CommandContext context, final CommandInput input) { + final String value = input.readString().toLowerCase(); + final @Nullable NpcEquipmentSlot slot = NpcEquipmentSlot.parse(value); + // Sending error message if input is not a valid NpcEquipmentSlot. + if (slot == null) + throw ReplyingParseException.replying(() -> translator.translate("command_invalid_equipment_slot").replaceStripped("input", value).send(context.sender())); + return slot; + } + + @Parser(name = "EquipmentCMD/item", suggestions = "EquipmentCMD/item") + public ItemStack parseItem(final CommandContext context, final CommandInput input) { + final String value = input.readString().toLowerCase(); + // Handling '@none', which returns air (and effectively disables) + if (value.equals("@none")) + return new ItemStack(Material.AIR); + // Handling '@hand', which returns item player currently have in their hand. + else if (value.equals("@hand") && context.sender() instanceof Player player) + return player.getInventory().getItemInMainHand().clone(); + // Otherwise, trying to parse input as an material. + else { + // Converting input to NamespacedKey. Defaults to 'minecraft:' namespace if missing from input. + final @Nullable NamespacedKey key = NamespacedKey.fromString(value); + // Sending error message if input is not a valid NamespacedKey. + if (key == null) + throw ReplyingParseException.replying(() -> translator.translate("command_invalid_material").replaceStripped("input", value).send(context.sender())); + // Getting material from the registry. + final @Nullable Material material = Registry.MATERIAL.get(key); + // Sending error message if no material was found. + if (material == null || !material.isItem()) + throw ReplyingParseException.replying(() -> translator.translate("command_invalid_material").replaceStripped("input", value).send(context.sender())); + // Returning new ItemStack object from the specified Material. + return new ItemStack(material); + } + } + + @Suggestions("EquipmentCMD/item") + public List suggestItem(final CommandContext context, final CommandInput input) { + return new ArrayList<>(MATERIAL_SUGGESTIONS) {{ + // Adding '@none' placeholder which is replaced with 'minecraft:air'. + add("@none"); + // If applicable, adding '@hand' placeholder which is replaced with item player currently have in their hand. + if (context.sender() instanceof Player) + add("@hand"); + }}; + } + + @Suggestions("EquipmentCMD/slot") + public List suggestSlot(final CommandContext context, final CommandInput input) { + return SLOT_SUGGESTIONS; + } + + /* UTILITY METHODS */ + + // NOTE: Might need to be improved later down the line, should get work done for now. + private @NotNull String getTranslatedSlot(final @NotNull NpcEquipmentSlot slot) { + return ((SimpleMessage) translator.translate( + switch (slot) { + case MAINHAND -> "main_hand"; + case OFFHAND -> "off_hand"; + case HEAD -> "head"; + case CHEST -> "chest"; + case LEGS -> "legs"; + case FEET -> "feet"; + } + )).getMessage(); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/FixCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/FixCMD.java new file mode 100644 index 00000000..940b98f5 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/FixCMD.java @@ -0,0 +1,30 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; + +import org.jetbrains.annotations.NotNull; + +public enum FixCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command("npc fix ") + @Permission("fancynpcs.command.npc.fix") + public void onFix( + final @NotNull CommandSender sender, + final @NotNull Npc npc + ) { + npc.removeForAll(); + npc.create(); + Bukkit.getOnlinePlayers().forEach(npc::checkAndUpdateVisibility); + translator.translate("npc_fix_success").replace("npc", npc.getData().getName()).send(sender); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/GlowingCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/GlowingCMD.java new file mode 100644 index 00000000..900040ce --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/GlowingCMD.java @@ -0,0 +1,72 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancylib.translations.message.SimpleMessage; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcModifyEvent; +import de.oliver.fancynpcs.utils.GlowingColor; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public enum GlowingCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command("npc glowing [color]") + @Permission("fancynpcs.command.npc.glowing") + public void onGlowing( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @Nullable GlowingColor color + ) { + // Handling 'toggle' state, which means inverting the current state. + if (color == null) { + // Inverting the current glowing state, so the command works like a toggle. + final boolean isGlowingToggled = !npc.getData().isGlowing(); + // Calling the event and updating the state if not cancelled. + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.GLOWING, isGlowingToggled, sender).callEvent()) { + npc.getData().setGlowing(isGlowingToggled); + npc.updateForAll(); + translator.translate(isGlowingToggled ? "npc_glowing_set_true" : "npc_glowing_set_false").replace("npc", npc.getData().getName()).send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } + // Handling 'disabled' state, which means disabling glowing state. + else if (color == GlowingColor.DISABLED) { + // Calling the event and updating the state if not cancelled. + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.GLOWING, false, sender).callEvent()) { + npc.getData().setGlowing(false); + npc.updateForAll(); + translator.translate("npc_glowing_set_false").replace("npc", npc.getData().getName()).send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + // Handling 'color' state, which means enabling glowing and changing the color to desired one. + } else if (npc.getData().isGlowing() || new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.GLOWING, true, sender).callEvent()) { + // Calling the event and updating the glowing color if not cancelled. + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.GLOWING_COLOR, color.getColor(), sender).callEvent()) { + npc.getData().setGlowingColor(color.getColor()); + // Updating the glowing state, if previously disabled. + if (!npc.getData().isGlowing()) + npc.getData().setGlowing(true); + npc.updateForAll(); + translator.translate("npc_glowing_set_color_success") + .replace("npc", npc.getData().getName()) + .replace("color", ((SimpleMessage) translator.translate(color.getTranslationKey())).getMessage()) + .send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/HelpCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/HelpCMD.java new file mode 100644 index 00000000..42d321ce --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/HelpCMD.java @@ -0,0 +1,58 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancylib.translations.message.MultiMessage; +import de.oliver.fancynpcs.FancyNpcs; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotations.Argument; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Default; +import org.incendo.cloud.annotations.Permission; +import org.incendo.cloud.annotations.suggestion.Suggestions; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.annotations.NotNull; + +public enum HelpCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command("npc help [page]") + @Permission("fancynpcs.command.npc") + public void onHelp( + final @NotNull CommandSender sender, + final @Argument(suggestions = "HelpCMD/page") @Default("1") int page + ) { + // Getting the (full) help contents. + final MultiMessage contents = (MultiMessage) translator.translate("npc_help_contents"); + // Calculating max page number. + final int maxPage = (int) Math.ceil(contents.getRawMessages().size() / 6F); + // Getting the requested page. Defaults to 1 for invalid input and is capped by number of the last page. + final int finalPage = Math.clamp(page, 1, maxPage); + // Sending help contents to the sender. + translator.translate("npc_help_page_header").replace("page", String.valueOf(finalPage)).replace("max_page", String.valueOf(maxPage)).send(sender); + contents.page(finalPage, 6).send(sender); + translator.translate("npc_help_page_footer").replace("page", String.valueOf(finalPage)).replace("max_page", String.valueOf(maxPage)).send(sender); + } + + /* PARSERS AND SUGGESTIONS */ + + @Suggestions("HelpCMD/page") + public List suggestPage(final CommandContext context, final CommandInput input) { + // Getting the (full) help contents. + final MultiMessage contents = (MultiMessage) translator.translate("npc_help_contents"); + // Calculating max page number. + final int maxPage = contents.getRawMessages().size() / 6 + 1; + // Returning suggestions... + return new ArrayList<>() {{ + for (int i = 1; i <= maxPage; i++) + add(String.valueOf(i)); + }}; + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/InfoCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/InfoCMD.java new file mode 100644 index 00000000..3fb7d3ef --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/InfoCMD.java @@ -0,0 +1,85 @@ +package de.oliver.fancynpcs.commands.npc; + +import com.destroystokyo.paper.profile.PlayerProfile; +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancylib.translations.message.SimpleMessage; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.utils.Interval; +import de.oliver.fancynpcs.api.utils.Interval.Unit; +import de.oliver.fancynpcs.utils.GlowingColor; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; + +import java.text.DecimalFormat; +import java.util.Collection; + +import org.jetbrains.annotations.NotNull; + +public enum InfoCMD { + INSTANCE; // SINGLETON + + private static final DecimalFormat COORDS_FORMAT = new DecimalFormat("#.##"); + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command("npc info ") + @Permission("fancynpcs.command.npc.info") + public void onInfo( + final @NotNull CommandSender sender, + final @NotNull Npc npc + ) { + final Location loc = npc.getData().getLocation(); + final Interval interactionCooldown = Interval.of(npc.getData().getInteractionCooldown(), Unit.SECONDS); + final int actionsTotal = npc.getData().getActions().values().stream().mapToInt(Collection::size).sum(); + // Getting the translated glowing state. This should never throw because all supported NamedTextColor objects has their mapping in GlowingColor enum. + final String glowingStateTranslated = (npc.getData().isGlowing() && npc.getData().getGlowingColor() != null) + ? ((SimpleMessage) translator.translate(GlowingColor.fromAdventure(npc.getData().getGlowingColor()).getTranslationKey())).getMessage() + : ((SimpleMessage) translator.translate("disabled")).getMessage(); + final String visibilityDistanceTranslated = (npc.getData().getVisibilityDistance() == -1) + ? ((SimpleMessage) translator.translate("default").replace("value", String.valueOf(FancyNpcs.getInstance().getFancyNpcConfig().getVisibilityDistance()))).getMessage() + : (npc.getData().getVisibilityDistance() == 0) + ? ((SimpleMessage) translator.translate("not_visible")).getMessage() + : (npc.getData().getVisibilityDistance() == Integer.MAX_VALUE) + ? ((SimpleMessage) translator.translate("always_visible")).getMessage() + : String.valueOf(npc.getData().getVisibilityDistance()); + // Getting the creator player profile, this will be completed from cache in order to get name of the player. + final PlayerProfile creatorProfile = Bukkit.createProfile(npc.getData().getCreator()); + translator.translate("npc_info_general") + .replace("name", npc.getData().getName()) + .replace("id", npc.getData().getId()) + .replace("id_short", npc.getData().getId().substring(0, 13) + "...") + .replace("creator_uuid", npc.getData().getCreator().toString()) + .replace("creator_uuid_short", creatorProfile.completeFromCache() ? npc.getData().getCreator().toString().substring(0, 13) + "..." : npc.getData().getCreator().toString().substring(0, 13)) + .replace("creator_name", creatorProfile.getName() != null ? creatorProfile.getName() : ((SimpleMessage) translator.translate("unknown")).getMessage()) + .replace("displayname", npc.getData().getDisplayName()) + .replace("type", "") // Not ideal solution but should work fine for now. + .replace("location_x", COORDS_FORMAT.format(loc.x())) + .replace("location_y", COORDS_FORMAT.format(loc.y())) + .replace("location_z", COORDS_FORMAT.format(loc.z())) + .replace("world", loc.getWorld().getName()) + .replace("glow", glowingStateTranslated) + .replace("is_collidable", getTranslatedBoolean(npc.getData().isCollidable())) + .replace("is_turn_to_player", getTranslatedBoolean(npc.getData().isTurnToPlayer())) + .replace("is_show_in_tab", getTranslatedBoolean(npc.getData().isShowInTab())) + .replace("is_skin_mirror", getTranslatedBoolean(npc.getData().isMirrorSkin())) + .replace("interaction_cooldown", npc.getData().getInteractionCooldown() <= 0 ? getTranslatedState(false) : interactionCooldown.toString()) + .replace("scale", String.valueOf(npc.getData().getScale())) + .replace("visibility_distance", visibilityDistanceTranslated) + .replace("actions_total", String.valueOf(actionsTotal)) + .send(sender); + } + + // NOTE: Might need to be improved later down the line, should get work done for now. + private @NotNull String getTranslatedBoolean(final boolean bool) { + return (bool) ? ((SimpleMessage) translator.translate("true")).getMessage() : ((SimpleMessage) translator.translate("false")).getMessage(); + } + + // NOTE: Might need to be improved later down the line, should get work done for now. + private @NotNull String getTranslatedState(final boolean bool) { + return (bool) ? ((SimpleMessage) translator.translate("enabled")).getMessage() : ((SimpleMessage) translator.translate("disabled")).getMessage(); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/InteractionCooldownCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/InteractionCooldownCMD.java new file mode 100644 index 00000000..f477e01e --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/InteractionCooldownCMD.java @@ -0,0 +1,103 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcModifyEvent; +import de.oliver.fancynpcs.api.utils.Interval; +import de.oliver.fancynpcs.api.utils.Interval.Unit; +import de.oliver.fancynpcs.commands.exceptions.ReplyingParseException; +import it.unimi.dsi.fastutil.Pair; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotations.Argument; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; +import org.incendo.cloud.annotations.parser.Parser; +import org.incendo.cloud.annotations.suggestion.Suggestions; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public enum InteractionCooldownCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + private static final Pattern SPLIT_PATTERN = Pattern.compile("(?<=\\d)(?=\\D)"); + + @Command("npc interaction_cooldown ") + @Permission("fancynpcs.command.npc.interaction_cooldown") + public void onInteractionCooldown( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull @Argument(parserName = "InteractionCooldownCMD/cooldown") Interval cooldown + ) { + // Calling the event and updating the cooldown if not cancelled. + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.INTERACTION_COOLDOWN, cooldown, sender).callEvent()) { + npc.getData().setInteractionCooldown((float) cooldown.as(Unit.MILLISECONDS) / 1000F); + translator.translate(cooldown.as(Unit.MILLISECONDS) != 0 ? "npc_interaction_cooldown_set" : "npc_interaction_cooldown_disabled") + .replace("npc", npc.getData().getName()) + .replace("time", cooldown.toString()) + .send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } + + /* PARSERS AND SUGGESTIONS */ + + @Parser(name = "InteractionCooldownCMD/cooldown", suggestions = "InteractionCooldownCMD/cooldown") + public @NotNull Interval parse(final CommandContext context, final CommandInput input) { + final String value = input.readString(); + // Handling 'disabled' as interval of 0 milliseconds. This is how plugin determines whether interaction cooldown is enabled or not. + if (value.equalsIgnoreCase("disabled")) + return Interval.of(0, Unit.MILLISECONDS); + // Splitting user input between a digit and a letter. + final String[] split = SPLIT_PATTERN.split(value); + final @Nullable Long num = (split.length == 2) ? parseLong(split[0]) : null; + final @Nullable Unit unit = (split.length == 2) ? Unit.fromShortCode(split[1].toLowerCase()) : null; + // Sending error message to the sender if input cannot be converted to a valid interval. + if (num == null || unit == null) + throw ReplyingParseException.replying(() -> translator.translate("command_invalid_interval").replaceStripped("input", value).send(context.sender())); + return Interval.of(Math.max(0, num), unit); + } + + @Suggestions(value = "InteractionCooldownCMD/cooldown") + public @NotNull Collection suggest(final CommandContext context, final CommandInput input) { + final String value = input.readString(); + // Splitting user input between a digit and a letter. + final String[] split = SPLIT_PATTERN.split(value); + final @Nullable Long num = parseLong(split[0]); + // Checking that the number is not null. + return (num == null || num <= 0) + ? List.of("30s", "5min", "8h", "disabled") + : new ArrayList<>() {{ + add("disabled"); + addAll(Stream.of( + Pair.of(Interval.of(num, Unit.MILLISECONDS), Unit.MILLISECONDS), + Pair.of(Interval.of(num, Unit.SECONDS), Unit.SECONDS), + Pair.of(Interval.of(num, Unit.MINUTES), Unit.MINUTES), + Pair.of(Interval.of(num, Unit.HOURS), Unit.HOURS) + ).map(pair -> num + pair.second().getShortCode()).toList()); + }}; + } + + /* UTILITY METHODS */ + + private @Nullable Long parseLong(final @NotNull String value) { + try { + return Long.parseLong(value); + } catch (final NumberFormatException e) { + return null; + } + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/ListCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/ListCMD.java new file mode 100644 index 00000000..999180af --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/ListCMD.java @@ -0,0 +1,82 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Flag; +import org.incendo.cloud.annotations.Permission; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.text.DecimalFormat; +import java.util.Comparator; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; + +public enum ListCMD { + INSTANCE; // SINGLETON + + private static final DecimalFormat COORDS_FORMAT = new DecimalFormat("#.##"); + + static { + COORDS_FORMAT.setMinimumFractionDigits(2); + } + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command("npc list") + @Permission("fancynpcs.command.npc.list") + public void onCommand( + final @NotNull CommandSender sender, + final @Nullable @Flag("type") EntityType type, + final @Nullable @Flag("sort") SortType sort + ) { + Stream stream = FancyNpcs.getInstance().getNpcManagerImpl().getAllNpcs().stream(); + // Excluding NPCs not created by the sender, if PLAYER_NPCS_FEATURE_FLAG is enabled and sender is a player. + if (FancyNpcs.PLAYER_NPCS_FEATURE_FLAG.isEnabled() && sender instanceof Player player) + stream = stream.filter(npc -> npc.getData().getCreator().equals(player.getUniqueId())); + // Excluding NPCs that are not of a specified type, if desired. + if (type != null) + stream = stream.filter(npc -> npc.getData().getType() == type); + // Sorting based on SortType choice. Defaults to SortType.NAME. There might be more sort types in the future which should be handled here accordingly. + switch (sort != null ? sort : SortType.NAME) { + case NAME -> stream = stream.sorted(Comparator.comparing(npc -> npc.getData().getName())); + case NAME_REVERSED -> + stream = stream.sorted(Comparator.comparing(npc -> ((Npc) npc).getData().getName()).reversed()); // This needs a cast for some reason. + } + translator.translate("npc_list_header").send(sender); + // Using AtomicInteger counter because streams don't expose entry index. + final AtomicInteger count = new AtomicInteger(0); + // Iterating over each NPC referenced in the stream. Usage of forEachOrdered should presumably preserve element order. + stream.forEachOrdered(npc -> { + translator.translate("npc_list_entry") + .replace("number", String.valueOf(count.incrementAndGet())) + .replace("npc", npc.getData().getName()) + .replace("location_x", COORDS_FORMAT.format(npc.getData().getLocation().x())) + .replace("location_y", COORDS_FORMAT.format(npc.getData().getLocation().y())) + .replace("location_z", COORDS_FORMAT.format(npc.getData().getLocation().z())) + .replace("world", npc.getData().getLocation().getWorld().getName()) + .send(sender); + }); + + final int totalCount = count.get(); // change this, once we have a page system + translator.translate("npc_list_footer") + .replace("count", String.valueOf(count)) + .replace("count_formatted", "· ".repeat(3 - String.valueOf(count).length()) + count) + .replace("total", String.valueOf(totalCount)) + .replace("total_formatted", "· ".repeat(3 - String.valueOf(totalCount).length()) + totalCount) + .send(sender); + } + + /** + * {@link SortType ListCMD.SortType} enum contains all possible sort types for the {@code /npc list} command. + */ + public enum SortType { + NAME, NAME_REVERSED + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/MoveHereCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/MoveHereCMD.java new file mode 100644 index 00000000..67861c83 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/MoveHereCMD.java @@ -0,0 +1,41 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcModifyEvent; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; + +import org.jetbrains.annotations.NotNull; + +public enum MoveHereCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command(value = "npc move_here ", requiredSender = Player.class) + @Permission("fancynpcs.command.npc.move_here") + public void onCommand( + final @NotNull Player sender, + final @NotNull Npc npc + ) { + final Location location = sender.getLocation(); + final String oldWorld = npc.getData().getLocation().getWorld().getName(); + // Calling the event and moving the NPc to location of the sender, if not cancelled. + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.LOCATION, location, sender).callEvent()) { + npc.getData().setLocation(location); + if (oldWorld.equals(location.getWorld().getName())) { + npc.updateForAll(); + } else { + npc.removeForAll(); + npc.spawnForAll(); + } + translator.translate("npc_move_here_success").replace("npc", npc.getData().getName()).send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/MoveToCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/MoveToCMD.java new file mode 100644 index 00000000..29d0a4ed --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/MoveToCMD.java @@ -0,0 +1,64 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcModifyEvent; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.annotations.Argument; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Flag; +import org.incendo.cloud.annotations.Permission; + +import java.text.DecimalFormat; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public enum MoveToCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + private static final DecimalFormat COORDS_FORMAT = new DecimalFormat("#.##"); + + @Command("npc move_to [world]") + @Permission("fancynpcs.command.npc.move_to") + public void onCommand( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull @Argument(suggestions = "relative_location") Location location, + final @Nullable World world, + final @Flag("look-in-my-direction") boolean shouldLookInSenderDirection + ) { + // Finalizing World argument. Player-like senders don't have to specify the 'world' argument which then defaults to the World sender is currently in. + final World finalWorld = (world == null && sender instanceof Player player) ? player.getWorld() : world; + // Sending error message if finalized World argument ended up being null. This can happen when command is executed by console and 'world' argument was not specified. + if (finalWorld == null) { + translator.translate("npc_move_to_failure_must_specify_world").send(sender); + return; + } + // Updating World of the finalized Location. This should never pass a null value. + location.setWorld(finalWorld); + // Updating direction NPC will be looking at. Only if '--look-in-my-direction' is present and sender is player. + if (shouldLookInSenderDirection && sender instanceof Player player) + location.setDirection(player.getLocation().subtract(location).toVector()); + // Calling the event and re-locating NPC if not cancelled. + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.LOCATION, location, sender).callEvent()) { + npc.getData().setLocation(location); + npc.updateForAll(); + translator.translate("npc_move_to_success") + .replace("npc", npc.getData().getName()) + .replace("x", COORDS_FORMAT.format(location.x())) + .replace("y", COORDS_FORMAT.format(location.y())) + .replace("z", COORDS_FORMAT.format(location.z())) + .replace("world", finalWorld.getName()) + .send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/NearbyCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/NearbyCMD.java new file mode 100644 index 00000000..7a29eb12 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/NearbyCMD.java @@ -0,0 +1,98 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import org.bukkit.Location; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Flag; +import org.incendo.cloud.annotations.Permission; + +import java.text.DecimalFormat; +import java.util.Comparator; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public enum NearbyCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + private static final DecimalFormat COORDS_FORMAT = new DecimalFormat("#.##"); + private static final DecimalFormat DISTANCE_FORMAT = new DecimalFormat("#.#"); + + static { + COORDS_FORMAT.setMinimumFractionDigits(2); + } + + @Command(value = "npc nearby", requiredSender = Player.class) + @Permission("fancynpcs.command.npc.nearby") + public void onCommand( + final @NotNull Player sender, + final @Nullable @Flag("radius") Long radius, + final @Nullable @Flag("type") EntityType type, + final @Nullable @Flag("sort") SortType sort + ) { + Stream stream = FancyNpcs.getInstance().getNpcManagerImpl().getAllNpcs().stream(); + // Getting senderLocation of the sender. + final Location senderLocation = sender.getLocation(); + // Creating a counter which is increased by 1 for every NPC present in player's world. + final AtomicInteger totalCount = new AtomicInteger(0); + // Excluding NPCs from different worlds. This also increments the counter defined above. + stream = stream.filter(npc -> { + if (npc.getData().getLocation().getWorld().equals(senderLocation.getWorld())) { + totalCount.incrementAndGet(); + return true; + } + return false; + }); + // Excluding NPCs not created by the sender, if PLAYER_NPCS_FEATURE_FLAG is enabled and sender is a player. + if (FancyNpcs.PLAYER_NPCS_FEATURE_FLAG.isEnabled()) + stream = stream.filter(npc -> npc.getData().getCreator().equals(sender.getUniqueId())); + // Excluding NPCs that are not in radius, if specified and sender is a player. (radius is calculated from the senderLocation of player) + if (radius != null) + stream = stream.filter(npc -> npc.getData().getLocation().distance(senderLocation) <= radius); + // Excluding NPCs that are not of a specified type, if desired. + if (type != null) + stream = stream.filter(npc -> npc.getData().getType() == type); + // Sorting based on SortType choice. Defaults to SortType.NEAREST. There might be more sort types in the future which should be handled here accordingly. + switch (sort != null ? sort : SortType.NEAREST) { // This should never produce NPE. + case NAME -> stream = stream.sorted(Comparator.comparing(npc -> npc.getData().getName())); + case NAME_REVERSED -> stream = stream.sorted(Comparator.comparing(npc -> ((Npc) npc).getData().getName()).reversed()); + case NEAREST -> stream = stream.sorted(Comparator.comparingDouble(npc -> npc.getData().getLocation().distance(senderLocation))); + case FARTHEST -> stream = stream.sorted(Comparator.comparingDouble(npc -> ((Npc) npc).getData().getLocation().distance(senderLocation)).reversed()); + } + translator.translate("npc_nearby_header").send(sender); + // Using AtomicInteger counter because streams don't expose entry index. + final AtomicInteger count = new AtomicInteger(0); + // Iterating over each NPC referenced in the stream. Usage of forEachOrdered should presumably preserve element order. + stream.forEachOrdered(npc -> { + translator.translate("npc_nearby_entry") + .replace("number", String.valueOf(count.incrementAndGet())) + .replace("npc", npc.getData().getName()) + .replace("distance", DISTANCE_FORMAT.format(npc.getData().getLocation().distance(senderLocation))) + .replace("location_x", COORDS_FORMAT.format(npc.getData().getLocation().x())) + .replace("location_y", COORDS_FORMAT.format(npc.getData().getLocation().y())) + .replace("location_z", COORDS_FORMAT.format(npc.getData().getLocation().z())) + .replace("world", npc.getData().getLocation().getWorld().getName()) + .send(sender); + }); + translator.translate("npc_nearby_footer") + .replace("count", String.valueOf(count)) + .replace("count_formatted", "· ".repeat(3 - String.valueOf(count).length()) + count) + .replace("total", String.valueOf(totalCount)) + .replace("total_formatted", "· ".repeat(3 - String.valueOf(totalCount).length()) + totalCount) + .send(sender); + } + + // SortType enum contains all possible sort types for the '/npc nearby' command. + public enum SortType { + NAME, NAME_REVERSED, NEAREST, FARTHEST + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/RemoveCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/RemoveCMD.java new file mode 100644 index 00000000..5fa009b2 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/RemoveCMD.java @@ -0,0 +1,46 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcRemoveEvent; +import de.oliver.fancynpcs.api.events.NpcStopLookingEvent; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; + +import org.jetbrains.annotations.NotNull; + +public enum RemoveCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command("npc remove ") + @Permission("fancynpcs.command.npc.remove") + public void onRemove( + final @NotNull CommandSender sender, + final @NotNull Npc npc + ) { + // Calling the event and removing the NPC if not cancelled. + if (new NpcRemoveEvent(npc, sender).callEvent()) { + npc.removeForAll(); + // Iterating over all online players that the NPC is currently looking at. + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + if (npc.getIsLookingAtPlayer().getOrDefault(onlinePlayer.getUniqueId(), false)) { + // Changing state as Npc#getIsLookingAtPlayer#get(...) called within the event listener should return false now. + npc.getIsLookingAtPlayer().put(onlinePlayer.getUniqueId(), false); + // Calling the NpcStopLookingEvent event. + new NpcStopLookingEvent(npc, onlinePlayer).callEvent(); + } + } + FancyNpcs.getInstance().getNpcManagerImpl().removeNpc(npc); + translator.translate("npc_remove_success").replace("npc", npc.getData().getName()).send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/ScaleCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/ScaleCMD.java new file mode 100644 index 00000000..7d85e0c5 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/ScaleCMD.java @@ -0,0 +1,35 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcModifyEvent; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; +import org.jetbrains.annotations.NotNull; + +public enum ScaleCMD { + INSTANCE; + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command("npc scale ") + @Permission("fancynpcs.command.npc.scale") + public void onScale( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final float factor + ) { + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.SCALE, factor, sender).callEvent()) { + npc.getData().setScale(factor); + npc.updateForAll(); + translator.translate("npc_scale_set_success") + .replace("npc", npc.getData().getName()) + .replace("scale", String.valueOf(factor)) + .send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/ShowInTabCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/ShowInTabCMD.java new file mode 100644 index 00000000..7530aa92 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/ShowInTabCMD.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcModifyEvent; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public enum ShowInTabCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command("npc show_in_tab [state]") + @Permission("fancynpcs.command.npc.show_in_tab") + public void onCommand( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @Nullable Boolean state + ) { + final boolean finalState = (state == null) ? !npc.getData().isShowInTab() : state; + // Calling the event and updating the state if not cancelled. + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.SHOW_IN_TAB, finalState, sender).callEvent()) { + npc.getData().setShowInTab(finalState); + npc.removeForAll(); + npc.create(); + npc.spawnForAll(); + translator.translate(finalState ? "npc_show_in_tab_set_true" : "npc_show_in_tab_set_false").replace("npc", npc.getData().getName()).send(sender); + return; + } + // Otherwise, sending error message to the sender. + translator.translate("command_npc_modification_cancelled").send(sender); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/SkinCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/SkinCMD.java new file mode 100644 index 00000000..09781943 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/SkinCMD.java @@ -0,0 +1,101 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcModifyEvent; +import de.oliver.fancynpcs.api.skins.SkinData; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.incendo.cloud.annotations.Argument; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Flag; +import org.incendo.cloud.annotations.Permission; +import org.incendo.cloud.annotations.suggestion.Suggestions; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public enum SkinCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + /* PARSERS AND SUGGESTIONS */ + + @Command("npc skin ") + @Permission("fancynpcs.command.npc.skin") + public void onSkin( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull @Argument(suggestions = "SkinCMD/skin") String skin, + final @Flag("slim") boolean slim + ) { + if (npc.getData().getType() != EntityType.PLAYER) { + translator.translate("command_unsupported_npc_type").send(sender); + return; + } + + final boolean isMirror = skin.equalsIgnoreCase("@mirror"); + final boolean isNone = skin.equalsIgnoreCase("@none"); + if (isMirror) { + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.MIRROR_SKIN, true, sender).callEvent()) { + npc.getData().setMirrorSkin(true); + npc.removeForAll(); + npc.create(); + npc.spawnForAll(); + translator.translate("npc_skin_set_mirror").replace("npc", npc.getData().getName()).send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } else if (isNone) { + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.SKIN, false, sender).callEvent() && new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.SKIN, null, sender).callEvent()) { + npc.getData().setMirrorSkin(false); + npc.getData().setSkinData(null); + npc.removeForAll(); + npc.create(); + npc.spawnForAll(); + translator.translate("npc_skin_set_none").replace("npc", npc.getData().getName()).send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } else { + SkinData.SkinVariant variant = slim ? SkinData.SkinVariant.SLIM : SkinData.SkinVariant.AUTO; + SkinData skinData = FancyNpcs.getInstance().getSkinManagerImpl().getByIdentifier(skin, variant); + if (!skinData.hasTexture()) { + translator.translate("npc_skin_set_later").replace("npc", npc.getData().getName()).send(sender); + } + + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.SKIN, false, sender).callEvent() && new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.SKIN, skinData, sender).callEvent()) { + npc.getData().setMirrorSkin(false); + npc.getData().setSkinData(skinData); + npc.removeForAll(); + npc.create(); + npc.spawnForAll(); + translator.translate("npc_skin_set") + .replace("npc", npc.getData().getName()) + .replace("name", skinData.getIdentifier()) + .send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } + } + + /* UTILITY METHODS */ + + @Suggestions("SkinCMD/skin") + public List suggestSkin(final CommandContext context, final CommandInput input) { + return new ArrayList<>() {{ + add("@none"); + add("@mirror"); + Bukkit.getOnlinePlayers().stream().map(Player::getName).forEach(this::add); + }}; + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/TeleportCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/TeleportCMD.java new file mode 100644 index 00000000..60598031 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/TeleportCMD.java @@ -0,0 +1,39 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; + +import org.jetbrains.annotations.NotNull; + +public enum TeleportCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command(value = "npc teleport ", requiredSender = Player.class) + @Permission("fancynpcs.command.npc.teleport") + public void onTeleport( + final @NotNull Player sender, + final @NotNull Npc npc + ) { + final Location location = npc.getData().getLocation(); + // Checking if the world is still loaded. + if (location.getWorld() == null) { + translator.translate("npc_teleport_failure_world_not_loaded").send(sender); + return; + } + // Teleporting and sending message to the sender. This operation can occasionally fail. + sender.teleportAsync(location).whenComplete((isSuccess, thr) -> { + translator.translate(isSuccess ? "npc_teleport_success" : "npc_teleport_failure_exception").replace("npc", npc.getData().getName()).send(sender); + // Printing stacktrace to the console in case an exception occurred. + if (thr != null) + thr.printStackTrace(); + }); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/TurnToPlayerCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/TurnToPlayerCMD.java new file mode 100644 index 00000000..4d939494 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/TurnToPlayerCMD.java @@ -0,0 +1,36 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcModifyEvent; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public enum TurnToPlayerCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command("npc turn_to_player [state]") + @Permission("fancynpcs.command.npc.turn_to_player") + public void onTurnToPlayer( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @Nullable Boolean state + ) { + final boolean finalState = (state == null) ? !npc.getData().isTurnToPlayer() : state; + // Calling the event and updating the state if not cancelled. + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.TURN_TO_PLAYER, finalState, sender).callEvent()) { + npc.getData().setTurnToPlayer(finalState); + translator.translate(finalState ? "npc_turn_to_player_set_true" : "npc_turn_to_player_set_false").replace("npc", npc.getData().getName()).send(sender); + return; + } + translator.translate("command_npc_modification_cancelled").send(sender); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/TypeCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/TypeCMD.java new file mode 100644 index 00000000..1114341a --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/TypeCMD.java @@ -0,0 +1,56 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcModifyEvent; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; +import org.jetbrains.annotations.NotNull; + +public enum TypeCMD { + INSTANCE; // SINGLETON + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + @Command("npc type ") + @Permission("fancynpcs.command.npc.type") + public void onType( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @NotNull EntityType type + ) { + // Calling the event and updating the type if not cancelled. + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.TYPE, type, sender).callEvent()) { + npc.getData().setType(type); + + if (type != EntityType.PLAYER) { + npc.getData().setShowInTab(false); + npc.getData().setSkinData(null); + npc.getData().setMirrorSkin(false); + } + + if (!type.isAlive() && npc.getData().getEquipment() != null) { + npc.getData().getEquipment().clear(); + } + + FancyNpcsPlugin.get().getNpcThread().submit(() -> { + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + npc.remove(onlinePlayer); + } + npc.create(); + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + npc.spawn(onlinePlayer); + } + }); + translator.translate("npc_type_success").replace("npc", npc.getData().getName()).replace("type", type.name().toLowerCase()).send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/VisibilityDistanceCMD.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/VisibilityDistanceCMD.java new file mode 100644 index 00000000..146b6e1f --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/commands/npc/VisibilityDistanceCMD.java @@ -0,0 +1,76 @@ +package de.oliver.fancynpcs.commands.npc; + +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcModifyEvent; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotations.Argument; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Permission; +import org.incendo.cloud.annotations.parser.Parser; +import org.incendo.cloud.annotations.suggestion.Suggestions; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; + +import java.util.List; + +import org.jetbrains.annotations.NotNull; + +public enum VisibilityDistanceCMD { + INSTANCE; + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + // Storing in a static variable to avoid re-creating the array each time suggestion is requested. + private final List DISTANCE_SUGGESTIONS = List.of("always_visible", "default", "not_visible"); + + @Command("npc visibility_distance ") + @Permission("fancynpcs.command.npc.visibility_distance") + public void onVisibilityDistance( + final @NotNull CommandSender sender, + final @NotNull Npc npc, + final @Argument(parserName = "VisibilityDistanceCMD/distance") int distance + ) { + final int finalDistance = Math.clamp(distance, -1, Integer.MAX_VALUE); + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.VISIBILITY_DISTANCE, distance, sender).callEvent()) { + npc.getData().setVisibilityDistance(finalDistance); + npc.updateForAll(); + translator.translate(finalDistance == -1 ? "npc_visibility_distance_set_default" : finalDistance == 0 ? "npc_visibility_distance_set_not_visible" : finalDistance == Integer.MAX_VALUE ? "npc_visibility_distance_set_always_visible" : "npc_visibility_distance_set_value") + .replace("npc", npc.getData().getName()) + .replace("distance", (finalDistance > -1) ? String.valueOf(finalDistance) : String.valueOf(FancyNpcs.getInstance().getFancyNpcConfig().getVisibilityDistance())) + .send(sender); + } else { + translator.translate("command_npc_modification_cancelled").send(sender); + } + } + + /* PARSERS AND SUGGESTIONS */ + + @Parser(name = "VisibilityDistanceCMD/distance", suggestions = "VisibilityDistanceCMD/distance") + public @NotNull Integer parse(final CommandContext context, final CommandInput input) { + // If 'default' string is provided, it is being handled as -1. + if (input.peekString().equalsIgnoreCase("default")) { + input.readString(); + return -1; + } + // If 'not_visible' string is provided, it is being handled as 0. + if (input.peekString().equalsIgnoreCase("not_visible")) { + input.readString(); + return 0; + } + // If 'always_visible' string is provided, it is being handled as Integer.MAX_VALUE. + if (input.peekString().equalsIgnoreCase("always_visible")) { + input.readString(); + return Integer.MAX_VALUE; + } + // Otherwise, reading next argument as int. + return input.readInteger(); + } + + @Suggestions("VisibilityDistanceCMD/distance") + public @NotNull List suggest(final CommandContext context, final CommandInput input) { + return DISTANCE_SUGGESTIONS; + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerChangedWorldListener.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerChangedWorldListener.java new file mode 100644 index 00000000..28744208 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerChangedWorldListener.java @@ -0,0 +1,18 @@ +package de.oliver.fancynpcs.listeners; + +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerChangedWorldEvent; + +public class PlayerChangedWorldListener implements Listener { + + @EventHandler + public void onWorldChange(PlayerChangedWorldEvent event) { + for (Npc npc : FancyNpcs.getInstance().getNpcManager().getAllNpcs()) { + npc.checkAndUpdateVisibility(event.getPlayer()); + } + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerJoinListener.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerJoinListener.java new file mode 100644 index 00000000..a2947aee --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerJoinListener.java @@ -0,0 +1,56 @@ +package de.oliver.fancynpcs.listeners; + +import com.destroystokyo.paper.profile.ProfileProperty; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.skins.SkinData; +import de.oliver.fancynpcs.v1_19_4.PacketReader_1_19_4; +import de.oliver.fancynpcs.v1_20.PacketReader_1_20; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; + +public class PlayerJoinListener implements Listener { + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + String mcVersion = Bukkit.getMinecraftVersion(); + if (mcVersion.equals("1.19.4")) { + PacketReader_1_19_4.inject(event.getPlayer()); + } else if (mcVersion.equals("1.20")) { + PacketReader_1_20.inject(event.getPlayer()); + } + + for (Npc npc : FancyNpcs.getInstance().getNpcManagerImpl().getAllNpcs()) { + npc.getIsVisibleForPlayer().put(event.getPlayer().getUniqueId(), false); + npc.getIsLookingAtPlayer().put(event.getPlayer().getUniqueId(), false); + npc.getIsTeamCreated().put(event.getPlayer().getUniqueId(), false); + } + + // don't spawn the npc for player if he just joined + FancyNpcs.getInstance().getVisibilityTracker().addJoinDelayPlayer(event.getPlayer().getUniqueId()); + FancyNpcs.getInstance().getScheduler().runTaskLater(null, 20L * 2, () -> FancyNpcs.getInstance().getVisibilityTracker().removeJoinDelayPlayer(event.getPlayer().getUniqueId())); + + if (!FancyNpcs.getInstance().getFancyNpcConfig().isMuteVersionNotification() && event.getPlayer().hasPermission("FancyNpcs.admin")) { + FancyNpcs.getInstance().getScheduler().runTaskAsynchronously( + () -> FancyNpcs.getInstance().getVersionConfig().checkVersionAndDisplay(event.getPlayer(), true) + ); + } + + for (ProfileProperty property : event.getPlayer().getPlayerProfile().getProperties()) { + if (!property.getName().equals("textures")) { + continue; + } + + SkinData skinData = new SkinData( + event.getPlayer().getUniqueId().toString(), + SkinData.SkinVariant.AUTO, + property.getValue(), + property.getSignature() + ); + + FancyNpcs.getInstance().getSkinManagerImpl().getMemCache().addSkin(skinData); + } + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerNpcsListener.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerNpcsListener.java new file mode 100644 index 00000000..c5ee1fef --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerNpcsListener.java @@ -0,0 +1,93 @@ +package de.oliver.fancynpcs.listeners; + +import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.player.PlotPlayer; +import com.plotsquared.core.plot.Plot; +import de.oliver.fancylib.translations.Translator; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcCreateEvent; +import de.oliver.fancynpcs.api.events.NpcModifyEvent; +import de.oliver.fancynpcs.api.events.NpcRemoveEvent; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +import java.util.Comparator; +import java.util.Map; + +public class PlayerNpcsListener implements Listener { + + private final Translator translator = FancyNpcs.getInstance().getTranslator(); + + private static final boolean isUsingPlotSquared = FancyNpcs.getInstance().isUsingPlotSquared(); + + @EventHandler + public void onNpcCreate(NpcCreateEvent event) { + if (!(event.getCreator() instanceof Player player)) { + return; + } + + if (isUsingPlotSquared) { + PlotPlayer plotPlayer = PlotSquared.platform().playerManager().getPlayer(player.getUniqueId()); + Plot currentPlot = plotPlayer.getCurrentPlot(); + if ((currentPlot == null || !currentPlot.isOwner(player.getUniqueId())) && !player.hasPermission("fancynpcs.admin")) { + translator.translate("player_npcs_create_failure_not_owned_plot").send(player); + event.setCancelled(true); + return; + } + } + int maxNpcs = FancyNpcs.getInstance().getFancyNpcConfig().getMaxNpcsPerPermission() + .entrySet().stream() + .filter(entry -> player.hasPermission(entry.getKey())) + .max(Comparator.comparingInt(Map.Entry::getValue)) + .map(Map.Entry::getValue) + .orElse(Integer.MAX_VALUE); + + int npcAmount = 0; + for (Npc npc : FancyNpcs.getInstance().getNpcManager().getAllNpcs()) { + if (npc.getData().getCreator().equals(player.getUniqueId())) + npcAmount++; + } + if (npcAmount >= maxNpcs && !player.hasPermission("fancynpcs.admin")) { + translator.translate("player_npcs_create_failure_limit_reached").send(player); + event.setCancelled(true); + return; + } + } + + @EventHandler + public void onNpcRemove(NpcRemoveEvent event) { + if (!(event.getSender() instanceof Player player)) { + return; + } + + if (!event.getNpc().getData().getCreator().equals(player.getUniqueId()) && !player.hasPermission("fancynpcs.admin")) { + translator.translate("player_npcs_cannot_modify_npc").send(player); + event.setCancelled(true); + return; + } + } + + @EventHandler + public void onNpcModify(NpcModifyEvent event) { + if (!(event.getModifier() instanceof Player player)) { + return; + } + + if (!event.getNpc().getData().getCreator().equals(player.getUniqueId()) && !player.hasPermission("fancynpcs.admin")) { + translator.translate("player_npcs_cannot_modify_npc").send(player); + event.setCancelled(true); + return; + } + if (isUsingPlotSquared && event.getModification() == NpcModifyEvent.NpcModification.LOCATION) { + PlotPlayer plotPlayer = PlotSquared.platform().playerManager().getPlayer(player.getUniqueId()); + Plot currentPlot = plotPlayer.getCurrentPlot(); + + if ((currentPlot == null || !currentPlot.isOwner(player.getUniqueId())) && !player.hasPermission("fancynpcs.admin")) { + translator.translate("player_npcs_cannot_move_npc").send(player); + event.setCancelled(true); + } + } + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerQuitListener.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerQuitListener.java new file mode 100644 index 00000000..7dc6f7a9 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerQuitListener.java @@ -0,0 +1,25 @@ +package de.oliver.fancynpcs.listeners; + +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.events.NpcStopLookingEvent; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; + +public class PlayerQuitListener implements Listener { + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + for (Npc npc : FancyNpcs.getInstance().getNpcManagerImpl().getAllNpcs()) { + // Changing isLookingAtPlayer state (of event player) to false. + // This allows the NpcStartLookingEvent to be called when player joins back. (Because otherwise, state would remain true and no change would be detected) + npc.getIsVisibleForPlayer().put(event.getPlayer().getUniqueId(), false); + npc.getIsLookingAtPlayer().put(event.getPlayer().getUniqueId(), false); + npc.getIsTeamCreated().put(event.getPlayer().getUniqueId(), false); + // Calling NpcStopLookingEvent. + Bukkit.getPluginManager().callEvent(new NpcStopLookingEvent(npc, event.getPlayer())); + } + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerTeleportListener.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerTeleportListener.java new file mode 100644 index 00000000..02cab378 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerTeleportListener.java @@ -0,0 +1,23 @@ +package de.oliver.fancynpcs.listeners; + +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.jetbrains.annotations.NotNull; + +public class PlayerTeleportListener implements Listener { + + @EventHandler(priority = EventPriority.MONITOR) + public void onTeleport(@NotNull final PlayerTeleportEvent event) { + Player player = event.getPlayer(); + + for (Npc npc : FancyNpcs.getInstance().getNpcManager().getAllNpcs()) { + npc.checkAndUpdateVisibility(player); + } + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerUseUnknownEntityListener.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerUseUnknownEntityListener.java new file mode 100644 index 00000000..d66efcfc --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/listeners/PlayerUseUnknownEntityListener.java @@ -0,0 +1,29 @@ +package de.oliver.fancynpcs.listeners; + +import com.destroystokyo.paper.event.player.PlayerUseUnknownEntityEvent; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.actions.ActionTrigger; +import org.bukkit.entity.EntityType; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.inventory.EquipmentSlot; + +public class PlayerUseUnknownEntityListener implements Listener { + + @EventHandler + public void onPlayerUseUnknownEntity(final PlayerUseUnknownEntityEvent event) { + final Npc npc = FancyNpcs.getInstance().getNpcManagerImpl().getNpc(event.getEntityId()); + // Skipping entities that are not FancyNpcs' NPCs + if (npc == null) + return; + // PlayerUseUnknownEntityEvent can optionally be ALSO called for OFF-HAND slot. Making sure to run logic only ONCE. + if (event.getHand() == EquipmentSlot.HAND) { + // PlayerUseUnknownEntityEvent can be called multiple times for interactions that are NOT attacks, making sure to run logic only ONCE. + if (event.isAttack() || event.getClickedRelativePosition() == null || npc.getData().getType() == EntityType.ARMOR_STAND) { + npc.interact(event.getPlayer(), event.isAttack() ? ActionTrigger.LEFT_CLICK : ActionTrigger.RIGHT_CLICK); + } + } + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/loaders/FancyNpcsBootstrapper.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/loaders/FancyNpcsBootstrapper.java new file mode 100644 index 00000000..d294ecae --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/loaders/FancyNpcsBootstrapper.java @@ -0,0 +1,19 @@ +package de.oliver.fancynpcs.loaders; + +import io.papermc.paper.plugin.bootstrap.BootstrapContext; +import io.papermc.paper.plugin.bootstrap.PluginBootstrap; +import io.papermc.paper.plugin.bootstrap.PluginProviderContext; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; + +public class FancyNpcsBootstrapper implements PluginBootstrap { + @Override + public void bootstrap(@NotNull BootstrapContext bootstrapContext) { + + } + + @Override + public @NotNull JavaPlugin createPlugin(@NotNull PluginProviderContext context) { + return PluginBootstrap.super.createPlugin(context); + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/loaders/FancyNpcsLoader.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/loaders/FancyNpcsLoader.java new file mode 100644 index 00000000..34bf4e51 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/loaders/FancyNpcsLoader.java @@ -0,0 +1,12 @@ +package de.oliver.fancynpcs.loaders; + +import io.papermc.paper.plugin.loader.PluginClasspathBuilder; +import io.papermc.paper.plugin.loader.PluginLoader; +import org.jetbrains.annotations.NotNull; + +public class FancyNpcsLoader implements PluginLoader { + @Override + public void classloader(@NotNull PluginClasspathBuilder pluginClasspathBuilder) { + + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/SkinManagerImpl.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/SkinManagerImpl.java new file mode 100644 index 00000000..c13f4c78 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/SkinManagerImpl.java @@ -0,0 +1,218 @@ +package de.oliver.fancynpcs.skins; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import de.oliver.fancylib.UUIDFetcher; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.skins.SkinData; +import de.oliver.fancynpcs.api.skins.SkinGeneratedEvent; +import de.oliver.fancynpcs.api.skins.SkinManager; +import de.oliver.fancynpcs.skins.cache.SkinCache; +import de.oliver.fancynpcs.skins.cache.SkinCacheData; +import de.oliver.fancynpcs.skins.mineskin.MineSkinQueue; +import de.oliver.fancynpcs.skins.mojang.MojangQueue; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.lushplugins.chatcolorhandler.ChatColorHandler; +import org.mineskin.data.Variant; +import org.mineskin.request.GenerateRequest; + +import java.io.File; +import java.net.MalformedURLException; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +public class SkinManagerImpl implements SkinManager, Listener { + + public final static ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool( + 5, + new ThreadFactoryBuilder() + .setNameFormat("FancyNpcs-Skins-%d") + .build() + ); + + private final String SKINS_DIRECTORY = "plugins/FancyNpcs/skins/"; + + private final SkinCache fileCache; + private final SkinCache memCache; + + public SkinManagerImpl(SkinCache fileCache, SkinCache memCache) { + this.fileCache = fileCache; + this.memCache = memCache; + + File skinsDir = new File(SKINS_DIRECTORY); + if (!skinsDir.exists()) { + skinsDir.mkdirs(); + } + } + + @Override + public SkinData getByIdentifier(String identifier, SkinData.SkinVariant variant) { + if (SkinUtils.isUUID(identifier)) { + return getByUUID(UUID.fromString(identifier), variant); + } + + if (SkinUtils.isURL(identifier)) { + return getByURL(identifier, variant); + } + + if (SkinUtils.isFile(identifier)) { + return getByFile(identifier, variant); + } + + if (SkinUtils.isPlaceholder(identifier)) { + String parsed = ChatColorHandler.translate(identifier); + + if (SkinUtils.isPlaceholder(parsed)) { + return null; + } + + return getByIdentifier(parsed, variant); + } + + // is username + UUID uuid = UUIDFetcher.getUUID(identifier); + if (uuid == null) { + return null; + } + + return getByUUID(uuid, variant); + } + + @Override + public SkinData getByUUID(UUID uuid, SkinData.SkinVariant variant) { + SkinData cached = tryToGetFromCache(uuid.toString(), variant); + if (cached != null) { + return cached; + } + + MojangQueue.get().add(new MojangQueue.SkinRequest(uuid.toString(), variant)); + +// GenerateRequest genReq = GenerateRequest.user(uuid); +// genReq.variant(Variant.valueOf(variant.name())); +// MineSkinQueue.get().add(new MineSkinQueue.SkinRequest(uuid.toString(), genReq)); + return new SkinData(uuid.toString(), variant); + } + + @Override + public SkinData getByUsername(String username, SkinData.SkinVariant variant) { + UUID uuid = UUIDFetcher.getUUID(username); + if (uuid == null) { + return null; + } + + return getByUUID(uuid, variant); + } + + @Override + public SkinData getByURL(String url, SkinData.SkinVariant variant) { + SkinData cached = tryToGetFromCache(url, variant); + if (cached != null) { + return cached; + } + + GenerateRequest genReq; + try { + genReq = GenerateRequest.url(url); + } catch (MalformedURLException e) { + FancyNpcs.getInstance().getFancyLogger().error("Invalid URL: " + url); + return null; + } + genReq.variant(Variant.valueOf(variant.name())); + MineSkinQueue.get().add(new MineSkinQueue.SkinRequest(url, genReq)); + return new SkinData(url, variant); + } + + @Override + public SkinData getByFile(String filePath, SkinData.SkinVariant variant) { + SkinData cached = tryToGetFromCache(filePath, variant); + if (cached != null) { + return cached; + } + + File file = new File(SKINS_DIRECTORY + filePath); + if (!file.exists()) { + FancyNpcs.getInstance().getFancyLogger().error("File does not exist: " + filePath); + return null; + } + + GenerateRequest genReq = GenerateRequest.upload(file); + genReq.variant(Variant.valueOf(variant.name())); + MineSkinQueue.get().add(new MineSkinQueue.SkinRequest(filePath, genReq)); + return new SkinData(filePath, variant); + } + + @EventHandler + public void onSkinGenerated(SkinGeneratedEvent event) { + if (event.getSkin() == null || !event.getSkin().hasTexture()) { + FancyNpcs.getInstance().getFancyLogger().error("Generated skin has no texture!"); + return; + } + + for (Npc npc : FancyNpcs.getInstance().getNpcManager().getAllNpcs()) { + SkinData skin = npc.getData().getSkinData(); + if (skin == null) + continue; + + String id = skin.getIdentifier(); + if (SkinUtils.isPlaceholder(id)) { + id = ChatColorHandler.translate(id); + } + + if (id.equals(event.getId())) { + npc.getData().setSkinData(event.getSkin()); + npc.removeForAll(); + npc.spawnForAll(); + FancyNpcs.getInstance().getFancyLogger().info("Updated skin for NPC: " + npc.getData().getName()); + } + } + + cacheSkin(event.getSkin()); + } + + private SkinData tryToGetFromCache(String identifier, SkinData.SkinVariant variant) { + FancyNpcs.getInstance().getFancyLogger().debug("Trying to get skin from mem cache: " + identifier); + + SkinCacheData data = memCache.getSkin(identifier); + if (data != null) { + if (data.skinData().getVariant() != variant) { + FancyNpcs.getInstance().getFancyLogger().debug("Skin variant does not match: " + identifier); + return null; + } + + FancyNpcs.getInstance().getFancyLogger().debug("Found skin from mem cache: " + identifier); + return data.skinData(); + } + + FancyNpcs.getInstance().getFancyLogger().debug("Trying to get skin from file cache: " + identifier); + + data = fileCache.getSkin(identifier); + if (data != null) { + if (data.skinData().getVariant() != variant) { + FancyNpcs.getInstance().getFancyLogger().debug("Skin variant does not match: " + identifier); + return null; + } + + FancyNpcs.getInstance().getFancyLogger().debug("Found skin from file cache: " + identifier); + memCache.addSkin(data.skinData()); + return data.skinData(); + } + + FancyNpcs.getInstance().getFancyLogger().debug("Skin not found in cache: " + identifier); + return null; + } + + public void cacheSkin(SkinData skinData) { + memCache.addSkin(skinData); + fileCache.addSkin(skinData); + } + + public SkinCache getFileCache() { + return fileCache; + } + + public SkinCache getMemCache() { + return memCache; + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/SkinUtils.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/SkinUtils.java new file mode 100644 index 00000000..c87a7ba9 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/SkinUtils.java @@ -0,0 +1,20 @@ +package de.oliver.fancynpcs.skins; + +public class SkinUtils { + + public static boolean isPlaceholder(String identifier) { + return identifier.startsWith("%") && identifier.endsWith("%") || identifier.startsWith("{") && identifier.endsWith("}"); + } + + public static boolean isUUID(String identifier) { + return identifier.length() == 36 && identifier.contains("-"); + } + + public static boolean isURL(String identifier) { + return identifier.startsWith("http"); + } + + public static boolean isFile(String identifier) { + return identifier.endsWith(".png") || identifier.endsWith(".jpg") || identifier.endsWith(".jpeg"); + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/cache/SkinCache.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/cache/SkinCache.java new file mode 100644 index 00000000..92d11d99 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/cache/SkinCache.java @@ -0,0 +1,17 @@ +package de.oliver.fancynpcs.skins.cache; + +import de.oliver.fancynpcs.api.skins.SkinData; + +public interface SkinCache { + + long CACHE_TIME = 1000 * 60 * 60 * 24 * 7; // 1 week + + SkinCacheData getSkin(String identifier); + + void addSkin(SkinData skin); + + void removeSkin(String identifier); + + void clear(); + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/cache/SkinCacheData.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/cache/SkinCacheData.java new file mode 100644 index 00000000..1ff0e6b8 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/cache/SkinCacheData.java @@ -0,0 +1,19 @@ +package de.oliver.fancynpcs.skins.cache; + +import de.oliver.fancynpcs.api.skins.SkinData; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * Represents the cached skin data. For internal use only. + * + * @param skinData The skin data. + * @param lastUpdated The timestamp when the skin data was last updated. + * @param timeToLive The time to live of the skin data in milliseconds. + */ +@ApiStatus.Internal +public record SkinCacheData(@NotNull SkinData skinData, long lastUpdated, long timeToLive) { + public boolean isExpired() { + return timeToLive > 0 && System.currentTimeMillis() - lastUpdated > timeToLive; + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/cache/SkinCacheFile.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/cache/SkinCacheFile.java new file mode 100644 index 00000000..3bd139d5 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/cache/SkinCacheFile.java @@ -0,0 +1,65 @@ +package de.oliver.fancynpcs.skins.cache; + +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.skins.SkinData; +import de.oliver.jdb.JDB; + +import java.io.IOException; +import java.util.Base64; + +public class SkinCacheFile implements SkinCache { + + private final JDB storage; + + public SkinCacheFile() { + this.storage = new JDB("plugins/FancyNpcs/.data"); + } + + @Override + public SkinCacheData getSkin(String identifier) { + String b64ID = Base64.getEncoder().encodeToString(identifier.getBytes()); + + SkinCacheData skinCacheData = null; + try { + skinCacheData = this.storage.get("skins/" + b64ID, SkinCacheData.class); + } catch (IOException e) { + FancyNpcs.getInstance().getFancyLogger().error("Failed to load skin cache"); + FancyNpcs.getInstance().getFancyLogger().error(e); + } + + if (skinCacheData == null) { + return null; + } + + if (skinCacheData.isExpired()) { + this.storage.delete("skins/" + b64ID); + return null; + } + + return skinCacheData; + } + + @Override + public void addSkin(SkinData skin) { + SkinCacheData skinCacheData = new SkinCacheData(skin, System.currentTimeMillis(), CACHE_TIME); + + try { + String b64ID = Base64.getEncoder().encodeToString(skin.getIdentifier().getBytes()); + this.storage.set("skins/" + b64ID, skinCacheData); + } catch (IOException e) { + FancyNpcs.getInstance().getFancyLogger().error("Failed to save skin cache"); + FancyNpcs.getInstance().getFancyLogger().error(e); + } + } + + @Override + public void removeSkin(String identifier) { + String b64ID = Base64.getEncoder().encodeToString(identifier.getBytes()); + this.storage.delete("skins/" + b64ID); + } + + @Override + public void clear() { + storage.delete("skins"); + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/cache/SkinCacheMemory.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/cache/SkinCacheMemory.java new file mode 100644 index 00000000..b8a8a075 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/cache/SkinCacheMemory.java @@ -0,0 +1,47 @@ +package de.oliver.fancynpcs.skins.cache; + + +import de.oliver.fancynpcs.api.skins.SkinData; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class SkinCacheMemory implements SkinCache { + + private final Map cache; + + public SkinCacheMemory() { + this.cache = new ConcurrentHashMap<>(); + } + + @Override + public SkinCacheData getSkin(String identifier) { + if (!cache.containsKey(identifier)) { + return null; + } + + SkinCacheData skinCacheData = cache.get(identifier); + if (skinCacheData.isExpired()) { + cache.remove(identifier); + return null; + } + + return skinCacheData; + } + + @Override + public void addSkin(SkinData skin) { + SkinCacheData skinCacheData = new SkinCacheData(skin, System.currentTimeMillis(), CACHE_TIME); + cache.put(skin.getIdentifier(), skinCacheData); + } + + @Override + public void removeSkin(String identifier) { + cache.remove(identifier); + } + + @Override + public void clear() { + cache.clear(); + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/mineskin/MineSkinAPI.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/mineskin/MineSkinAPI.java new file mode 100644 index 00000000..0edd2677 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/mineskin/MineSkinAPI.java @@ -0,0 +1,93 @@ +package de.oliver.fancynpcs.skins.mineskin; + +import de.oliver.fancynpcs.FancyNpcs; +import org.mineskin.JsoupRequestHandler; +import org.mineskin.MineSkinClient; +import org.mineskin.data.CodeAndMessage; +import org.mineskin.data.JobReference; +import org.mineskin.data.SkinInfo; +import org.mineskin.exception.MineSkinRequestException; +import org.mineskin.request.GenerateRequest; +import org.mineskin.response.MineSkinResponse; +import org.mineskin.response.QueueResponse; + +import java.net.SocketTimeoutException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledExecutorService; + +public class MineSkinAPI { + + private final MineSkinClient client; + + public MineSkinAPI(ScheduledExecutorService executor) { + this.client = MineSkinClient.builder() + .requestHandler(JsoupRequestHandler::new) + .apiKey(FancyNpcs.getInstance().getFancyNpcConfig().getMineSkinApiKey()) + .userAgent("FancyNpcs") + .timeout(1000 * 3) + .getExecutor(executor) + .generateExecutor(executor) + .generateRequestScheduler(executor) + .generateRequestScheduler(executor) + .jobCheckScheduler(executor) + .build(); + } + + public SkinInfo generateSkin(GenerateRequest req) throws RatelimitException { + FancyNpcs.getInstance().getFancyLogger().debug("Generating a skin with MineSkinAPI..."); + + QueueResponse queueResp = null; + JobReference jobResp = null; + + try { + queueResp = client.queue().submit(req).get(); + if (queueResp.getRateLimit().limit().remaining() == 0) { + // TODO use queueResp.getRateLimit().next() instead + throw new RatelimitException(System.currentTimeMillis() + 1000 * 10); // retry in next run + } + + jobResp = queueResp.getJob().waitForCompletion(client).get(); + + SkinInfo skinInfo = jobResp.getOrLoadSkin(client).get(); + + FancyNpcs.getInstance().getFancyLogger().debug("Skin generated with MineSkinApi: " + skinInfo.toString()); + return skinInfo; + } catch (RatelimitException e) { + throw e; // rethrow + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + if (cause instanceof MineSkinRequestException requestException) { + MineSkinResponse response = requestException.getResponse(); + for (CodeAndMessage error : response.getErrors()) { + if (error.code().equals("rate_limit")) { + // TODO use queueResp.getRateLimit().next() instead + throw new RatelimitException(System.currentTimeMillis() + 1000 * 10); // retry in next run + } + FancyNpcs.getInstance().getFancyLogger().warn("Could not fetch skin: " + error.code() + ": " + error.message()); + FancyNpcs.getInstance().getFancyLogger().debug("QueueResp: " + queueResp.toString()); + FancyNpcs.getInstance().getFancyLogger().debug("JobResp: " + jobResp.toString()); + } + } else if (cause instanceof SocketTimeoutException timeoutException) { + FancyNpcs.getInstance().getFancyLogger().warn("Timeout while fetching skin: " + timeoutException.getMessage()); + FancyNpcs.getInstance().getFancyLogger().debug("QueueResp: " + queueResp.toString()); + FancyNpcs.getInstance().getFancyLogger().debug("JobResp: " + jobResp.toString()); + throw new RatelimitException(System.currentTimeMillis() + 1000 * 10); // retry in next run + } else { + FancyNpcs.getInstance().getFancyLogger().error("Error in mineskin request: " + cause.getMessage()); + FancyNpcs.getInstance().getFancyLogger().debug("QueueResp: " + queueResp.toString()); + FancyNpcs.getInstance().getFancyLogger().debug("JobResp: " + jobResp.toString()); + } + } catch (InterruptedException e) { + FancyNpcs.getInstance().getFancyLogger().error("Thread was interrupted while waiting for skin generation."); + FancyNpcs.getInstance().getFancyLogger().debug("QueueResp: " + queueResp.toString()); + FancyNpcs.getInstance().getFancyLogger().debug("JobResp: " + jobResp.toString()); + } catch (Exception e) { + FancyNpcs.getInstance().getFancyLogger().error("Unexpected error in skin generation: " + e.getMessage()); + FancyNpcs.getInstance().getFancyLogger().debug("QueueResp: " + queueResp.toString()); + FancyNpcs.getInstance().getFancyLogger().debug("JobResp: " + jobResp.toString()); + } + + return null; + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/mineskin/MineSkinQueue.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/mineskin/MineSkinQueue.java new file mode 100644 index 00000000..7594f58f --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/mineskin/MineSkinQueue.java @@ -0,0 +1,105 @@ +package de.oliver.fancynpcs.skins.mineskin; + +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.skins.SkinData; +import de.oliver.fancynpcs.api.skins.SkinGeneratedEvent; +import de.oliver.fancynpcs.skins.SkinManagerImpl; +import org.mineskin.data.SkinInfo; +import org.mineskin.data.Variant; +import org.mineskin.request.GenerateRequest; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +public class MineSkinQueue { + private static MineSkinQueue INSTANCE; + + private final MineSkinAPI api; + private final Queue queue; + private ScheduledFuture scheduler; + + private long nextRequestTime = System.currentTimeMillis(); + + private MineSkinQueue() { + this.queue = new LinkedList<>(); + this.api = new MineSkinAPI(SkinManagerImpl.EXECUTOR); + + run(); + } + + public static MineSkinQueue get() { + if (INSTANCE == null) { + INSTANCE = new MineSkinQueue(); + } + + return INSTANCE; + } + + public void run() { + scheduler = SkinManagerImpl.EXECUTOR.scheduleWithFixedDelay(this::poll, 5, 1, TimeUnit.SECONDS); + } + + private void poll() { + if (this.queue.isEmpty()) { + return; + } + + if (System.currentTimeMillis() < this.nextRequestTime) { + FancyNpcs.getInstance().getFancyLogger().debug("Retrying to generate skin in " + (nextRequestTime - System.currentTimeMillis()) + "ms"); + return; + } + + SkinRequest req = this.queue.poll(); + if (req == null) { + return; + } + + try { + FancyNpcs.getInstance().getFancyLogger().debug("Fetching skin from MineSkin: " + req.id()); + SkinInfo skin = this.api.generateSkin(req.request()); + if (skin == null) { + this.nextRequestTime = System.currentTimeMillis(); + return; + } + + SkinData skinData = new SkinData( + req.id(), + skin.variant() == Variant.SLIM ? SkinData.SkinVariant.SLIM : SkinData.SkinVariant.AUTO, + skin.texture().data().value(), + skin.texture().data().signature() + ); + new SkinGeneratedEvent(req.id(), skinData).callEvent(); + } catch (RatelimitException e) { + this.nextRequestTime = e.getNextRequestTime(); + this.queue.add(req); + FancyNpcs.getInstance().getFancyLogger().debug("Failed to generate skin: ratelimited by MineSkin, retrying in " + (nextRequestTime - System.currentTimeMillis()) + "ms"); + return; + } + + this.nextRequestTime = System.currentTimeMillis(); + } + + public void add(SkinRequest req) { + // check if request is already in queue + for (SkinRequest r : this.queue) { + if (r.id().equals(req.id())) { + return; + } + } + + this.queue.add(req); + } + + public void clear() { + this.queue.clear(); + } + + public ScheduledFuture getScheduler() { + return scheduler; + } + + public record SkinRequest(String id, GenerateRequest request) { + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/mineskin/RatelimitException.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/mineskin/RatelimitException.java new file mode 100644 index 00000000..43dc6df2 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/mineskin/RatelimitException.java @@ -0,0 +1,15 @@ +package de.oliver.fancynpcs.skins.mineskin; + +public class RatelimitException extends RuntimeException { + + private final long nextRequestTime; + + public RatelimitException(long nextRequestTime) { + super("Rate limit reached. Next request possible at " + nextRequestTime); + this.nextRequestTime = nextRequestTime; + } + + public long getNextRequestTime() { + return nextRequestTime; + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/mojang/MojangAPI.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/mojang/MojangAPI.java new file mode 100644 index 00000000..4b32b8a4 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/mojang/MojangAPI.java @@ -0,0 +1,72 @@ +package de.oliver.fancynpcs.skins.mojang; + +import com.google.gson.Gson; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.skins.SkinData; +import de.oliver.fancynpcs.skins.mineskin.RatelimitException; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +public class MojangAPI { + + private final HttpClient client; + private final Gson gson = new Gson(); + + public MojangAPI() { + this.client = HttpClient.newBuilder() + .connectTimeout(Duration.of(3, ChronoUnit.SECONDS)) + .build(); + } + + public SkinData fetchSkin(String uuid, SkinData.SkinVariant variant) throws RatelimitException { + FancyNpcsPlugin.get().getFancyLogger().debug("Fetching skin from MojangAPI for " + uuid); + + try { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid + "?unsigned=false")) + .GET() + .build(); + + HttpResponse resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + if (resp.statusCode() != 200) { + FancyNpcsPlugin.get().getFancyLogger().warn("Failed to fetch skin from Mojang API for " + uuid + " (status code: " + resp.statusCode() + ")"); + FancyNpcsPlugin.get().getFancyLogger().debug("Body: " + resp.body()); + return null; + } else if (resp.statusCode() == 429) { + throw new RatelimitException(System.currentTimeMillis() + 1000 * 10); // retry in next run + } + + RequestResponse response = gson.fromJson(resp.body(), RequestResponse.class); + RequestResponseProperty textures = response.getProperty("textures"); + + FancyNpcsPlugin.get().getFancyLogger().debug("Skin fetched from MojangAPI for " + uuid); + return new SkinData(uuid, variant, textures.value(), textures.signature()); + } catch (RatelimitException e) { + throw e; // rethrow + } catch (Exception e) { + FancyNpcsPlugin.get().getFancyLogger().warn("Failed to fetch skin from Mojang API for " + uuid); + FancyNpcsPlugin.get().getFancyLogger().warn(e); + return null; + } + } + + record RequestResponse(String id, String name, RequestResponseProperty[] properties) { + public RequestResponseProperty getProperty(String name) { + for (RequestResponseProperty property : properties) { + if (property.name.equals(name)) { + return property; + } + } + return null; + } + } + + record RequestResponseProperty(String name, String value, String signature) { + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/mojang/MojangQueue.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/mojang/MojangQueue.java new file mode 100644 index 00000000..4b6e5981 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/skins/mojang/MojangQueue.java @@ -0,0 +1,99 @@ +package de.oliver.fancynpcs.skins.mojang; + +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.skins.SkinData; +import de.oliver.fancynpcs.api.skins.SkinGeneratedEvent; +import de.oliver.fancynpcs.skins.SkinManagerImpl; +import de.oliver.fancynpcs.skins.mineskin.RatelimitException; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +public class MojangQueue { + + private static MojangQueue INSTANCE; + + private final Queue queue; + private final MojangAPI api; + private ScheduledFuture scheduler; + private long nextRequestTime = System.currentTimeMillis(); + + private MojangQueue() { + this.queue = new LinkedList<>(); + this.api = new MojangAPI(); + + run(); + } + + public static MojangQueue get() { + if (INSTANCE == null) { + INSTANCE = new MojangQueue(); + } + + return INSTANCE; + } + + public void run() { + scheduler = SkinManagerImpl.EXECUTOR.scheduleWithFixedDelay(this::pollMany, 5, 1, TimeUnit.SECONDS); + } + + private void pollMany() { + for (int i = 0; i < 5; i++) { + poll(); + } + } + + private void poll() { + if (this.queue.isEmpty()) { + return; + } + + if (System.currentTimeMillis() < this.nextRequestTime) { + FancyNpcs.getInstance().getFancyLogger().debug("Retrying to fetch skin from Mojang in " + (nextRequestTime - System.currentTimeMillis()) + "ms"); + return; + } + + SkinRequest req = this.queue.poll(); + if (req == null) { + return; + } + + try { + FancyNpcs.getInstance().getFancyLogger().debug("Fetching skin from Mojang: " + req.uuid()); + SkinData skinData = this.api.fetchSkin(req.uuid(), req.variant()); + new SkinGeneratedEvent(req.uuid(), skinData).callEvent(); + } catch (RatelimitException e) { + this.nextRequestTime = e.getNextRequestTime(); + this.queue.add(req); + FancyNpcs.getInstance().getFancyLogger().debug("Failed to generate skin: ratelimited by Mojang, retrying in " + (nextRequestTime - System.currentTimeMillis()) + "ms"); + return; + } + + this.nextRequestTime = System.currentTimeMillis(); + } + + public void add(SkinRequest req) { + // check if request is already in queue + for (SkinRequest r : this.queue) { + if (r.uuid().equals(req.uuid())) { + return; + } + } + + this.queue.add(req); + } + + public void clear() { + this.queue.clear(); + } + + public ScheduledFuture getScheduler() { + return scheduler; + } + + public record SkinRequest(String uuid, SkinData.SkinVariant variant) { + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/FancyNpcsTests.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/FancyNpcsTests.java new file mode 100644 index 00000000..aa5c0dd6 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/FancyNpcsTests.java @@ -0,0 +1,79 @@ +package de.oliver.fancynpcs.tests; + +import de.oliver.fancynpcs.tests.api.ChatColorHandlerTest; +import de.oliver.fancynpcs.tests.api.CreateNpcTest; +import de.oliver.fancynpcs.tests.api.SkinManagerTest; +import de.oliver.fancynpcs.tests.commands.*; +import de.oliver.plugintests.FPTestClass; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +/** + * FancyNpcsTests is a class responsible for managing and running test classes associated with NPC behavior. + * It maintains a list of test classes and provides methods to add new test classes and run all registered tests. + */ +public class FancyNpcsTests { + + private final List tests = new ArrayList<>(); + + public FancyNpcsTests() { + // api tests + addTest(CreateNpcTest.class); + addTest(ChatColorHandlerTest.class); + addTest(SkinManagerTest.class); + + // command tests + addTest(CreateCMDTest.class); + addTest(TypeCMDTest.class); + addTest(DisplayNameCMDTest.class); + addTest(TurnToPlayerCMDTest.class); + addTest(ActionCMDTest.class); + } + + /** + * Adds a test class to the list of test classes to be run. + * + * @param testClass the test class to be added + * @return this instance, allowing for method chaining + */ + public FancyNpcsTests addTest(Class testClass) { + tests.add(FPTestClass.fromClass(testClass)); + return this; + } + + /** + * Runs all registered test classes using the provided player context. + * + * @param player The player context to pass to the test methods. + * @return true if all tests completed successfully, false if any test failed or an unexpected exception occurred. + */ + public boolean runAllTests(Player player) { + for (FPTestClass test : tests) { + try { + if (!test.runTests(player)) { + return false; + } + } catch (Exception e) { + return false; + } + } + return true; + } + + /** + * Returns the current count of test classes registered to be run. + * + * @return the number of test classes in the list + */ + public int getTestCount() { + int count = 0; + + for (FPTestClass test : tests) { + count += test.testMethods().size(); + } + + return count; + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/api/ChatColorHandlerTest.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/api/ChatColorHandlerTest.java new file mode 100644 index 00000000..214dd8fd --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/api/ChatColorHandlerTest.java @@ -0,0 +1,32 @@ +package de.oliver.fancynpcs.tests.api; + +import de.oliver.plugintests.annotations.FPTest; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.lushplugins.chatcolorhandler.ChatColorHandler; +import org.lushplugins.chatcolorhandler.parsers.ParserTypes; + +import static de.oliver.plugintests.Expectable.expect; + +public class ChatColorHandlerTest { + + @FPTest(name = "Test Placeholders") + public void testPlaceholders(Player player) { + if (!isPlaceholderAPIEnabled()) { + return; + } + + String input = "Player name: %player_name%"; + String got = ChatColorHandler.translate(input, player, ParserTypes.placeholder()); + String expected = "Player name: " + player.getName(); + + expect(got).toEqual(expected); + } + + private boolean isPlaceholderAPIEnabled() { + Plugin placeholderAPI = Bukkit.getPluginManager().getPlugin("PlaceholderAPI"); + return placeholderAPI != null && placeholderAPI.isEnabled(); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/api/CreateNpcTest.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/api/CreateNpcTest.java new file mode 100644 index 00000000..ecc9d181 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/api/CreateNpcTest.java @@ -0,0 +1,67 @@ +package de.oliver.fancynpcs.tests.api; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.NpcManager; +import de.oliver.plugintests.annotations.FPAfterEach; +import de.oliver.plugintests.annotations.FPBeforeEach; +import de.oliver.plugintests.annotations.FPTest; +import org.bukkit.Location; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +import java.util.UUID; + +import static de.oliver.plugintests.Expectable.expect; + +public class CreateNpcTest { + + private static final NpcManager NPC_MANAGER = FancyNpcsPlugin.get().getNpcManager(); + + private String npcName; + private UUID creatorUUID; + private Location location; + + private Npc createdNpc; + + @FPBeforeEach + public void setUp(Player player) { + npcName = "test-" + UUID.randomUUID().toString().substring(0, 8); + creatorUUID = player.getUniqueId(); + location = player.getLocation().clone(); + createdNpc = null; + } + + @FPAfterEach + public void tearDown(Player player) { + if (createdNpc != null) { + NPC_MANAGER.removeNpc(createdNpc); + } + + expect(NPC_MANAGER.getNpc(npcName)).toBeNull(); + + createdNpc = null; + npcName = null; + creatorUUID = null; + location = null; + } + + @FPTest(name = "Create and register npc") + public void createAndRegisterNpc(Player player) { + NpcData data = new NpcData(npcName, creatorUUID, location); + createdNpc = FancyNpcsPlugin.get().getNpcAdapter().apply(data); + expect(createdNpc).toBeDefined(); + + createdNpc.create(); + expect(createdNpc.getEntityId()).toBeGreaterThan(-1); + expect(createdNpc.getData().getName()).toEqual(npcName); + expect(createdNpc.getData().getCreator()).toEqual(creatorUUID); + expect(createdNpc.getData().getLocation()).toEqual(location); + expect(createdNpc.getData().getType()).toEqual(EntityType.PLAYER); + + NPC_MANAGER.registerNpc(createdNpc); + expect(NPC_MANAGER.getNpc(npcName)).toBeDefined(); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/api/NpcTestEnv.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/api/NpcTestEnv.java new file mode 100644 index 00000000..98df6702 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/api/NpcTestEnv.java @@ -0,0 +1,44 @@ +package de.oliver.fancynpcs.tests.api; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcData; +import org.bukkit.Bukkit; +import org.bukkit.Location; + +import java.util.UUID; + +import static de.oliver.plugintests.Expectable.expect; + +public class NpcTestEnv { + + public static Npc givenDefaultNpcIsCreated() { + String name = "test-" + UUID.randomUUID().toString().substring(0, 8); + UUID creator = UUID.randomUUID(); + Location location = new Location(Bukkit.getWorld("world"), 100, 100, -100); + + NpcData data = new NpcData(name, creator, location); + Npc npc = FancyNpcsPlugin.get().getNpcAdapter().apply(data); + expect(npc).toBeDefined(); + + npc.create(); + expect(npc.getEntityId()).toBeGreaterThan(-1); + + return npc; + } + + public static void givenNpcIsRegistered(Npc npc) { + expect(npc).toBeDefined(); + + FancyNpcsPlugin.get().getNpcManager().registerNpc(npc); + expect(FancyNpcsPlugin.get().getNpcManager().getNpc(npc.getData().getName())).toBeDefined(); + } + + public static void givenNpcIsUnregistered(Npc npc) { + expect(npc).toBeDefined(); + + FancyNpcsPlugin.get().getNpcManager().removeNpc(npc); + expect(FancyNpcsPlugin.get().getNpcManager().getNpc(npc.getData().getName())).toBeNull(); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/api/SkinManagerTest.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/api/SkinManagerTest.java new file mode 100644 index 00000000..f7feb9a0 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/api/SkinManagerTest.java @@ -0,0 +1,78 @@ +package de.oliver.fancynpcs.tests.api; + +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.skins.SkinData; +import de.oliver.fancynpcs.skins.SkinManagerImpl; +import de.oliver.fancynpcs.skins.cache.SkinCacheMemory; +import de.oliver.plugintests.annotations.FPBeforeEach; +import de.oliver.plugintests.annotations.FPTest; +import org.bukkit.entity.Player; + +import java.io.File; + +import static de.oliver.plugintests.Expectable.expect; + +public class SkinManagerTest { + + private SkinManagerImpl skinFetcher; + + @FPBeforeEach + public void setUp(Player player) { + + skinFetcher = new SkinManagerImpl(new SkinCacheMemory(), new SkinCacheMemory()); + } + + @FPTest(name = "Test fetch skin by UUID") + public void testSkinByUUID(Player player) { + SkinData skin = skinFetcher.getByUUID(player.getUniqueId(), SkinData.SkinVariant.AUTO); + + expect(skin).toBeDefined(); + expect(skin.getIdentifier()).toEqual(player.getUniqueId().toString()); + expect(skin.getVariant()).toEqual(SkinData.SkinVariant.AUTO); + expect(skin.getTextureValue()).toBeDefined(); + expect(skin.getTextureValue().length()).toBeGreaterThan(0); + expect(skin.getTextureSignature()).toBeDefined(); + expect(skin.getTextureSignature().length()).toBeGreaterThan(0); + } + + @FPTest(name = "Test fetch skin by username") + public void testSkinByUsername(Player player) { + SkinData skin = skinFetcher.getByUsername(player.getName(), SkinData.SkinVariant.AUTO); + + expect(skin).toBeDefined(); + expect(skin.getIdentifier()).toEqual(player.getUniqueId().toString()); + expect(skin.getVariant()).toEqual(SkinData.SkinVariant.AUTO); + expect(skin.getTextureValue()).toBeDefined(); + expect(skin.getTextureValue().length()).toBeGreaterThan(0); + expect(skin.getTextureSignature()).toBeDefined(); + } + + @FPTest(name = "Test fetch skin by URL") + public void testSkinByURL(Player player) { + SkinData skin = skinFetcher.getByURL("https://s.namemc.com/i/de7d8a3ffd1f584c.png", SkinData.SkinVariant.AUTO); + + expect(skin).toBeDefined(); + expect(skin.getIdentifier()).toEqual("https://s.namemc.com/i/de7d8a3ffd1f584c.png"); + expect(skin.getVariant()).toEqual(SkinData.SkinVariant.AUTO); + expect(skin.getTextureValue()).toBeDefined(); + expect(skin.getTextureValue().length()).toBeGreaterThan(0); + expect(skin.getTextureSignature()).toBeDefined(); + expect(skin.getTextureSignature().length()).toBeGreaterThan(0); + } + + @FPTest(name = "Test fetch skin by file") + public void testSkinByFile(Player player) { + FancyNpcs.getInstance().getPlugin().saveResource("skins/testskin.png", true); + SkinData skin = skinFetcher.getByFile("testskin.png", SkinData.SkinVariant.AUTO); + + expect(skin).toBeDefined(); + expect(skin.getIdentifier()).toEqual("testskin.png"); + expect(skin.getVariant()).toEqual(SkinData.SkinVariant.AUTO); + expect(skin.getTextureValue()).toBeDefined(); + expect(skin.getTextureValue().length()).toBeGreaterThan(0); + expect(skin.getTextureSignature()).toBeDefined(); + expect(skin.getTextureSignature().length()).toBeGreaterThan(0); + + new File("plugins/FancyNpcs/skins/testskin.png").delete(); + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/commands/ActionCMDTest.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/commands/ActionCMDTest.java new file mode 100644 index 00000000..7c8eef6d --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/commands/ActionCMDTest.java @@ -0,0 +1,161 @@ +package de.oliver.fancynpcs.tests.commands; + +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.actions.ActionManager; +import de.oliver.fancynpcs.api.actions.ActionTrigger; +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.tests.api.NpcTestEnv; +import de.oliver.plugintests.annotations.FPAfterEach; +import de.oliver.plugintests.annotations.FPBeforeEach; +import de.oliver.plugintests.annotations.FPTest; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +import static de.oliver.plugintests.Expectable.expect; + +public class ActionCMDTest { + + private final ActionManager actionManager = FancyNpcs.getInstance().getActionManager(); + + private Npc npc; + private String npcName; + + @FPBeforeEach + public void setUp(Player player) { + npc = NpcTestEnv.givenDefaultNpcIsCreated(); + npcName = npc.getData().getName(); + + NpcTestEnv.givenNpcIsRegistered(npc); + } + + @FPAfterEach + public void tearDown(Player player) { + NpcTestEnv.givenNpcIsUnregistered(npc); + + npc = null; + npcName = null; + } + + @FPTest(name = "Add action") + public void addAction(Player player) { + ActionTrigger actionTrigger = ActionTrigger.RIGHT_CLICK; + String actionType = "player_command"; + String actionValue = "say Hello World!"; + + expect(player.performCommand("npc action " + npcName + " " + actionTrigger + " add " + actionType + " " + actionValue)).toBe(true); + + expect(npc.getData().getActions(actionTrigger).size()).toEqual(1); + expect(npc.getData().getActions(actionTrigger).getFirst().action().getName()).toEqual(actionType); + expect(npc.getData().getActions(actionTrigger).getFirst().value()).toEqual(actionValue); + } + + @FPTest(name = "Add action before") + public void addActionBefore(Player player) { + ActionTrigger actionTrigger = ActionTrigger.RIGHT_CLICK; + NpcAction.NpcActionData existingAction = new NpcAction.NpcActionData(1, actionManager.getActionByName("player_command"), "say Hello World!"); + npc.getData().setActions(actionTrigger, new ArrayList<>(List.of(existingAction))); + + String actionType = "player_command"; + String actionValue = "say Hello World 2!"; + + expect(player.performCommand("npc action " + npcName + " " + actionTrigger + " add_before 1 " + actionType + " " + actionValue)).toBe(true); + + expect(npc.getData().getActions(actionTrigger).size()).toEqual(2); + expect(npc.getData().getActions(actionTrigger).getFirst().action().getName()).toEqual(actionType); + expect(npc.getData().getActions(actionTrigger).getFirst().value()).toEqual(actionValue); + expect(npc.getData().getActions(actionTrigger).getLast().action().getName()).toEqual(existingAction.action().getName()); + expect(npc.getData().getActions(actionTrigger).getLast().value()).toEqual(existingAction.value()); + } + + @FPTest(name = "Add action after") + public void addActionAfter(Player player) { + ActionTrigger actionTrigger = ActionTrigger.RIGHT_CLICK; + NpcAction.NpcActionData existingAction = new NpcAction.NpcActionData(1, actionManager.getActionByName("player_command"), "say Hello World!"); + npc.getData().setActions(actionTrigger, new ArrayList<>(List.of(existingAction))); + + String actionType = "player_command"; + String actionValue = "say Hello World 2!"; + + expect(player.performCommand("npc action " + npcName + " " + actionTrigger + " add_after 1 " + actionType + " " + actionValue)).toBe(true); + + expect(npc.getData().getActions(actionTrigger).size()).toEqual(2); + expect(npc.getData().getActions(actionTrigger).getFirst().action().getName()).toEqual(existingAction.action().getName()); + expect(npc.getData().getActions(actionTrigger).getFirst().value()).toEqual(existingAction.value()); + expect(npc.getData().getActions(actionTrigger).getLast().action().getName()).toEqual(actionType); + expect(npc.getData().getActions(actionTrigger).getLast().value()).toEqual(actionValue); + } + + @FPTest(name = "Set action") + public void setAction(Player player) { + ActionTrigger actionTrigger = ActionTrigger.RIGHT_CLICK; + NpcAction.NpcActionData existingAction = new NpcAction.NpcActionData(1, actionManager.getActionByName("player_command"), "say Hello World!"); + npc.getData().setActions(actionTrigger, new ArrayList<>(List.of(existingAction))); + + String actionType = "player_command"; + String actionValue = "say Hello World 2!"; + + expect(player.performCommand("npc action " + npcName + " " + actionTrigger + " set 1 " + actionType + " " + actionValue)).toBe(true); + + expect(npc.getData().getActions(actionTrigger).size()).toEqual(1); + expect(npc.getData().getActions(actionTrigger).getFirst().action().getName()).toEqual(actionType); + expect(npc.getData().getActions(actionTrigger).getFirst().value()).toEqual(actionValue); + } + + @FPTest(name = "Remove action") + public void removeAction(Player player) { + ActionTrigger actionTrigger = ActionTrigger.RIGHT_CLICK; + NpcAction.NpcActionData existingAction = new NpcAction.NpcActionData(1, actionManager.getActionByName("player_command"), "say Hello World!"); + npc.getData().setActions(actionTrigger, new ArrayList<>(List.of(existingAction))); + + expect(player.performCommand("npc action " + npcName + " " + actionTrigger + " remove 1")).toBe(true); + + expect(npc.getData().getActions(actionTrigger).size()).toEqual(0); + } + + @FPTest(name = "Move action up") + public void moveActionUp(Player player) { + ActionTrigger actionTrigger = ActionTrigger.RIGHT_CLICK; + NpcAction.NpcActionData existingAction1 = new NpcAction.NpcActionData(1, actionManager.getActionByName("player_command"), "say Hello World!"); + NpcAction.NpcActionData existingAction2 = new NpcAction.NpcActionData(2, actionManager.getActionByName("player_command"), "say Hello World 2!"); + npc.getData().setActions(actionTrigger, new ArrayList<>(List.of(existingAction1, existingAction2))); + + expect(player.performCommand("npc action " + npcName + " " + actionTrigger + " move_up 2")).toBe(true); + + expect(npc.getData().getActions(actionTrigger).size()).toEqual(2); + expect(npc.getData().getActions(actionTrigger).getFirst().action().getName()).toEqual(existingAction2.action().getName()); + expect(npc.getData().getActions(actionTrigger).getFirst().value()).toEqual(existingAction2.value()); + expect(npc.getData().getActions(actionTrigger).getLast().action().getName()).toEqual(existingAction1.action().getName()); + expect(npc.getData().getActions(actionTrigger).getLast().value()).toEqual(existingAction1.value()); + } + + @FPTest(name = "Move action down") + public void moveActionDown(Player player) { + ActionTrigger actionTrigger = ActionTrigger.RIGHT_CLICK; + NpcAction.NpcActionData existingAction1 = new NpcAction.NpcActionData(1, actionManager.getActionByName("player_command"), "say Hello World!"); + NpcAction.NpcActionData existingAction2 = new NpcAction.NpcActionData(2, actionManager.getActionByName("player_command"), "say Hello World 2!"); + npc.getData().setActions(actionTrigger, new ArrayList<>(List.of(existingAction1, existingAction2))); + + expect(player.performCommand("npc action " + npcName + " " + actionTrigger + " move_down 1")).toBe(true); + + expect(npc.getData().getActions(actionTrigger).size()).toEqual(2); + expect(npc.getData().getActions(actionTrigger).getFirst().action().getName()).toEqual(existingAction2.action().getName()); + expect(npc.getData().getActions(actionTrigger).getFirst().value()).toEqual(existingAction2.value()); + expect(npc.getData().getActions(actionTrigger).getLast().action().getName()).toEqual(existingAction1.action().getName()); + expect(npc.getData().getActions(actionTrigger).getLast().value()).toEqual(existingAction1.value()); + } + + @FPTest(name = "Clear actions") + public void clearActions(Player player) { + ActionTrigger actionTrigger = ActionTrigger.RIGHT_CLICK; + NpcAction.NpcActionData existingAction = new NpcAction.NpcActionData(1, actionManager.getActionByName("player_command"), "say Hello World!"); + npc.getData().setActions(actionTrigger, new ArrayList<>(List.of(existingAction))); + + expect(player.performCommand("npc action " + npcName + " " + actionTrigger + " clear")).toBe(true); + + expect(npc.getData().getActions(actionTrigger).size()).toEqual(0); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/commands/CreateCMDTest.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/commands/CreateCMDTest.java new file mode 100644 index 00000000..2079b330 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/commands/CreateCMDTest.java @@ -0,0 +1,145 @@ +package de.oliver.fancynpcs.tests.commands; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcManager; +import de.oliver.plugintests.annotations.FPAfterEach; +import de.oliver.plugintests.annotations.FPBeforeEach; +import de.oliver.plugintests.annotations.FPTest; +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +import java.util.UUID; + +import static de.oliver.plugintests.Expectable.expect; + +public class CreateCMDTest { + + private static final NpcManager NPC_MANAGER = FancyNpcsPlugin.get().getNpcManager(); + + private String npcName; + private Npc createdNpc; + + @FPBeforeEach + public void setUp(Player player) { + npcName = "test-" + UUID.randomUUID().toString().substring(0, 8); + createdNpc = null; + } + + @FPAfterEach + public void tearDown(Player player) { + if (createdNpc != null) { + NPC_MANAGER.removeNpc(createdNpc); + } + + expect(NPC_MANAGER.getNpc(npcName)).toBeNull(); + + createdNpc = null; + npcName = null; + } + + @FPTest(name = "Create npc") + public void createNpc(Player player) { + expect(player.performCommand("npc create " + npcName)).toBe(true); + + createdNpc = NPC_MANAGER.getNpc(npcName); + expect(createdNpc).toBeDefined(); + + expect(createdNpc.getEntityId()).toBeGreaterThan(-1); + + expect(createdNpc.getData().getName()).toEqual(npcName); + expect(createdNpc.getData().getType()).toEqual(EntityType.PLAYER); + expect(createdNpc.getData().getLocation()).toBeDefined(); + expect(createdNpc.getData().getLocation().getWorld().getName()).toEqual(player.getWorld().getName()); + expect(createdNpc.getData().getCreator()).toEqual(player.getUniqueId()); + } + + @FPTest(name = "Create npc with type") + public void createNpcWithType(Player player) { + expect(player.performCommand("npc create " + npcName + " --type PIG")).toBe(true); + + createdNpc = NPC_MANAGER.getNpc(npcName); + expect(createdNpc).toBeDefined(); + + expect(createdNpc.getEntityId()).toBeGreaterThan(-1); + + expect(createdNpc.getData().getType()).toEqual(EntityType.PIG); + } + + @FPTest(name = "Create npc with location") + public void createNpcWithLocation(Player player) { + expect(player.performCommand("npc create " + npcName + " --location 12 154 842")).toBe(true); + + createdNpc = NPC_MANAGER.getNpc(npcName); + expect(createdNpc).toBeDefined(); + + expect(createdNpc.getEntityId()).toBeGreaterThan(-1); + + expect(createdNpc.getData().getLocation().x()).toEqual(12d); + expect(createdNpc.getData().getLocation().y()).toEqual(154d); + expect(createdNpc.getData().getLocation().z()).toEqual(842d); + } + + @FPTest(name = "Create npc with world") + public void createNpcWithWorld(Player player) { + String worldName = "world_the_nether"; + if (Bukkit.getWorld(worldName) == null) { + worldName = "world"; + } + + expect(player.performCommand("npc create " + npcName + " --world " + worldName)).toBe(true); + + createdNpc = NPC_MANAGER.getNpc(npcName); + expect(createdNpc).toBeDefined(); + + expect(createdNpc.getEntityId()).toBeGreaterThan(-1); + + expect(createdNpc.getData().getLocation().getWorld().getName()).toEqual(worldName); + } + + @FPTest(name = "Create npc with invalid name") + public void createNpcWithInvalidName(Player player) { + expect(player.performCommand("npc create " + "invalid.name")).toBe(true); + + createdNpc = NPC_MANAGER.getNpc("invalid.name"); + expect(createdNpc).toBeNull(); + } + + @FPTest(name = "Create npc with existing name") + public void createNpcWithExistingName(Player player) { + expect(player.performCommand("npc create " + npcName)).toBe(true); + + createdNpc = NPC_MANAGER.getNpc(npcName); + expect(createdNpc).toBeDefined(); + + expect(player.performCommand("npc create " + npcName)).toBe(true); + + Npc existingNpc = NPC_MANAGER.getNpc(npcName); + expect(existingNpc).toBeDefined(); + + expect(existingNpc.getEntityId()).toBeGreaterThan(-1); + expect(existingNpc).toEqual(createdNpc); + } + + @FPTest(name = "Create npc with all flags") + public void createNpcWithAllFlags(Player player) { + String worldName = "world_the_nether"; + if (Bukkit.getWorld(worldName) == null) { + worldName = "world"; + } + + expect(player.performCommand("npc create " + npcName + " --type COW --location 137 131 -571 --world " + worldName)).toBe(true); + + createdNpc = NPC_MANAGER.getNpc(npcName); + expect(createdNpc).toBeDefined(); + + expect(createdNpc.getEntityId()).toBeGreaterThan(-1); + + expect(createdNpc.getData().getType()).toEqual(EntityType.COW); + expect(createdNpc.getData().getLocation().x()).toEqual(137d); + expect(createdNpc.getData().getLocation().y()).toEqual(131d); + expect(createdNpc.getData().getLocation().z()).toEqual(-571d); + expect(createdNpc.getData().getLocation().getWorld().getName()).toEqual(worldName); + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/commands/DisplayNameCMDTest.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/commands/DisplayNameCMDTest.java new file mode 100644 index 00000000..c2a4a8e6 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/commands/DisplayNameCMDTest.java @@ -0,0 +1,68 @@ +package de.oliver.fancynpcs.tests.commands; + +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.tests.api.NpcTestEnv; +import de.oliver.plugintests.annotations.FPAfterEach; +import de.oliver.plugintests.annotations.FPBeforeEach; +import de.oliver.plugintests.annotations.FPTest; +import org.bukkit.entity.Player; + +import java.util.List; + +import static de.oliver.plugintests.Expectable.expect; + +public class DisplayNameCMDTest { + + private Npc npc; + private String npcName; + + @FPBeforeEach + public void setUp(Player player) { + npc = NpcTestEnv.givenDefaultNpcIsCreated(); + npcName = npc.getData().getName(); + + NpcTestEnv.givenNpcIsRegistered(npc); + } + + @FPAfterEach + public void tearDown(Player player) { + NpcTestEnv.givenNpcIsUnregistered(npc); + + npc = null; + npcName = null; + } + + @FPTest(name = "Set display name") + public void setDisplayName(Player player) { + String displayName = "Test Display Name"; + expect(player.performCommand("npc displayname " + npcName + " " + displayName)).toBe(true); + expect(npc.getData().getDisplayName()).toEqual(displayName); + } + + @FPTest(name = "Set display name to none") + public void setDisplayNameToNone(Player player) { + expect(player.performCommand("npc displayname " + npcName + " @none")).toBe(true); + expect(npc.getData().getDisplayName()).toEqual(""); + } + + @FPTest(name = "Set display name to empty") + public void setDisplayNameToEmpty(Player player) { + expect(player.performCommand("npc displayname " + npcName + " ")).toBe(true); + expect(npc.getData().getDisplayName()).toEqual(""); + } + + @FPTest(name = "Set display name with blocked command") + public void setDisplayNameWithBlockedCommand(Player player) { + List blockedCommands = FancyNpcs.getInstance().getFancyNpcConfig().getBlockedCommands(); + if (blockedCommands.isEmpty()) { + return; + } + String blockedCommand = blockedCommands.get(0); + + expect(player.performCommand("npc displayname " + npcName + " hello")).toBe(true); + + expect(npc.getData().getDisplayName()).toEqual(npcName); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/commands/TurnToPlayerCMDTest.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/commands/TurnToPlayerCMDTest.java new file mode 100644 index 00000000..b2cfff54 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/commands/TurnToPlayerCMDTest.java @@ -0,0 +1,47 @@ +package de.oliver.fancynpcs.tests.commands; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.tests.api.NpcTestEnv; +import de.oliver.plugintests.annotations.FPAfterEach; +import de.oliver.plugintests.annotations.FPBeforeEach; +import de.oliver.plugintests.annotations.FPTest; +import org.bukkit.entity.Player; + +import static de.oliver.plugintests.Expectable.expect; + +public class TurnToPlayerCMDTest { + + private Npc npc; + private String npcName; + + @FPBeforeEach + public void setUp(Player player) { + npc = NpcTestEnv.givenDefaultNpcIsCreated(); + npcName = npc.getData().getName(); + + NpcTestEnv.givenNpcIsRegistered(npc); + } + + @FPAfterEach + public void tearDown(Player player) { + NpcTestEnv.givenNpcIsUnregistered(npc); + + npc = null; + npcName = null; + } + + @FPTest(name = "Set turnToPlayer to true") + public void setTurnToPlayerToTrue(Player player) { + expect(player.performCommand("npc turn_to_player " + npcName + " true")).toBe(true); + expect(npc.getData().isTurnToPlayer()).toBe(true); + } + + @FPTest(name = "Set turnToPlayer to false") + public void setTurnToPlayerToFalse(Player player) { + npc.getData().setTurnToPlayer(true); + + expect(player.performCommand("npc turn_to_player " + npcName + " false")).toBe(true); + expect(npc.getData().isTurnToPlayer()).toBe(false); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/commands/TypeCMDTest.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/commands/TypeCMDTest.java new file mode 100644 index 00000000..a66c80f7 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tests/commands/TypeCMDTest.java @@ -0,0 +1,48 @@ +package de.oliver.fancynpcs.tests.commands; + +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.tests.api.NpcTestEnv; +import de.oliver.plugintests.annotations.FPAfterEach; +import de.oliver.plugintests.annotations.FPBeforeEach; +import de.oliver.plugintests.annotations.FPTest; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +import static de.oliver.plugintests.Expectable.expect; + +public class TypeCMDTest { + + private Npc npc; + private String npcName; + + @FPBeforeEach + public void setUp(Player player) { + npc = NpcTestEnv.givenDefaultNpcIsCreated(); + npcName = npc.getData().getName(); + + NpcTestEnv.givenNpcIsRegistered(npc); + } + + @FPAfterEach + public void tearDown(Player player) { + NpcTestEnv.givenNpcIsUnregistered(npc); + + npc = null; + npcName = null; + } + + @FPTest(name = "Set type to COW") + public void setTypeToCow(Player player) { + expect(player.performCommand("npc type " + npcName + " COW")).toBe(true); + expect(npc.getData().getType()).toBe(EntityType.COW); + } + + @FPTest(name = "Set type to COW with showInTab") + public void setTypeToCowWithShowInTab(Player player) { + npc.getData().setShowInTab(true); + expect(player.performCommand("npc type " + npcName + " COW")).toBe(true); + + expect(npc.getData().getType()).toBe(EntityType.COW); + expect(npc.getData().isShowInTab()).toBe(false); + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tracker/TurnToPlayerTracker.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tracker/TurnToPlayerTracker.java new file mode 100644 index 00000000..e2bc1479 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tracker/TurnToPlayerTracker.java @@ -0,0 +1,65 @@ +package de.oliver.fancynpcs.tracker; + +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.events.NpcStartLookingEvent; +import de.oliver.fancynpcs.api.events.NpcStopLookingEvent; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.Collection; + +public class TurnToPlayerTracker implements Runnable { + + @Override + public void run() { + Collection npcs = FancyNpcs.getInstance().getNpcManagerImpl().getAllNpcs(); + int turnToPlayerDistance = FancyNpcs.getInstance().getFancyNpcConfig().getTurnToPlayerDistance(); + + for (Player player : Bukkit.getOnlinePlayers()) { + Location playerLocation = player.getLocation(); + + for (Npc npc : npcs) { + NpcData npcData = npc.getData(); + Location npcLocation = npcData.getLocation(); + + if (npcLocation == null || !npcLocation.getWorld().getName().equalsIgnoreCase(playerLocation.getWorld().getName())) { + continue; + } + + double distance = playerLocation.distance(npcLocation); + if (Double.isNaN(distance)) { + continue; + } + + if (npcData.isTurnToPlayer() && distance < turnToPlayerDistance) { + Location newLoc = playerLocation.clone(); + newLoc.setDirection(newLoc.subtract(npcLocation).toVector()); + npc.lookAt(player, newLoc); + // Setting NPC to be looking at the player and getting the value previously stored (or not) inside a map. + Boolean wasPreviouslyLooking = npc.getIsLookingAtPlayer().put(player.getUniqueId(), true); + // Comparing the previous state with current state to prevent event from being called continuously. + if (wasPreviouslyLooking == null || !wasPreviouslyLooking) { + // Calling NpcStartLookingEvent from the main thread. + FancyNpcs.getInstance().getScheduler().runTask(null, () -> { + Bukkit.getPluginManager().callEvent(new NpcStartLookingEvent(npc, player)); + }); + } + // Updating state if changed. + } else if (npcData.isTurnToPlayer() && npc.getIsLookingAtPlayer().getOrDefault(player.getUniqueId(), false)) { + npc.getIsLookingAtPlayer().put(player.getUniqueId(), false); + // Resetting to initial direction, if configured. + if (FancyNpcs.getInstance().getFancyNpcConfig().isTurnToPlayerResetToInitialDirection()) { + npc.move(player, false); + } + // Calling NpcStopLookingEvent from the main thread. + FancyNpcs.getInstance().getScheduler().runTask(null, () -> { + Bukkit.getPluginManager().callEvent(new NpcStopLookingEvent(npc, player)); + }); + } + } + } + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tracker/VisibilityTracker.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tracker/VisibilityTracker.java new file mode 100644 index 00000000..36673346 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/tracker/VisibilityTracker.java @@ -0,0 +1,40 @@ +package de.oliver.fancynpcs.tracker; + +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.Npc; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +public class VisibilityTracker implements Runnable { + + private Set joinDelayPlayers; + + public VisibilityTracker() { + this.joinDelayPlayers = new HashSet<>(); + } + + @Override + public void run() { + for (Player player : Bukkit.getOnlinePlayers()) { + if (joinDelayPlayers.contains(player.getUniqueId())) { + continue; + } + + for (Npc npc : FancyNpcs.getInstance().getNpcManagerImpl().getAllNpcs()) { + npc.checkAndUpdateVisibility(player); + } + } + } + + public void addJoinDelayPlayer(UUID player) { + joinDelayPlayers.add(player); + } + + public void removeJoinDelayPlayer(UUID player) { + joinDelayPlayers.remove(player); + } +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/utils/GlowingColor.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/utils/GlowingColor.java new file mode 100644 index 00000000..d5e1e564 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/utils/GlowingColor.java @@ -0,0 +1,52 @@ +package de.oliver.fancynpcs.utils; + +import net.kyori.adventure.text.format.NamedTextColor; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +// Used 'info' and 'glowing' sub-commands. +public enum GlowingColor { + DISABLED(null, ""), + BLACK(NamedTextColor.BLACK, "color_black"), + DARK_BLUE(NamedTextColor.DARK_BLUE, "color_dark_blue"), + DARK_GREEN(NamedTextColor.DARK_GREEN, "color_dark_green"), + DARK_AQUA(NamedTextColor.DARK_AQUA, "color_dark_aqua"), + DARK_RED(NamedTextColor.DARK_RED, "color_dark_red"), + DARK_PURPLE(NamedTextColor.DARK_PURPLE, "color_dark_purple"), + GOLD(NamedTextColor.GOLD, "color_gold"), + GRAY(NamedTextColor.GRAY, "color_gray"), + DARK_GRAY(NamedTextColor.DARK_GRAY, "color_dark_gray"), + BLUE(NamedTextColor.BLUE, "color_blue"), + GREEN(NamedTextColor.GREEN, "color_green"), + AQUA(NamedTextColor.AQUA, "color_aqua"), + RED(NamedTextColor.RED, "color_red"), + LIGHT_PURPLE(NamedTextColor.LIGHT_PURPLE, "color_light_purple"), + YELLOW(NamedTextColor.YELLOW, "color_yellow"), + WHITE(NamedTextColor.WHITE, "color_white"); + + private final @Nullable NamedTextColor color; + private final @NotNull String translationKey; + + GlowingColor(final @Nullable NamedTextColor color, final @NotNull String translationKey) { + this.color = color; + this.translationKey = translationKey; + } + + public @Nullable NamedTextColor getColor() { + return color; + } + + public @NotNull String getTranslationKey() { + return translationKey; + } + + public static @NotNull GlowingColor fromAdventure(final @NotNull NamedTextColor color) { + for (final GlowingColor glowingColor : GlowingColor.values()) + if (glowingColor.color != null && glowingColor.color.value() == color.value()) + return glowingColor; + // Throwing exception if specified color is not mapped. + throw new IllegalArgumentException("UNSUPPORTED COLOR"); + } + +} diff --git a/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/utils/OldSkinCacheMigrator.java b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/utils/OldSkinCacheMigrator.java new file mode 100644 index 00000000..35d9eef8 --- /dev/null +++ b/plugins/fancynpcs/src/main/java/de/oliver/fancynpcs/utils/OldSkinCacheMigrator.java @@ -0,0 +1,42 @@ +package de.oliver.fancynpcs.utils; + +import de.oliver.fancyanalytics.api.events.Event; +import de.oliver.fancynpcs.FancyNpcs; +import de.oliver.fancynpcs.api.skins.SkinData; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; + +public class OldSkinCacheMigrator { + + private static final File OLD_SKIN_CACHE = new File("plugins/FancyNPCs/.skinCache.yml"); + + public static void migrate() { + if (!OLD_SKIN_CACHE.exists()) { + return; + } + + YamlConfiguration config = YamlConfiguration.loadConfiguration(OLD_SKIN_CACHE); + ConfigurationSection skinsSection = config.getConfigurationSection("skins"); + if (skinsSection == null) { + return; + } + + int amount = 0; + for (String key : skinsSection.getKeys(false)) { + String id = skinsSection.getString(key + ".identifier"); + String value = skinsSection.getString(key + ".value"); + String signature = skinsSection.getString(key + ".signature"); + SkinData skinData = new SkinData(id, SkinData.SkinVariant.AUTO, value, signature); + FancyNpcs.getInstance().getSkinManagerImpl().cacheSkin(skinData); + amount++; + } + + OLD_SKIN_CACHE.delete(); + + FancyNpcs.getInstance().getLogger().info("Migrated " + amount + " skins from old cache."); + FancyNpcs.getInstance().getFancyAnalytics().sendEvent(new Event("SkinCacheMigrated").withProperty("amount", String.valueOf(amount))); + } + +} diff --git a/plugins/fancynpcs/src/main/resources/languages/default.yml b/plugins/fancynpcs/src/main/resources/languages/default.yml new file mode 100644 index 00000000..21a7e738 --- /dev/null +++ b/plugins/fancynpcs/src/main/resources/languages/default.yml @@ -0,0 +1,327 @@ +# ======================================================================== +# THIS FILE IS A TEMPLATE, ANY MODIFICATIONS MADE HERE MAY NOT BE APPLIED. +# ======================================================================== +# HOW TO CREATE CUSTOM LANGUAGE: +# 1. Copy this file and name it eg. "en.yml" +# 2. Change "language_name" property to eg. "english" and update "language" property inside config.yml to match this value. +# 3. Modify contents to your liking. +# 4. Reload the plugin using "/fancynpcs reload" command. +# ======================================================================== + +# Language name. This value can be used to specify "language" property in the config.yml file. +language_name: default + +# Messages support MiniMessage formatting: https://docs.advntr.dev/minimessage/format.html +messages: + # Common (States) + true: "{successColor}True" + enabled: "{successColor}Enabled" + false: "{errorColor}False" + disabled: "{errorColor}Disabled" + + # Common (Equipment Slots) + main_hand: "Main Hand" + off_hand: "Off Hand" + head: "Head" + chest: "Chest" + legs: "Legs" + feet: "Feet" + + # Common (Availability) + unknown: "Unknown" + not_available: "N/A" + + # Common (Visibility) + always_visible: "Always Visible" + not_visible: "Not Visible" + + # Common (Default Values) + default: "Default ({value})" + + # Common (Colors) + color_black: "Black" + color_dark_blue: "Dark Blue" + color_dark_green: "Dark Green" + color_dark_aqua: "Dark Aqua" + color_dark_red: "Dark Red" + color_dark_purple: "Dark Purple" + color_gold: "Gold" + color_gray: "Gray" + color_dark_gray: "Dark Gray" + color_blue: "Blue" + color_green: "Green" + color_aqua: "Aqua" + color_red: "Red" + color_light_purple: "Light Purple" + color_yellow: "Yellow" + color_white: "White" + + # Common (Other) + interaction_on_cooldown: "› {errorColor}You're currently on cooldown. {warningColor}{time}{errorColor} remaining." + action_missing_permissions: "› {errorColor}Insufficient permissions. You cannot perform this action." + player_npcs_cannot_modify_npc: "› {errorColor}You can only modify NPCs you own." + player_npcs_cannot_move_npc: "› {errorColor}You are only allowed to teleport NPCs to your plot." + player_npcs_create_failure_limit_reached: "› {errorColor}You have reached maximum number of NPCs." + player_npcs_create_failure_not_owned_plot: "› {errorColor}You can only create NPCs on your plot." + + # Commands (Common Replies) + command_missing_permissions: "› {errorColor}Insufficient permissions. You cannot use this command." + command_wrong_usage: "› {errorColor}This sub-command does not exist. Use {warningColor}/npc help{errorColor} to view available commands." + command_incomplete_usage: "› {errorColor}Incomplete command. Use {warningColor}/npc help{errorColor} to view correct syntax." + command_player_only: "› {errorColor}This command can only be executed by in-game players." + command_invalid_boolean: "› {errorColor}Argument {warningColor}{input}{errorColor} must be either {warningColor}true{errorColor} or {warningColor}false{errorColor}." + command_invalid_number: "› {errorColor}Argument {warningColor}{input}{errorColor} is not a valid number." + command_invalid_location: "› {errorColor}Argument {warningColor}{input}{errorColor} is not a valid location." + command_invalid_world: "› {errorColor}World named {warningColor}{input}{errorColor} does not exist or is not loaded." + command_invalid_glowing_color: "› {errorColor}Argument named {warningColor}{input}{errorColor} is not a valid glowing color." + command_invalid_list_sort_type: "› {errorColor}Argument named {warningColor}{input}{errorColor} is not a valid sort type." + command_invalid_nearby_sort_type: "› {errorColor}Argument named {warningColor}{input}{errorColor} is not a valid sort type." + command_invalid_entity_type: "› {errorColor}Argument named {warningColor}{input}{errorColor} is not a valid entity type." + command_invalid_action_trigger: "› {errorColor}Argument named {warningColor}{input}{errorColor} is not a valid action trigger." + command_invalid_action_type: "› {errorColor}Argument named {warningColor}{input}{errorColor} is not a valid action type." + command_invalid_npc: "› {errorColor}NPC {warningColor}{input}{errorColor} does not exist." + command_invalid_material: "› {errorColor}Argument {warningColor}{input}{errorColor} is not a valid material." + command_invalid_attribute: "› {errorColor}Specified value {warningColor}{input}{errorColor} is not a valid attribute." + command_invalid_attribute_value: "› {errorColor}Specified value {warningColor}{input}{errorColor} is not valid for this attribute." + command_invalid_equipment_slot: "› {errorColor}Argument {warningColor}{input}{errorColor} is not a valid slot." + command_invalid_interval: "› {errorColor}Argument {warningColor}{input}{errorColor} is not a valid duration of time." + command_invalid_enum_generic: "› {errorColor}Argument {warningColor}{input}{errorColor} is not a valid {enum}." + command_unsupported_npc_type: "› {errorColor}This NPC type does not support this feature." + command_input_contains_blocked_command: "› {errorColor}This command is not allowed for use in interactions." + command_npc_modification_cancelled: "› {errorColor}NPC modification has been cancelled by the API." + + # Generic Syntax Message (syntax is provided by Cloud and may not be human-friendly) + command_invalid_syntax_generic: "Syntax: {warningColor}/{syntax}" + + # Command Syntaxes + command_syntax: + fancynpcs: "Syntax: {primaryColor}/fancynpcs {secondaryColor}(version | reload | save | feature_flags)" + npc: "› {errorColor}Unknown command. Use {warningColor}/npc help{errorColor} to view available commands." + npc_action: "Syntax: {primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}(add | add_after | add_before | clear | list | move_down | move_up | remove | set)" + npc_action_add: "Syntax: {primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}add {secondaryColor}(type) [value]" + npc_action_add_after: "Syntax: {primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}add_after {secondaryColor}(index) (type) [value]" + npc_action_add_before: "Syntax: {primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}add_before {secondaryColor}(index) (type) [value]" + npc_action_remove: "Syntax: {primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}remove {secondaryColor}(number)" + npc_action_move_down: "Syntax: {primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}move_down {secondaryColor}(index)" + npc_action_move_up: "Syntax: {primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}move_down {secondaryColor}(index)" + npc_action_set: "Syntax: {primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}set {secondaryColor}(index) (type) [value]" + npc_attribute: "Syntax: {primaryColor}/npc attribute {secondaryColor}(npc) {primaryColor}(set | list)" + npc_attribute_set: "Syntax: {primaryColor}/npc attribute {secondaryColor}(npc) {primaryColor}set {secondaryColor}(attribute) (value)" + npc_collidable: "Syntax: {primaryColor}/npc collidable {secondaryColor}(npc) (state)" + npc_copy: "Syntax: {primaryColor}/npc copy {secondaryColor}(npc) (new_name)" + npc_create: "Syntax: {primaryColor}/npc create {secondaryColor}(npc) [--type] [--position] [--world]" + npc_displayname: "Syntax: {primaryColor}/npc displayname {secondaryColor}(npc) (name)" + npc_equipment: "Syntax: {primaryColor}/npc equipment {secondaryColor}(npc) {primaryColor}(set | clear | list)" + npc_equipment_set: "Syntax: {primaryColor}/npc equipment {secondaryColor}(npc) {primaryColor}set {secondaryColor}(slot) (@hand | @none | item)" + npc_glowing: "Syntax: {primaryColor}/npc glowing {secondaryColor}(npc) (disabled | color)" + npc_info: "Syntax: {primaryColor}/npc info {secondaryColor}(npc)" + npc_interaction_cooldown: "Syntax: {primaryColor}/npc interaction_cooldown {secondaryColor}(npc) (disabled | time)" + npc_scale: "Syntax: {primaryColor}/npc scale {secondaryColor}(npc) (factor)" + npc_list: "Syntax: {primaryColor}/npc list {secondaryColor}[filters...]" + npc_move_here: "Syntax: {primaryColor}/npc move_here {secondaryColor}(npc)" + npc_move_to: "Syntax: {primaryColor}/npc move_to {secondaryColor}(npc) (x) (y) (z) [world]" + npc_nearby: "Syntax: {primaryColor}/npc nearby {secondaryColor}[filters...]" + npc_remove: "Syntax: {primaryColor}/npc remove {secondaryColor}(npc)" + npc_show_in_tab: "Syntax: {primaryColor}/npc show_in_tab {secondaryColor}(npc) (state)" + npc_skin: "Syntax: {primaryColor}/npc skin {secondaryColor}(npc) (@none | @mirror | name | uuid | placeholder | url | file name) [--slim]" + npc_teleport: "Syntax: {primaryColor}/npc teleport {secondaryColor}(npc)" + npc_turn_to_player: "Syntax: {primaryColor}/npc turn_to_player {secondaryColor}(npc) (state)" + npc_type: "Syntax: {primaryColor}/npc type {secondaryColor}(npc) (type)" + npc_visibility_distance: "Syntax: {primaryColor}/npc visibility_distance {secondaryColor}(npc) (always_visible | default | not_visible | distance)" + + # Commands (fancynpcs) + fancynpcs_reload_success: "› {successColor}Plugin has been reloaded." + fancynpcs_save_success: "› {successColor}NPCs have been saved." + fancynpcs_feature_flags_header: "------------------- Feature Flags ------------------" + fancynpcs_feature_flags_entry: " <#848484>{number}. {warningColor}{name} <#848484>({id}): {state}" + fancynpcs_feature_flags_footer: "------------- Showing total of {warningColor}{total_formatted} entries -------------" + fancynpcs_test_success: "› {successColor}All {count} tests have passed successfully (ran by {player} at {time})." + fancynpcs_test_failure: "› {errorColor}Some tests have failed (ran by {player} at {time})." + fancynpcs_skin_system_restart_schedulers_success: "› {successColor}Skin schedulers have been restarted." + fancynpcs_skin_system_clear_queues_success: "› {successColor}Skin generation queues have been cleared." + fancynpcs_skin_system_clear_cache_success: "› {successColor}Skin cache has been cleared." + + # Commands (npc help) + npc_help_page_header: "------------- {primaryColor}FancyNpcs Commands ({primaryColor}{page}/{primaryColor}{max_page}) --------------" + npc_help_page_footer: "----------- Click {primaryColor}here to open documentation -----------" + npc_help_contents: + - "Adds new action to the list.'>{primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}add {secondaryColor}(index) [value]" + - "Adds new action to the list after specified position.'>{primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}add_after {secondaryColor}(index) [value]" + - "Adds new action to the list before specified position.'>{primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}add_before {secondaryColor}(index) [value]" + - "Clears all actions of the specified trigger.'>{primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}clear" + - "Lists all actions of the specified trigger.'>{primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}list" + - "Moves action at specified index down in the list.'>{primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}move_down {secondaryColor}(index)" + - "Moves action at specified index up in the list.'>{primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}move_up {secondaryColor}(index)" + - "Removes action from the list.'>{primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}remove {secondaryColor}(index)" + - "Adds new action to the list at specified position.'>{primaryColor}/npc action {secondaryColor}(npc) (trigger) {primaryColor}set {secondaryColor}(index) [value]" + - "Sets an attribute of the NPC.'>{primaryColor}/npc attribute {secondaryColor}(npc) {primaryColor}set {secondaryColor}(attribute) (value)" + - "Lists all modified attributes of the NPC.'>{primaryColor}/npc attribute {secondaryColor}(npc) {primaryColor}list" + - "Changes whether the NPC can collide with other entities.'>{primaryColor}/npc collidable {secondaryColor}(npc) [state]" + - "Copies (duplicates) specified NPC.'>{primaryColor}/npc copy {secondaryColor}(npc) (new_name)" + - "Creates a new NPC. Can be customized with flags.'>{primaryColor}/npc create {secondaryColor}(npc) [--type] [--location] [--world]" + - "Changes displayname of the NPC. Supports MiniMessage, PlaceholderAPI and MiniPlaceholders.'>{primaryColor}/npc displayname {secondaryColor}(npc) (name)" + - "Sets equipment slot of the NPC to item currently held in main hand, none or a specific item type.'>{primaryColor}/npc equipment {secondaryColor}(npc) {primaryColor}set {secondaryColor}(slot) (@hand | @none | item)" + - "Clears all equipment slots of the NPC.'>{primaryColor}/npc equipment {secondaryColor}(npc) {primaryColor}clear" + - "Lists all equipment of the NPC.'>{primaryColor}/npc equipment {secondaryColor}(npc) {primaryColor}list" + - "Changes glowing state and color of the NPC.'>{primaryColor}/npc glowing {secondaryColor}(npc) (disabled | color)" + - "Shows information about specified NPC.'>{primaryColor}/npc info {secondaryColor}(npc)" + - "Changes duration between interactions (cooldown) of the NPC.'>{primaryColor}/npc interaction_cooldown {secondaryColor}(npc) (disabled | time)" + - "Lists all NPCs in all worlds. Can be filtered and sorted.'>{primaryColor}/npc list {secondaryColor}[--type] [--sort]" + - "Teleports specified NPC to your location.'>{primaryColor}/npc move_here {secondaryColor}(npc)" + - "Teleports NPC to specified location.'>{primaryColor}/npc move_to {secondaryColor}(npc) (x) (y) (z) [world]" + - "Lists all NPCs in your world. Can be filtered and sorted.'>{primaryColor}/npc nearby {secondaryColor}[--radius] [--type] [--sort]" + - "Removes (deletes) specified NPC.'>{primaryColor}/npc remove {secondaryColor}(npc)" + - "Changes the scale of the size of the NPC.'>{primaryColor}/npc scale {secondaryColor}(npc) (factor)" + - "Changes whether the NPC is shown in the player-list. This works only on NPCs of PLAYER type.{errorColor}Re-connecting to the server might be required for changes to take effect.'>{primaryColor}/npc show_in_tab {secondaryColor}(npc) (state)" + - "Changes skin of the NPC.Supports PlaceholderAPI and MiniPlaceholders.{warningColor}@none - removes the skin{warningColor}@mirror - mirrors player skin{warningColor}(name) - name of any player{warningColor}(url) - url of the skin texture'>{primaryColor}/npc skin {secondaryColor}(npc) (@none | @mirror | name | url) [--slim]" + - "Teleports you to the specified NPC.'>{primaryColor}/npc teleport {secondaryColor}(npc)" + - "Changes whether the NPC should turn to the player when in range.'>{primaryColor}/npc turn_to_player {secondaryColor}(npc) (state)" + - "Changes the type of the NPC.'>{primaryColor}/npc type {secondaryColor}(npc) (type)" + - "Changes the visibility distance of the NPC.'>{primaryColor}/npc visibility_distance {secondaryColor}(npc) (default | distance | ...)" + + # Commands (npc action) + npc_action_add_success: "Action has been added. There are {warningColor}{total} actions in total." + npc_action_add_before_success: "Action has been added before action {warningColor}{number}. There are {warningColor}{total} actions in total." + npc_action_add_after_success: "Action has been added after action {warningColor}{number}. There are {warningColor}{total} actions in total." + npc_action_set_success: "Action trigger {warningColor}{number} has been updated. There are {warningColor}{total} actions in total." + npc_action_set_failure: "› {errorColor}Action trigger {warningColor}{number}{errorColor} does not exist." + npc_action_move_up_success: "Action trigger {warningColor}{number} has been moved up." + npc_action_move_up_failure: "› {errorColor}Action trigger {warningColor}{number}{errorColor} is already at the top." + npc_action_move_down_success: "Action trigger {warningColor}{number} has been moved down." + npc_action_move_down_failure: "› {errorColor}Action trigger {warningColor}{number}{errorColor} is already at the bottom." + npc_action_requires_value: "› {errorColor}You must specify a value for this action." + npc_action_remove_success: "Action trigger {warningColor}{number} has been removed. There are {warningColor}{total} actions in total." + npc_action_remove_failure: "› {errorColor}Action trigger {warningColor}{number}{errorColor} does not exist." + npc_action_clear_success: "Actions have been cleared." + npc_action_list_header: "--------------------- Actions ---------------------" + npc_action_list_entry: " <#848484>{number}. {warningColor}{action}: {value}" + npc_action_list_footer: "------------- Showing total of {warningColor}{total} entries -------------" + npc_action_list_failure_empty: "› {errorColor}There are no actions in the list. Use {warningColor}/npc action (npc) (trigger) add (action){errorColor} to add your first action." + + # Commands (npc attribute) + npc_attribute_set: "Attribute {warningColor}{attribute} has been set to {warningColor}{value}." + npc_attribute_set_invalid_for_this_entity_type: "› {errorColor}Attribute {warningColor}{input}{errorColor} is not valid attribute for this entity type." + npc_attribute_list_header: "-------------------- Attributes --------------------" + npc_attribute_list_entry: " › <#848484>{attribute}: {warningColor}{value}" + npc_attribute_list_footer: "---------------------------------------------------" + npc_attribute_list_failure_empty: "› {errorColor}There are no attributes set. Use {warningColor}/npc attribute (npc) set (attribute) (value){errorColor} to set an attribute." + + # Commands (npc collidable) + npc_collidable_set_true: "NPC {warningColor}{npc} is now collidable." + npc_collidable_set_false: "NPC {warningColor}{npc} is no longer collidable." + + # Commands (npc copy) + npc_copy_success: "NPC {warningColor}{npc} has been copied to {warningColor}{new_npc}." + + # Commands (npc create) + npc_create_success: "NPC {warningColor}{npc} has been created." + npc_create_failure_invalid_name: "› {errorColor}Name contains illegal characters. Only [{warningColor}A-Z{errorColor}, {warningColor}a-z{errorColor}, {warningColor}0-9{errorColor}, {warningColor}_{errorColor}, {warningColor}-{errorColor}, {warningColor}/{errorColor}] characters are allowed." + npc_create_failure_already_exists: "› {errorColor}NPC {warningColor}{npc}{errorColor} already exists." + npc_create_failure_must_specify_world: "› {errorColor}You must specify {warningColor}--world{errorColor} flag when running this command from the console." + + # Commands (npc displayname) + npc_displayname_set_name: "NPC {warningColor}{npc} is now using {name} as their display name." + npc_displayname_set_empty: "NPC {warningColor}{npc} is no longer showing display name." + + # Commands (npc equipment) + npc_equipment_set_item: "Equipment slot {warningColor}{slot} has been set to ." + npc_equipment_set_empty: "Equipment slot {warningColor}{slot} has been removed." + npc_equipment_set_failure_invalid_slot: "› {errorColor}Argument {warningColor}{input}{errorColor} is not a valid equipment slot." + npc_equipment_clear_success: "Equipment has been cleared." + npc_equipment_list_header: "-------------------- Equipment --------------------" + npc_equipment_list_entry: " <#848484>{slot}: " + npc_equipment_list_footer: "---------------------------------------------------" + npc_equipment_list_failure_empty: "› {errorColor}There are no equipment slots set. Use {warningColor}/npc equipment (npc) set (slot) (@hand | @none | material){errorColor} to set an equipment slot." + + # Commands (npc fix) + npc_fix_success: "Attempted to fix NPC {warningColor}{npc}... Still having issues? Please let us know." + + # Commands (npc glowing) + npc_glowing_set_true: "NPC {warningColor}{npc} is now glowing." + npc_glowing_set_false: "NPC {warningColor}{npc} is no longer glowing." + npc_glowing_set_color_success: "NPC {warningColor}{npc} is now glowing in {color}." + npc_glowing_set_color_failure_invalid_color: "› {errorColor}Specified value {warningColor}{input}{errorColor} is not a valid color." + + # Commands (npc info) + npc_info_general: + - "" + - "Unique, permanent identifier of the NPC.'><#848484>Identifier: Click to copy UUID to clipboard.'>{warningColor}{id}" + - "Identifier of player who created this NPC.'>Creator: Click to copy creator UUID to clipboard.'>{warningColor}{creator_uuid_short} ({creator_name})" + - "Name of the NPC, used in commands and displayed above their head if display name is not set.'><#848484>Name: {warningColor}{name}" + - "Display name of the NPC, displayed above their head.'>Display Name: {displayname}" + - "Entity type of the NPC.'><#848484>Type: {warningColor}{type}" + - "Current location of the NPC.'>Location: Click to copy location to clipboard.'>{warningColor}{location_x}, {warningColor}{location_y}, {warningColor}{location_z} in {warningColor}{world}" + - "Glowing state of the NPC. Can be a {warningColor}color or {errorColor}disabled.'><#848484>Glow: {glow}" + - "Whether the NPC should turn to player. Can be {successColor}true or {errorColor}false.'>Turns to Player: {is_turn_to_player}" + - "Whether the NPC should be shown in player-list. Can be {successColor}true or {errorColor}false.'><#848484>Shown in TAB: {is_show_in_tab}" + - "Collidable state of the NPC. Can be {successColor}true or {errorColor}false.'>Collidable: {is_collidable}" + - "Skin mirroring state of the NPC. Can be {successColor}true or {errorColor}false.'><#848484>Skin Mirroring: {warningColor}{is_skin_mirror}" + - "Cooldown between interactions.'>Interaction Cooldown: {warningColor}{interaction_cooldown}" + - "Scale factor for the NPC size.'><#848484>Scale: {warningColor}x{scale}" + - "Visibility distance of the NPC.'>Visibility Distance: {warningColor}{visibility_distance}" + - "" + - "Equipment of the NPC.Click the {warningColor}[Click Here] button to browse.'>Equipment: {warningColor}[Click Here]" + - "Modified attributes of the NPC.Click the {warningColor}[Click Here] button to browse.'><#848484>Attributes: {warningColor}[Click Here]" + - "" + - "Use commands from {warningColor}/npc action (npc) (trigger) group to manage actions.'>There are {warningColor}{actions_total} actions associated with this NPC." + - "" + - "› {primaryColor}Can't find what you're looking for?" + - "Open the chat window to see all information." + - "" + + # Commands (npc interaction_cooldown) + npc_interaction_cooldown_set: "Interaction cooldown has been set to {warningColor}{time}." + npc_interaction_cooldown_disabled: "Interaction cooldown has been disabled." + + # Commands (npc list) + npc_list_header: "------------------ List Query Result ------------------" + npc_list_entry: " <#848484>{number}. Click to see more details.'>{warningColor}{npc} Click to teleport.'><#848484>({location_x}, {location_y}, {location_z} in {world})" + npc_list_footer: "---------- Showing {warningColor}{count_formatted} out of total {warningColor}{total_formatted} entries ----------" + npc_list_failure_sort_requires_player: "› {errorColor}This sort type cannot be used from the console." + npc_list_failure_requires_world_flag: "› {errorColor}You must specify {warningColor}--world{errorColor} flag when running this command from the console." + + # Commands (npc move_here) + npc_move_here_success: "NPC {warningColor}{npc} has been moved to your location." + + # Commands (npc move_to) + npc_move_to_success: "NPC {warningColor}{npc} has been moved to {warningColor}{x}, {warningColor}{y}, {warningColor}{z} in {warningColor}{world}." + npc_move_to_failure_must_specify_world: "› {errorColor}You must specify world when running this command from the console." + + # Commands (npc nearby) + npc_nearby_header: "---------------- Nearby Query Result -----------------" + npc_nearby_entry: " <#848484>{number}. Click to see more details.'>{warningColor}{npc} Click to teleport.'><#848484>({distance} blocks away)" + npc_nearby_footer: "---------- Showing {warningColor}{count_formatted} out of total {warningColor}{total_formatted} entries ----------" + + # Commands (npc remove) + npc_remove_success: "NPC {warningColor}{npc} has been removed." + + # Commands (scale) + npc_scale_set_success: "NPC {warningColor}{npc} has been scaled to {warningColor}{scale}." + + # Commands (npc visibility_distance) + npc_visibility_distance_set_value: "NPC {warningColor}{npc} is now visible from {warningColor}{distance} blocks." + npc_visibility_distance_set_default: "NPC {warningColor}{npc} is now using default visibility distance of {warningColor}{distance}." + npc_visibility_distance_set_not_visible: "NPC {warningColor}{npc} is no longer visible." + npc_visibility_distance_set_always_visible: "NPC {warningColor}{npc} is now always visible." + + # Commands (npc show_in_tab) + npc_show_in_tab_set_true: "NPC {warningColor}{npc} is now shown in player-list." + npc_show_in_tab_set_false: "NPC {warningColor}{npc} is no longer shown in player-list." + + # Commands (npc skin) + npc_skin_set: "NPC {warningColor}{npc} is now using the {warningColor}{name} skin." + npc_skin_set_mirror: "NPC {warningColor}{npc} is now mirroring player skin." + npc_skin_set_none: "NPC {warningColor}{npc} is no longer using any skin." + npc_skin_set_later: "NPC {warningColor}{npc} will be updated with the new skin shortly." + + # Commands (npc teleport) + npc_teleport_success: "You have been teleported to NPC {warningColor}{npc}." + npc_teleport_failure_exception: "› {errorColor}An error occurred while trying to teleport to NPC. Check console for errors." + npc_teleport_failure_world_not_loaded: "› {errorColor}An error occurred while trying to teleport to NPC. Destination world is not loaded." + + # Commands (npc turn_to_player) + npc_turn_to_player_set_true: "NPC {warningColor}{npc} is now turning to player." + npc_turn_to_player_set_false: "NPC {warningColor}{npc} is no longer turning to player." + + # Commands (npc type) + npc_type_success: "NPC {warningColor}{npc} type has been changed to {warningColor}{type}." diff --git a/plugins/fancynpcs/src/main/resources/skins/testskin.png b/plugins/fancynpcs/src/main/resources/skins/testskin.png new file mode 100644 index 00000000..b841077c Binary files /dev/null and b/plugins/fancynpcs/src/main/resources/skins/testskin.png differ diff --git a/plugins/fancynpcs/src/main/resources/version.yml b/plugins/fancynpcs/src/main/resources/version.yml new file mode 100644 index 00000000..2de69789 --- /dev/null +++ b/plugins/fancynpcs/src/main/resources/version.yml @@ -0,0 +1,3 @@ +version: $version +build: $build +hash: $hash \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 1e6fc32c..df52045f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,6 +7,19 @@ include(":plugins:fancyholograms:implementation_1_20_2") include(":plugins:fancyholograms:implementation_1_20_1") include(":plugins:fancyholograms:implementation_1_19_4") +include(":plugins:fancynpcs:api") +include(":plugins:fancynpcs:implementation_1_21_5") +include(":plugins:fancynpcs:implementation_1_21_4") +include(":plugins:fancynpcs:implementation_1_21_3") +include(":plugins:fancynpcs:implementation_1_21_1") +include(":plugins:fancynpcs:implementation_1_20_6") +include(":plugins:fancynpcs:implementation_1_20_4") +include(":plugins:fancynpcs:implementation_1_20_2") +include(":plugins:fancynpcs:implementation_1_20_1") +include(":plugins:fancynpcs:implementation_1_20") +include(":plugins:fancynpcs:implementation_1_19_4") + + include(":plugins:fancyvisuals") include(":libraries:common")