Initial commit

This commit is contained in:
Teriuihi 2021-09-25 01:56:42 +02:00
commit 5dd6cb44e5
27 changed files with 1366 additions and 0 deletions

51
.gitignore vendored Normal file
View File

@ -0,0 +1,51 @@
#
.idea
run
.gradle/
build/
# Eclipse stuff
.classpath
.project
.settings/
# VSCode stuff
.vscode/
# netbeans
nbproject/
nbactions.xml
# we use maven!
build.xml
# maven
target/
dependency-reduced-pom.xml
# vim
.*.sw[a-p]
# various other potential build files
build/
bin/
dist/
manifest.mf
# Mac filesystem dust
.DS_Store/
.DS_Store
# intellij
*.iml
*.ipr
*.iws
.idea/
out/
# Linux temp files
*~
!gradle/wrapper/gradle-wrapper.jar
build.bat

51
pom.xml Normal file
View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>VillagerShopUI</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
</properties>
<repositories>
<repository> <!-- Paper -->
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
<repository> <!-- MiniMessage -->
<id>sonatype-oss-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
<repository> <!-- Vault -->
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.17-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-minimessage</artifactId>
<version>4.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,20 @@
package com.alttd.GUI;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.Merchant;
import java.util.HashMap;
import java.util.UUID;
public interface GUI {
HashMap<UUID, GUI> GUIByUUID = new HashMap<>();
void open(Player player);
GUIAction getAction(int slot);
Inventory getInventory();
Merchant getMerchant();
}

View File

@ -0,0 +1,7 @@
package com.alttd.GUI;
import org.bukkit.entity.Player;
public interface GUIAction {
void click(Player player);
}

View File

@ -0,0 +1,50 @@
package com.alttd.GUI;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.Merchant;
import java.util.HashMap;
public abstract class GUIInventory implements GUI {
protected final Inventory inventory;
protected final HashMap<Integer, GUIAction> actions;
public GUIInventory(InventoryType type, Component name) {
inventory = Bukkit.createInventory(null, type, name);
actions = new HashMap<>();
}
public Merchant getMerchant() {
return null;
}
public Inventory getInventory() {
return inventory;
}
public void setItem(int slot, ItemStack stack, GUIAction action){
inventory.setItem(slot, stack);
if (action != null){
actions.put(slot, action);
}
}
public void setItem(int slot, ItemStack stack){
setItem(slot, stack, null);
}
public void open(Player player){
player.openInventory(inventory);
GUIByUUID.put(player.getUniqueId(), this);
}
public GUIAction getAction(int slot) {
return actions.get(slot);
}
}

View File

@ -0,0 +1,84 @@
package com.alttd.GUI;
import net.kyori.adventure.text.minimessage.MiniMessage;
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.event.inventory.InventoryType;
import org.bukkit.event.inventory.TradeSelectEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.MerchantRecipe;
public class GUIListener implements Listener {
@EventHandler
public void onClick(InventoryClickEvent event){
if (!(event.getWhoClicked() instanceof Player player)){
return;
}
GUI gui = GUI.GUIByUUID.get(player.getUniqueId());
if (gui == null || gui.getInventory() == null) {
if (event.getSlotType().equals(InventoryType.SlotType.CRAFTING) && event.getRawSlot() < 2)
event.setCancelled(true);
else if (event.getRawSlot() == 2 && event.getSlotType().equals(InventoryType.SlotType.RESULT)) {
event.setCancelled(true);
onResultSlotClick(event, gui);
}
return;
}
if (!gui.getInventory().equals(event.getInventory())) {
return;
}
event.setCancelled(true);
GUIAction action = gui.getAction(event.getSlot());
if (action != null){
action.click(player);
}
}
private void onResultSlotClick(InventoryClickEvent event, GUI gui) {
ItemStack currentItem = event.getCurrentItem();
if (currentItem == null)
return;
if (event.getClick().isShiftClick())
event.getWhoClicked().sendMessage(MiniMessage.get().parse(currentItem.getType().name() + ": " + event.getCurrentItem().getType().getMaxStackSize()));
else
event.getWhoClicked().sendMessage(MiniMessage.get().parse(currentItem.getType().name() + ": " + event.getCurrentItem().getAmount()));
}
@EventHandler
public void onTradeSelect(TradeSelectEvent event) {
if (!(event.getWhoClicked() instanceof Player player)){
return;
}
GUI gui = GUI.GUIByUUID.get(player.getUniqueId());
if ((!(gui instanceof GUIMerchant guiMerchant)))
return;
if (!gui.getMerchant().equals(event.getMerchant())) {
return;
}
event.setCancelled(true);
GUIAction action = guiMerchant.getAction(event.getIndex());
if (action != null){
action.click(player);
}
}
@EventHandler
public void onClose(InventoryCloseEvent event) {
GUI.GUIByUUID.remove(event.getPlayer().getUniqueId());
}
@EventHandler
public void onQuit(PlayerQuitEvent event){
GUI.GUIByUUID.remove(event.getPlayer().getUniqueId());
}
}

