Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F11421746
D3647.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
17 KB
Referenced Files
None
Subscribers
None
D3647.diff
View Options
diff --git a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/SuhaylPlugin.java b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/SuhaylPlugin.java
--- a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/SuhaylPlugin.java
+++ b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/SuhaylPlugin.java
@@ -3,27 +3,48 @@
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
-import org.eu.loupsgris.quilvaryn.config.DefaultSuhaylConfig;
import org.eu.loupsgris.quilvaryn.config.SuhaylConfig;
+import org.eu.loupsgris.quilvaryn.items.CommandmentStick;
import org.eu.loupsgris.quilvaryn.listeners.HappyGhastBehavior;
import org.eu.loupsgris.quilvaryn.listeners.OptimizedWeather;
+import org.eu.loupsgris.quilvaryn.listeners.WanderingTraderBehavior;
+import org.eu.loupsgris.quilvaryn.menus.WanderingTraderMenu;
+import org.eu.loupsgris.quilvaryn.services.WanderingTraderService;
public class SuhaylPlugin extends JavaPlugin {
public SuhaylConfig config;
+ public static String NAMESPACE = "suhayl";
+
@Override
public void onEnable() {
- config = new DefaultSuhaylConfig();
+ config = SuhaylConfig.Load(this.getConfig());
// Register features
Bukkit.getPluginManager().registerEvents(new OptimizedWeather(this), this);
Bukkit.getPluginManager().registerEvents(new HappyGhastBehavior(this), this);
+
+ registerCommandmentStick();
+ }
+
+ private void registerCommandmentStick() {
+ WanderingTraderService service = new WanderingTraderService(config, getLogger());
+
+ WanderingTraderMenu menu = new WanderingTraderMenu(service, config);
+ Bukkit.getPluginManager().registerEvents(menu, this);
+
+ WanderingTraderBehavior listener = new WanderingTraderBehavior(
+ service,
+ menu
+ );
+ Bukkit.getPluginManager().registerEvents(listener, this);
+
+ Bukkit.addRecipe(CommandmentStick.getRecipe());
}
@Override
public void onDisable() {
getLogger().info("Suhayl quality of life plugin disabled.");
}
-
}
diff --git a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/config/DefaultSuhaylConfig.java b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/config/DefaultSuhaylConfig.java
deleted file mode 100644
--- a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/config/DefaultSuhaylConfig.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.eu.loupsgris.quilvaryn.config;
-
-public class DefaultSuhaylConfig extends SuhaylConfig {
-
- public DefaultSuhaylConfig() {
- happyGhastSpeedBoost = 2.0;
- rainRate = 0.7;
- }
-
-}
diff --git a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/config/SuhaylConfig.java b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/config/SuhaylConfig.java
--- a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/config/SuhaylConfig.java
+++ b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/config/SuhaylConfig.java
@@ -1,10 +1,17 @@
package org.eu.loupsgris.quilvaryn.config;
+import org.bukkit.configuration.file.FileConfiguration;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
public class SuhaylConfig {
/*
* No-op values:
- * 1.0 for multipliers
+ * 1.0 for multipliers.
+ * 0.0 for cooldown delays.
*
* Actual default values are in Default- class.
*/
@@ -13,4 +20,50 @@
public double rainRate = 1.0;
+ public int commandmentStickBanTime = 0;
+
+ public Map<String, String> commandmentStickMessages = new HashMap<>();
+
+ public static SuhaylConfig Load (FileConfiguration inFileConfig) {
+ SuhaylConfig loadedConfig = new SuhaylConfig();
+
+ SuhaylConfig defaultConfig = new SuhaylConfig();
+ defaultConfig.setDefault();
+
+ loadedConfig.commandmentStickBanTime = inFileConfig.getInt(
+ "commandment-stick.banish-minutes",
+ defaultConfig.commandmentStickBanTime
+ );
+
+ Object value = inFileConfig.get("commandment-stick.messages");
+ if (value instanceof Map messages) {
+ try {
+ loadedConfig.commandmentStickMessages = messages;
+ } catch (Exception ignored) {
+ }
+ }
+
+ return loadedConfig;
+ }
+
+ public String getCommandmentStickMessage(String key, String defaultMessage) {
+ return commandmentStickMessages.getOrDefault(key, defaultMessage);
+ }
+
+ public Optional<String> getCommandmentStickMessage(String key) {
+ if (!commandmentStickMessages.containsKey(key)) {
+ return Optional.empty();
+ }
+
+ return Optional.of(commandmentStickMessages.get(key));
+ }
+
+ public void setDefault() {
+ happyGhastSpeedBoost = 2.0;
+
+ rainRate = 0.7;
+
+ commandmentStickBanTime = 20;
+ }
+
}
diff --git a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/items/CommandmentStick.java b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/items/CommandmentStick.java
new file mode 100644
--- /dev/null
+++ b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/items/CommandmentStick.java
@@ -0,0 +1,80 @@
+package org.eu.loupsgris.quilvaryn.items;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.ShapedRecipe;
+
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+import org.eu.loupsgris.quilvaryn.SuhaylPlugin;
+
+import java.util.List;
+
+public class CommandmentStick {
+
+ public static String ITEM_TAG = "is_commandment_stick";
+
+ public static ItemStack getItem() {
+ ItemStack item = new ItemStack(Material.BLAZE_ROD);
+
+ item.editMeta(meta -> {
+ // Gold is uncommon
+ meta.customName(
+ Component.text("Commandment Stick", NamedTextColor.GOLD)
+ );
+
+ meta.lore(List.of(
+ Component.text("§7Right-click a Wandering Trader"),
+ Component.text("§7to give them instructions.")
+ ));
+
+ NamespacedKey key = new NamespacedKey(SuhaylPlugin.NAMESPACE, ITEM_TAG);
+ meta.getPersistentDataContainer()
+ .set(key, PersistentDataType.BOOLEAN, true);
+
+ meta.setMaxStackSize(1);
+ });
+
+ return item;
+ }
+
+ public static Boolean isItem(ItemStack item) {
+ if (item.getType() != Material.BLAZE_ROD || !item.hasItemMeta()) {
+ return false;
+ }
+
+ ItemMeta meta = item.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+
+ NamespacedKey key = new NamespacedKey(SuhaylPlugin.NAMESPACE, ITEM_TAG);
+ return
+ pdc.has(key, PersistentDataType.BOOLEAN)
+ &&
+ pdc.get(key, PersistentDataType.BOOLEAN);
+ }
+
+ public static ShapedRecipe getRecipe () {
+ NamespacedKey key = new NamespacedKey(
+ SuhaylPlugin.NAMESPACE, "commandment_stick"
+ );
+ ItemStack item = getItem();
+
+ ShapedRecipe recipe = new ShapedRecipe(key, item);
+ recipe.shape(
+ " G ",
+ " P ",
+ " S "
+ );
+ recipe.setIngredient('G', Material.GOLD_NUGGET);
+ recipe.setIngredient('P', Material.PAPER);
+ recipe.setIngredient('S', Material.STICK);
+
+ return recipe;
+ }
+
+}
diff --git a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/listeners/WanderingTraderBehavior.java b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/listeners/WanderingTraderBehavior.java
new file mode 100644
--- /dev/null
+++ b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/listeners/WanderingTraderBehavior.java
@@ -0,0 +1,61 @@
+package org.eu.loupsgris.quilvaryn.listeners;
+
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.CreatureSpawnEvent;
+import org.bukkit.event.player.PlayerInteractAtEntityEvent;
+import org.bukkit.inventory.EquipmentSlot;
+import org.bukkit.inventory.ItemStack;
+
+import org.eu.loupsgris.quilvaryn.items.CommandmentStick;
+import org.eu.loupsgris.quilvaryn.menus.WanderingTraderMenu;
+import org.eu.loupsgris.quilvaryn.services.WanderingTraderService;
+
+import java.util.UUID;
+
+public class WanderingTraderBehavior implements Listener {
+
+ private final WanderingTraderService service;
+
+ private final WanderingTraderMenu menu;
+
+ public WanderingTraderBehavior(WanderingTraderService service, WanderingTraderMenu menu) {
+ this.service = service;
+ this.menu = menu;
+ }
+
+ @EventHandler
+ public void onTraderSpawn(CreatureSpawnEvent event) {
+ if (event.getEntityType() != EntityType.WANDERING_TRADER) {
+ return;
+ }
+
+ if (!service.canTradersVisit()) {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler
+ public void onUseStick(PlayerInteractAtEntityEvent event) {
+ if (event.getRightClicked().getType() != EntityType.WANDERING_TRADER) {
+ return;
+ }
+
+ if (event.getHand() != EquipmentSlot.HAND) return;
+
+ Player player = event.getPlayer();
+ ItemStack item = player.getInventory().getItemInMainHand();
+
+ if (!CommandmentStick.isItem(item)) {
+ return;
+ }
+
+ event.setCancelled(true);
+
+ UUID traderId = event.getRightClicked().getUniqueId();
+ menu.openFor(player, traderId);
+ }
+
+}
diff --git a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/menus/WanderingTraderMenu.java b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/menus/WanderingTraderMenu.java
new file mode 100644
--- /dev/null
+++ b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/menus/WanderingTraderMenu.java
@@ -0,0 +1,113 @@
+package org.eu.loupsgris.quilvaryn.menus;
+
+import net.kyori.adventure.text.Component;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+
+import org.eu.loupsgris.quilvaryn.config.SuhaylConfig;
+import org.eu.loupsgris.quilvaryn.services.WanderingTraderService;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public class WanderingTraderMenu implements Listener {
+
+ private final WanderingTraderService service;
+
+ private final Map<UUID, UUID> openMenus;
+
+ private final String title;
+
+ private final String labelLeave;
+
+ private final String labelBanish;
+
+ private final String labelSilence;
+
+ public WanderingTraderMenu (WanderingTraderService service, SuhaylConfig config) {
+ this.service = service;
+
+ openMenus = new HashMap<>();
+
+ title = config.getCommandmentStickMessage("menu-title", "#WANDERING_TRADER_MENU_TITLE");
+
+ labelLeave = config.getCommandmentStickMessage("option-leave", "#OPTION_LEAVE");
+ labelBanish = config.getCommandmentStickMessage("option-banish", "#OPTION_BANISH");
+ labelSilence = config.getCommandmentStickMessage("option-silence", "#OPTION_SIlENCE");
+ }
+
+ public void openFor(Player player, UUID traderId) {
+ Inventory inventory = Bukkit.createInventory(player, 9, title);
+
+ inventory.setItem(2, buildButton(Material.LIME_DYE, labelLeave));
+ inventory.setItem(4, buildButton(Material.CLOCK, labelBanish));
+ inventory.setItem(6, buildButton(Material.MUSIC_DISC_13, labelSilence));
+
+ openMenus.put(player.getUniqueId(), traderId);
+
+ player.openInventory(inventory);
+ }
+
+ private ItemStack buildButton(Material material, String name) {
+ ItemStack item = new ItemStack(material);
+
+ item.editMeta(meta -> {
+ meta.displayName(
+ Component.text(name)
+ );
+ });
+
+ return item;
+ }
+
+ @EventHandler
+ public void onClick(InventoryClickEvent event) {
+ if (!(event.getWhoClicked() instanceof Player player)) {
+ return;
+ }
+
+ if (!event.getView().getTitle().equals(title)) {
+ return;
+ }
+
+ event.setCancelled(true);
+
+ UUID traderId = openMenus.get(player.getUniqueId());
+ if (traderId == null) {
+ return;
+ }
+
+ var current = event.getCurrentItem();
+ if (current == null || !current.hasItemMeta()) {
+ return;
+ }
+ String name = current.getItemMeta().getDisplayName();
+
+ if (name.equals(labelLeave)) {
+ service.askToLeave(player, traderId);
+ player.closeInventory();
+ } else if (name.equals(labelBanish)) {
+ service.banish(player, traderId);
+ player.closeInventory();
+ } else if (name.equals(labelSilence)) {
+ service.toggleSilence(player, traderId);
+ }
+ }
+
+ @EventHandler
+ public void onClose(InventoryCloseEvent e) {
+ if (e.getView().getTitle().equals(title)) {
+ openMenus.remove(e.getPlayer().getUniqueId());
+ }
+ }
+
+}
diff --git a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/services/WanderingTraderService.java b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/services/WanderingTraderService.java
new file mode 100644
--- /dev/null
+++ b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/services/WanderingTraderService.java
@@ -0,0 +1,101 @@
+package org.eu.loupsgris.quilvaryn.services;
+
+import org.bukkit.World;
+import org.bukkit.entity.*;
+import org.bukkit.potion.PotionEffect;
+import org.bukkit.potion.PotionEffectType;
+
+import org.eu.loupsgris.quilvaryn.config.SuhaylConfig;
+
+import java.util.UUID;
+import java.util.logging.Logger;
+
+public class WanderingTraderService {
+
+ private final SuhaylConfig config;
+
+ private final Logger logger;
+
+ private long banishUntil = 0L;
+
+ public WanderingTraderService(SuhaylConfig config, Logger logger) {
+ this.config = config;
+ this.logger = logger;
+ }
+
+ public void askToLeave(Player requester, UUID traderId) {
+ World world = requester.getWorld();
+ Entity entity = world.getEntity(traderId);
+ if (!(entity instanceof WanderingTrader trader)) {
+ logger.info("Potential plugin issue - askToLeave called on non wandering trader");
+ return;
+ }
+
+ // Brief particles and invisibility before removal
+ // That allows to render the disapproval of the trader of the request
+ trader.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 40, 1, true, false, false));
+
+ removeLeashedTraderLlamas(trader);
+ trader.remove();
+
+ logger.info("Wandering trader removed.");
+ }
+
+ public void banish(Player requester, UUID traderId) {
+ askToLeave(requester, traderId);
+
+ int banTime = this.config.commandmentStickBanTime;
+ banishUntil = System.currentTimeMillis() + banTime * 60_000L;
+
+ requester.sendMessage("§7Wandering traders are discouraged from visiting for a while.");
+ }
+
+ public void toggleSilence(Player requester, UUID traderId) {
+ World world = requester.getWorld();
+ Entity entity = world.getEntity(traderId);
+ if (!(entity instanceof WanderingTrader trader)) {
+ logger.info("Potential plugin issue - toggleSilence called on non wandering trader");
+ return;
+ }
+
+ if (trader.isSilent()) {
+ trader.setSilent(false);
+ sendMessageToPlayer(requester, "silenced");
+ } else {
+ // Shhh!
+ trader.setSilent(true);
+ sendMessageToPlayer(requester, "unsilenced");
+ }
+ }
+
+ private void sendMessageToPlayer(Player player, String messageKey) {
+ config
+ .getCommandmentStickMessage(messageKey)
+ .ifPresent(player::sendMessage);
+ }
+
+ private void removeLeashedTraderLlamas(WanderingTrader trader) {
+ trader
+ .getWorld()
+ .getNearbyEntities(trader.getLocation(), 16, 16, 16)
+ .forEach(this::tryToRemoveLlama);
+ }
+
+ private void tryToRemoveLlama(Entity entity) {
+ if (!(entity instanceof TraderLlama llama)) {
+ return;
+ }
+
+ if (isUnderTraderControl(llama)) {
+ llama.remove();
+ }
+ }
+
+ private boolean isUnderTraderControl(TraderLlama llama) {
+ return llama.isLeashed() && llama.getLeashHolder() instanceof WanderingTrader;
+ }
+
+ public boolean canTradersVisit() {
+ return System.currentTimeMillis() > banishUntil;
+ }
+}
diff --git a/plugins/suhayl/src/main/resources/config.yml b/plugins/suhayl/src/main/resources/config.yml
new file mode 100644
--- /dev/null
+++ b/plugins/suhayl/src/main/resources/config.yml
@@ -0,0 +1,11 @@
+commandment-stick:
+ banish-minutes: 120
+ messages:
+ menu-title: "Trader Etiquette"
+ option-leave: "All business is done, you can leave."
+ option-banish: "Come back later. No more trader this week."
+ option-silence: "Shhh… be quiet."
+
+ left-message: "§aThe trader nods and drifts away…"
+ silenced: "§7The trader goes quiet."
+ unsilenced: "§7The trader finds their voice again."
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Aug 27, 12:20 (16 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2936061
Default Alt Text
D3647.diff (17 KB)
Attached To
Mode
D3647: Add commandment stick
Attached
Detach File
Event Timeline
Log In to Comment