View File

@ -0,0 +1,61 @@
package com.alttd.GUI;
import com.alttd.objects.VillagerType;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.inventory.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
public abstract class GUIMerchant implements GUI{
protected final Merchant merchant;
protected final HashMap<Integer, GUIAction> actions;
private final VillagerType villagerType;
public GUIMerchant(Component name, VillagerType villagerType) {
merchant = Bukkit.createMerchant(name);
actions = new HashMap<>();
this.villagerType = villagerType;
}
public Merchant getMerchant() {
return merchant;
}
public Inventory getInventory() {
return null;
}
public void addItem(@NotNull ItemStack result, @NotNull ItemStack one, @Nullable ItemStack two, @Nullable GUIAction action){
MerchantRecipe merchantRecipe = new MerchantRecipe(result, 0, 10000, false, 0, 0);
merchantRecipe.addIngredient(one);
if (two != null)
merchantRecipe.addIngredient(two);
merchantRecipe.setPriceMultiplier(0);
ArrayList<MerchantRecipe> recipes = new ArrayList<>(merchant.getRecipes());
recipes.add(merchantRecipe);
merchant.setRecipes(recipes);
if (action != null){
actions.put(recipes.size() - 1, action);
}
}
public void open(Player player){
player.openMerchant(merchant, false);
GUIByUUID.put(player.getUniqueId(), this);
}
public GUIAction getAction(int slot) {
return actions.get(slot);
}
public VillagerType getVillagerType() {
return villagerType;
}
}

View File

@ -0,0 +1,45 @@
package com.alttd.GUI.windows;
import com.alttd.GUI.GUIMerchant;
import com.alttd.config.Config;
import com.alttd.economy.Calculation;
import com.alttd.objects.VillagerType;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.Template;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
public class BuyGUI extends GUIMerchant {
private static final MiniMessage miniMessage = MiniMessage.get();
public BuyGUI(VillagerType villagerType) {
super(MiniMessage.get().parse(Config.BUY_WINDOW,
Template.of("trader", villagerType.getDisplayName()),
Template.of("percentage", "100")), villagerType); //TODO get percentage from player somehow
for (ItemStack itemStack : villagerType.getBuying()) {
double price = Calculation.price(itemStack);
addItem(itemStack,
getPriceItem(price),
null,
player -> player.sendMessage(MiniMessage.get().parse("Hi! you bought: " + itemStack.getAmount() + " " + itemStack.getType().name() + " for " + price + "."))
);
}
}
private ItemStack getPriceItem(double price) {
if (price < 0) return nameItem(new ItemStack(Material.BARRIER), -1);
else if (price <= 10) return nameItem(new ItemStack(Material.IRON_INGOT), price);
else if (price <= 100) return nameItem(new ItemStack(Material.GOLD_INGOT), price);
else if (price <= 500) return nameItem(new ItemStack(Material.DIAMOND), price);
else return nameItem(new ItemStack(Material.NETHERITE_INGOT), price);
}
private ItemStack nameItem(ItemStack itemStack, double price) {
ItemMeta itemMeta = itemStack.getItemMeta();
itemMeta.displayName(miniMessage.parse("<green>" + price + "</green>")); //TODO configurable
itemStack.setItemMeta(itemMeta);
return itemStack;
}
}

View File

@ -0,0 +1,40 @@
package com.alttd.GUI.windows;
import com.alttd.GUI.GUIInventory;
import com.alttd.config.Config;
import com.alttd.objects.VillagerType;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.Template;
import org.bukkit.Material;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
public class OpenGUI extends GUIInventory {
private static final ItemStack BUY = new ItemStack(Material.GOLD_INGOT);
private static final ItemStack SELL = new ItemStack(Material.BUCKET);
static {
MiniMessage miniMessage = MiniMessage.get();
ItemMeta itemMeta;
{
itemMeta = BUY.getItemMeta();
itemMeta.displayName(miniMessage.parse("<green>Buy</green>"));
BUY.setItemMeta(itemMeta);
}
{
itemMeta = SELL.getItemMeta();
itemMeta.displayName(miniMessage.parse("<green>Sell</green>"));
SELL.setItemMeta(itemMeta);
}
}
public OpenGUI(VillagerType villagerType) {
super(InventoryType.HOPPER, MiniMessage.get().parse(Config.INITIAL_VILLAGER_WINDOW,
Template.of("trader", villagerType.getDisplayName()),
Template.of("percentage", "100"))); //TODO get percentage from player somehow
setItem(1, BUY, player -> new BuyGUI(villagerType).open(player));
setItem(3, SELL, player -> new SellGUI(villagerType).open(player));
}
}

View File

@ -0,0 +1,45 @@
package com.alttd.GUI.windows;
import com.alttd.GUI.GUIMerchant;
import com.alttd.config.Config;
import com.alttd.economy.Calculation;
import com.alttd.objects.VillagerType;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.Template;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
public class SellGUI extends GUIMerchant {
private static final MiniMessage miniMessage = MiniMessage.get();
public SellGUI(VillagerType villagerType) {
super(MiniMessage.get().parse(Config.SELL_WINDOW,
Template.of("trader", villagerType.getDisplayName()),
Template.of("percentage", "100")), villagerType); //TODO get percentage from player somehow
for (ItemStack itemStack : villagerType.getSelling()) {
double price = Calculation.price(itemStack);
addItem(itemStack,
getPriceItem(price),
null,
player -> player.sendMessage(MiniMessage.get().parse("Hi! you sold: " + itemStack.getAmount() + " " + itemStack.getType().name() + " for " + price + "."))
);
}
}
private ItemStack getPriceItem(double price) {
if (price < 0) return nameItem(new ItemStack(Material.BARRIER), -1);
else if (price <= 10) return nameItem(new ItemStack(Material.IRON_INGOT), price);
else if (price <= 100) return nameItem(new ItemStack(Material.GOLD_INGOT), price);
else if (price <= 500) return nameItem(new ItemStack(Material.DIAMOND), price);
else return nameItem(new ItemStack(Material.NETHERITE_INGOT), price);
}
private ItemStack nameItem(ItemStack itemStack, double price) {
ItemMeta itemMeta = itemStack.getItemMeta();
itemMeta.displayName(miniMessage.parse("<red>" + price * -1 + "</red>")); //TODO configurable
itemStack.setItemMeta(itemMeta);
return itemStack;
}
}

View File

@ -0,0 +1,41 @@
package com.alttd;
import com.alttd.GUI.GUIListener;
import com.alttd.commands.CommandManager;
import com.alttd.config.Config;
import com.alttd.config.VillagerConfig;
import com.alttd.config.WorthConfig;
import com.alttd.events.VillagerInteract;
import org.bukkit.plugin.java.JavaPlugin;
public class VillagerUI extends JavaPlugin {
public static VillagerUI instance;
public static VillagerUI getInstance() {
return instance;
}
@Override
public void onLoad() {
instance = this;
}
@Override
public void onEnable() {
registerEvents();
new CommandManager();
Config.reload();
VillagerConfig.reload();
WorthConfig.reload();
getLogger().info("--------------------------------------------------");
getLogger().info("Villager UI started");
getLogger().info("--------------------------------------------------");
}
private void registerEvents() {
getServer().getPluginManager().registerEvents(new GUIListener(), this);
getServer().getPluginManager().registerEvents(new VillagerInteract(), this);
}
}

View File

@ -0,0 +1,92 @@
package com.alttd.commands;
import com.alttd.VillagerUI;
import com.alttd.commands.subcommands.CommandCreateVillager;
import com.alttd.commands.subcommands.CommandHelp;
import com.alttd.commands.subcommands.CommandReload;
import com.alttd.config.Config;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.Template;
import org.bukkit.command.*;
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.Collectors;
public class CommandManager implements CommandExecutor, TabExecutor {
private final List<SubCommand> subCommands;
private final MiniMessage miniMessage;
public CommandManager() {
VillagerUI villagerUI = VillagerUI.getInstance();
PluginCommand command = villagerUI.getCommand("villagerui");
if (command == null) {
subCommands = null;
miniMessage = null;
villagerUI.getLogger().severe("Unable to find villager ui command.");
return;
}
command.setExecutor(this);
command.setTabCompleter(this);
subCommands = Arrays.asList(
new CommandHelp(this),
new CommandCreateVillager(),
new CommandReload());
miniMessage = MiniMessage.get();
}
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String cmd, @NotNull String[] args) {
if (args.length == 0) {
commandSender.sendMessage(miniMessage.parse(Config.HELP_MESSAGE_WRAPPER, Template.of("commands", subCommands.stream()
.filter(subCommand -> commandSender.hasPermission(subCommand.getPermission()))
.map(SubCommand::getHelpMessage)
.collect(Collectors.joining("\n")))));
return true;
}
SubCommand subCommand = getSubCommand(args[0]);
if (!commandSender.hasPermission(subCommand.getPermission())) {
commandSender.sendMessage(miniMessage.parse(Config.NO_PERMISSION));
return true;
}
return subCommand.onCommand(commandSender, args);
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String cmd, @NotNull String[] args) {
List<String> res = new ArrayList<>();
if (args.length <= 1) {
res.addAll(subCommands.stream()
.filter(subCommand -> commandSender.hasPermission(subCommand.getPermission()))
.map(SubCommand::getName)
.filter(name -> args.length == 0 || name.startsWith(args[0]))
.collect(Collectors.toList())
);
} else {
SubCommand subCommand = getSubCommand(args[0]);
if (subCommand != null && commandSender.hasPermission(subCommand.getPermission()))
res.addAll(subCommand.getTabComplete(commandSender, args));
}
return res;
}
public List<SubCommand> getSubCommands() {
return subCommands;
}
private SubCommand getSubCommand(String cmdName) {
return subCommands.stream()
.filter(subCommand -> subCommand.getName().equals(cmdName))
.findFirst()
.orElse(null);
}
}

View File

@ -0,0 +1,31 @@
package com.alttd.commands;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.command.CommandSender;
import java.util.List;
public abstract class SubCommand {
private final MiniMessage miniMessage;
public SubCommand() {
miniMessage = MiniMessage.get();
}
public abstract boolean onCommand(CommandSender commandSender, String[] args);
public abstract String getName();
public String getPermission() {
return "villagerui." + getName();
}
public abstract List<String> getTabComplete(CommandSender commandSender, String[] args);
public abstract String getHelpMessage();
protected MiniMessage getMiniMessage() {
return miniMessage;
}
}

View File

@ -0,0 +1,115 @@
package com.alttd.commands.subcommands;
import com.alttd.VillagerUI;
import com.alttd.commands.SubCommand;
import com.alttd.config.Config;
import com.alttd.config.VillagerConfig;
import com.alttd.objects.LoadedVillagers;
import com.alttd.objects.VillagerType;
import com.alttd.util.Utilities;
import net.kyori.adventure.text.minimessage.Template;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.Villager;
import org.bukkit.event.entity.CreatureSpawnEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
public class CommandCreateVillager extends SubCommand {
@Override
public boolean onCommand(CommandSender commandSender, String[] args) {
if (args.length < 8) {
commandSender.sendMessage(getMiniMessage().parse(getHelpMessage()));
return true;
}
Optional<VillagerType> first = VillagerType.getVillagerTypes().stream().filter(villagerType -> villagerType.getName().equalsIgnoreCase(args[1])).findFirst();
if (first.isEmpty()) {
commandSender.sendMessage(getMiniMessage().parse(getHelpMessage()));
return true;
}
VillagerType villagerType = first.get();
World world = Bukkit.getServer().getWorld(args[7]);
if (world == null) {
commandSender.sendMessage(getMiniMessage().parse(getHelpMessage()));
return true;
}
Location location = new Location(world, Double.parseDouble(args[2]),Double.parseDouble(args[3]),Double.parseDouble(args[4]), Float.parseFloat(args[5]), Float.parseFloat(args[6]));
Villager villager = (Villager) world.spawnEntity(location, EntityType.VILLAGER, CreatureSpawnEvent.SpawnReason.CUSTOM);
villager.setPersistent(true);
villager.setInvulnerable(true);
// villager.setVillagerType(Villager.Type.); TODO choose villager type?
villager.setRemoveWhenFarAway(false);
villager.customName(getMiniMessage().parse(Config.VILLAGER_NAME, Template.of("name", villagerType.getDisplayName())));
villager.setCustomNameVisible(true);
villager.setAI(false);
UUID uuid = villager.getUniqueId();
LoadedVillagers.addLoadedVillager(uuid, villagerType);
VillagerConfig.addVillager(uuid, villagerType);
return true;
}
@Override
public String getName() {
return "createvillager";
}
@Override
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
List<String> res = new ArrayList<>();
switch (args.length) {
case 2 -> res.addAll(VillagerType.getVillagerTypes().stream()
.map(VillagerType::getName)
.collect(Collectors.toList()));
case 3 -> {
if (commandSender instanceof Player player) {
res.add(String.valueOf(Utilities.round(player.getLocation().getX(), 2)));
}
}
case 4 -> {
if (commandSender instanceof Player player) {
res.add(String.valueOf(Utilities.round(player.getLocation().getY(), 1)));
}
}
case 5 -> {
if (commandSender instanceof Player player) {
res.add(String.valueOf(Utilities.round(player.getLocation().getZ(), 2)));
}
}
case 6 -> {
if (commandSender instanceof Player player) {
res.add(String.valueOf(Utilities.round(player.getLocation().getYaw(), 2)));
}
}
case 7 -> {
if (commandSender instanceof Player player) {
res.add(String.valueOf(Utilities.round(player.getLocation().getPitch(), 2)));
}
}
case 8 -> {
if (commandSender instanceof Player player) {
res.add(player.getLocation().getWorld().getName());
}
}
}
return res;
}
@Override
public String getHelpMessage() {
return Config.CREATE_VILLAGER_MESSAGE;
}
}

View File

@ -0,0 +1,50 @@
package com.alttd.commands.subcommands;
import com.alttd.commands.CommandManager;
import com.alttd.commands.SubCommand;
import com.alttd.config.Config;
import net.kyori.adventure.text.minimessage.Template;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class CommandHelp extends SubCommand {
private final CommandManager commandManager;
public CommandHelp(CommandManager commandManager) {
super();
this.commandManager = commandManager;
}
@Override
public boolean onCommand(CommandSender commandSender, String[] args) {
commandSender.sendMessage(getMiniMessage().parse(Config.HELP_MESSAGE_WRAPPER, Template.of("commands", commandManager.getSubCommands().stream()
.filter(subCommand -> commandSender.hasPermission(subCommand.getPermission()))
.map(SubCommand::getHelpMessage)
.collect(Collectors.joining("\n")))));
return true;
}
@Override
public String getName() {
return "help";
}
@Override
public String getPermission() {
return "villagerui.use";
}
@Override
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
return new ArrayList<>();
}
@Override
public String getHelpMessage() {
return Config.HELP_MESSAGE;
}
}

View File

@ -0,0 +1,37 @@
package com.alttd.commands.subcommands;
import com.alttd.commands.SubCommand;
import com.alttd.config.Config;
import com.alttd.config.VillagerConfig;
import com.alttd.config.WorthConfig;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.List;
public class CommandReload extends SubCommand {
@Override
public boolean onCommand(CommandSender commandSender, String[] args) {
Config.reload();
VillagerConfig.reload();
WorthConfig.reload();
commandSender.sendMessage(getMiniMessage().parse("<green>Reloaded VillagerShopUI config.</green>"));
return true;
}
@Override
public String getName() {
return "reload";
}
@Override
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
return new ArrayList<>();
}
@Override
public String getHelpMessage() {
return Config.RELOAD_MESSAGE;
}
}

View File

@ -0,0 +1,4 @@
package com.alttd.commands.subcommands;
public class CommandRemoveVillager {
}

View File

@ -0,0 +1,132 @@
package com.alttd.config;
import com.alttd.VillagerUI;
import com.google.common.collect.ImmutableMap;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
@SuppressWarnings({"unused", "SameParameterValue"})
abstract class AbstractConfig {
File file;
YamlConfiguration yaml;
Logger logger;
AbstractConfig(String filename) {
init(new File(VillagerUI.getInstance().getDataFolder(), filename), filename);
}
AbstractConfig(File file, String filename) {
init(new File(file.getPath() + File.separator + filename), filename);
}
private void init(File file, String filename) {
this.file = file;
this.yaml = new YamlConfiguration();
this.logger = VillagerUI.getInstance().getLogger();
try {
yaml.load(file);
} catch (IOException ignore) {
} catch (InvalidConfigurationException ex) {
logger.severe(String.format("Could not load %s, please correct your syntax errors", filename));
throw new RuntimeException(ex);
}
yaml.options().copyDefaults(true);
}
void readConfig(Class<?> clazz, Object instance) {
for (Method method : clazz.getDeclaredMethods()) {
if (Modifier.isPrivate(method.getModifiers())) {
if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) {
try {
method.setAccessible(true);
method.invoke(instance);
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex.getCause());
} catch (Exception ex) {
logger.severe("Error invoking " + method);
ex.printStackTrace();
}
}
}
}
save();
}
private void save() {
try {
yaml.save(file);
} catch (IOException ex) {
logger.severe("Could not save " + file);
ex.printStackTrace();
}
}
void set(String path, Object val) {
yaml.addDefault(path, val);
yaml.set(path, val);
save();
}
String getString(String path, String def) {
yaml.addDefault(path, def);
return yaml.getString(path, yaml.getString(path));
}
boolean getBoolean(String path, boolean def) {
yaml.addDefault(path, def);
return yaml.getBoolean(path, yaml.getBoolean(path));
}
int getInt(String path, int def) {
yaml.addDefault(path, def);
return yaml.getInt(path, yaml.getInt(path));
}
double getDouble(String path, double def) {
yaml.addDefault(path, def);
return yaml.getDouble(path, yaml.getDouble(path));
}
<T> List<?> getList(String path, T def) {
yaml.addDefault(path, def);
return yaml.getList(path, yaml.getList(path));
}
@NonNull
<T> Map<String, T> getMap(final @NonNull String path, final @Nullable Map<String, T> def) {
final ImmutableMap.Builder<String, T> builder = ImmutableMap.builder();
if (def != null && yaml.getConfigurationSection(path) == null) {
yaml.addDefault(path, def.isEmpty() ? new HashMap<>() : def);
return def;
}
final ConfigurationSection section = yaml.getConfigurationSection(path);
if (section != null) {
for (String key : section.getKeys(false)) {
@SuppressWarnings("unchecked")
final T val = (T) section.get(key);
if (val != null) {
builder.put(key, val);
}
}
}
return builder.build();
}
ConfigurationSection getConfigurationSection(String path) {
return yaml.getConfigurationSection(path);
}
}

View File

@ -0,0 +1,110 @@
package com.alttd.config;
import com.alttd.VillagerUI;
import com.alttd.objects.VillagerType;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.ItemStack;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
public final class Config extends AbstractConfig {
static Config config;
static int version;
public Config() {
super(new File(System.getProperty("user.home") + File.separator + "share" + File.separator + "configs" + File.separator + "VillagerShopUI"), "config.yml");
}
public static void reload() {
config = new Config();
version = config.getInt("config-version", 1);
config.set("config-version", 1);
config.readConfig(Config.class, null);
}
public static String INITIAL_VILLAGER_WINDOW = "<trader> price: <percentage>%";
public static String BUY_WINDOW = "<trader> price: <percentage>%";
public static String SELL_WINDOW = "<trader> price: <percentage>%";
private static void loadUI() {
INITIAL_VILLAGER_WINDOW = config.getString("ui.initial-window-name", INITIAL_VILLAGER_WINDOW);
BUY_WINDOW = config.getString("ui.buy-window-name", BUY_WINDOW);
SELL_WINDOW = config.getString("ui.sell-window-name", SELL_WINDOW);
}
public static String HELP_MESSAGE_WRAPPER = "<gold>VillagerShopUI help:\n<commands></gold>";
public static String HELP_MESSAGE = "<green>Show this menu: <gold>/villagerui help</gold></green>";
public static String RELOAD_MESSAGE = "<green>Reload configs: <gold>/villagerui reload</gold></green>";
public static String CREATE_VILLAGER_MESSAGE = "<green>Create a new trading villager: <gold>/villagerui createvillager <type> <x> <y> <z> <yaw> <pitch> <world></gold></green>";
private static void loadHelp() {
HELP_MESSAGE_WRAPPER = config.getString("help.help-wrapper", HELP_MESSAGE_WRAPPER);
HELP_MESSAGE = config.getString("help.help", HELP_MESSAGE);
RELOAD_MESSAGE = config.getString("help.reload", RELOAD_MESSAGE);
CREATE_VILLAGER_MESSAGE = config.getString("help.create-villager", CREATE_VILLAGER_MESSAGE);
}
public static String NO_PERMISSION = "<red>You do not have permission to do that.</red>";
public static String NO_CONSOLE = "<red>You cannot use this command from console.</red>";
private static void loadGeneric() {
NO_PERMISSION = config.getString("generic.no-permission", NO_PERMISSION);
NO_CONSOLE = config.getString("generic.no-console", NO_CONSOLE);
}
public static String VILLAGER_NAME = "<green><name></green>";
private static void loadIDKYET() {//TODO rename
VILLAGER_NAME = config.getString("idkyet.villager-name", VILLAGER_NAME); //TODO change path
}
private static void loadVillagerTypes() {
VillagerType.clearVillagerTypes();
ConfigurationSection configurationSection = config.getConfigurationSection("villager-types");
if (configurationSection == null) {
VillagerUI.getInstance().getLogger().warning("No villager types found in config.");
return;
}
Set<String> keys = configurationSection.getKeys(false);
if (keys.isEmpty())
VillagerUI.getInstance().getLogger().warning("No villager types found in config.");
keys.forEach(key -> {
ConfigurationSection villagerType = configurationSection.getConfigurationSection(key);
if (villagerType == null)
return;
VillagerType.addVillagerType(new VillagerType(
key,
villagerType.getString("name"),
loadProducts(villagerType.getConfigurationSection("buying")),
loadProducts(villagerType.getConfigurationSection("selling")),
villagerType.getDouble("price-modifier"))
);
});
}
private static HashSet<ItemStack> loadProducts(ConfigurationSection productsSection) {
HashSet<ItemStack> products = new HashSet<>();
if (productsSection == null)
return products;
productsSection.getKeys(false).forEach(item -> {
Material material = Material.getMaterial(item);
if (material == null) {
VillagerUI.getInstance().getLogger().warning("Invalid key in products -> " + item);
return;
}
products.add(new ItemStack(material, productsSection.getInt(item)));
});
return products;
}
}

View File

@ -0,0 +1,44 @@
package com.alttd.config;
import com.alttd.VillagerUI;
import com.alttd.objects.LoadedVillagers;
import com.alttd.objects.VillagerType;
import org.bukkit.configuration.ConfigurationSection;
import java.util.Set;
import java.util.UUID;
public class VillagerConfig extends AbstractConfig {
static VillagerConfig config;
static int version;
public VillagerConfig() {
super("villagerConfig.yml");
}
public static void reload() {
config = new VillagerConfig();
version = config.getInt("config-version", 1);
config.set("config-version", 1);
config.readConfig(VillagerConfig.class, null);
}
private static void loadVillagers() {
LoadedVillagers.clearLoadedVillagers();
config.getConfigurationSection("").getKeys(false).forEach(key -> {
VillagerType villagerType = VillagerType.getVillagerType(config.getString(key, ""));
if (villagerType != null)
LoadedVillagers.addLoadedVillager(UUID.fromString(key), villagerType);
else
VillagerUI.getInstance().getLogger().warning("Invalid config entry " + key + ".");
});
}
public static void addVillager(UUID uuid, VillagerType villagerType) {
config.set(uuid.toString(), villagerType.getName());
}
}

View File

@ -0,0 +1,47 @@
package com.alttd.config;
import com.alttd.VillagerUI;
import com.alttd.util.Utilities;
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import java.io.File;
import java.util.Set;
public class WorthConfig extends AbstractConfig {
static WorthConfig config;
static int version;
public WorthConfig() {
super(new File(System.getProperty("user.home") + File.separator + "share" + File.separator + "configs" + File.separator + "VillagerShopUI"), "worth.yml");
}
public static void reload() {
config = new WorthConfig();
version = config.getInt("config-version", 1);
config.set("config-version", 1);
config.readConfig(WorthConfig.class, null);
}
public static Object2DoubleMap<Material> prices = new Object2DoubleOpenHashMap<>();
private static void loadWorth() {
prices.clear();
ConfigurationSection worth = config.getConfigurationSection("worth");
Set<String> keys = worth.getKeys(false);
for (String key : keys) {
Material material = Material.getMaterial(key);
if (material == null) {
VillagerUI.getInstance().getLogger().warning("Invalid key in worth.yml -> " + key);
continue;
}
int price = (int) (worth.getDouble(key) * 100);
prices.put(Material.getMaterial(key), Utilities.round(price, 2));
}
}
}

View File

@ -0,0 +1,67 @@
package com.alttd.economy;
import com.alttd.config.WorthConfig;
import com.alttd.util.Utilities;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.inventory.*;
import java.util.Collection;
import java.util.List;
public class Calculation {
/**
* Calculate the price for an item
* @param item to calculate price for
* @return price or int < 0 for error
*/
public static double price(ItemStack item) {
if (WorthConfig.prices.containsKey(item.getType()))
return Utilities.round(WorthConfig.prices.getDouble(item.getType()) * item.getAmount(), 2);
WorthConfig.prices.put(item.getType(), Utilities.round(getPrice(item, null), 2));
return WorthConfig.prices.getDouble(item.getType()) * item.getAmount();
}
private static double getPrice(ItemStack item, Material blockedMaterial) {
if (WorthConfig.prices.containsKey(item.getType()))
return WorthConfig.prices.getDouble(item.getType());
double price = -1;
List<Recipe> recipes = Bukkit.getRecipesFor(item);
for (Recipe recipe : recipes) {
double possiblePrice;
if (recipe instanceof ShapedRecipe shapedRecipe) {
Collection<ItemStack> values = shapedRecipe.getIngredientMap().values();
if (values.stream().anyMatch(itemStack -> itemStack.getType().equals(blockedMaterial)))
continue;
possiblePrice = getPrice(values.stream().toList(), item.getType());
if (price == -1 || price > possiblePrice)
price = possiblePrice;
} else if (recipe instanceof ShapelessRecipe shapelessRecipe) {
if (shapelessRecipe.getIngredientList().stream().anyMatch(itemStack -> itemStack.getType().equals(blockedMaterial)))
continue;
possiblePrice = getPrice(shapelessRecipe.getIngredientList(), item.getType());
if (price == -1 || price > possiblePrice)
price = possiblePrice;
} else if (recipe instanceof FurnaceRecipe furnaceRecipe) {
possiblePrice = getPrice(furnaceRecipe.getInput(), item.getType());
if (price == -1 || price > possiblePrice)
price = possiblePrice;
}
}
return price;
}
private static double getPrice(List<ItemStack> items, Material blockedMaterial) {
double price = 0;
for (ItemStack item : items) {
double tmp = getPrice(item, blockedMaterial);
if (tmp == -1)
return -1;
price += tmp;
}
return price;
}
}

View File

@ -0,0 +1,24 @@
package com.alttd.events;
import com.alttd.GUI.windows.OpenGUI;
import com.alttd.objects.LoadedVillagers;
import com.alttd.objects.VillagerType;
import org.bukkit.entity.Villager;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractEntityEvent;
public class VillagerInteract implements Listener {
@EventHandler
public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
if (!(event.getRightClicked() instanceof Villager villager))
return;
VillagerType loadedVillager = LoadedVillagers.getLoadedVillager(villager.getUniqueId());
if (loadedVillager == null)
return;
new OpenGUI(loadedVillager).open(event.getPlayer());
}
}

View File

@ -0,0 +1,25 @@
package com.alttd.objects;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class LoadedVillagers {
private final static Map<UUID, VillagerType> loadedVillagers = new HashMap<>();
public static VillagerType getLoadedVillager(UUID uuid) {
return loadedVillagers.get(uuid);
}
public static void addLoadedVillager(UUID uuid, VillagerType villagerType) {
loadedVillagers.put(uuid, villagerType);
}
public static void removeLoadedVillager(UUID uuid) {
loadedVillagers.remove(uuid);
}
public static void clearLoadedVillagers() {
loadedVillagers.clear();
}
}

View File

@ -0,0 +1,59 @@
package com.alttd.objects;
import org.bukkit.inventory.ItemStack;
import java.util.HashSet;
import java.util.Set;
public class VillagerType {
private static final Set<VillagerType> villagerTypes = new HashSet<>();
public static Set<VillagerType> getVillagerTypes() {
return villagerTypes;
}
public static VillagerType getVillagerType(String name) {
return villagerTypes.stream().filter(villagerType -> villagerType.getName().equals(name)).findFirst().orElse(null);
}
public static void addVillagerType(VillagerType villagerType) {
villagerTypes.add(villagerType);
}
public static void clearVillagerTypes() {
villagerTypes.clear();
}
private final String name;
private final String displayName;
private final Set<ItemStack> buying;
private final Set<ItemStack> selling;
private final double priceModifier;
public VillagerType(String name, String displayName, Set<ItemStack> buying, Set<ItemStack> selling, double priceModifier) {
this.name = name;
this.displayName = displayName;
this.buying = buying;
this.selling = selling;
this.priceModifier = priceModifier;
}
public String getName() {
return name;
}
public String getDisplayName() {
return displayName;
}
public Set<ItemStack> getBuying() {
return buying;
}
public Set<ItemStack> getSelling() {
return selling;
}
public double getPriceModifier() {
return priceModifier;
}
}

View File

@ -0,0 +1,25 @@
package com.alttd.util;
public class Utilities {
/**
* Rounds num down to precision (rounds up if last cut off decimal is bigger than 4)
*
* @param num value to be rounded
* @param precision length to round to
* @return num rounded
*/
public static double round(double num, int precision) {
double scale = Math.pow(10, precision);
double total = (double) (Math.round(num * scale)) / scale;
scale = (int) Math.pow(10, precision + 1);
long tmp = (Math.round(num * scale));
while (tmp > 10)
tmp /= 10;
if (tmp > 4)
total += 0.01;
return total;
}
}

View File

@ -0,0 +1,9 @@
name: VillagerShopUI
version: ${project.version}
main: com.alttd.VillagerUI
api-version: 1.17
commands:
villagerui:
permission: villagerui.use
description: Handles all villagerui commands
usage: /villagerui