175 lines
8.1 KiB
Java
175 lines
8.1 KiB
Java
package com.pretzel.dev.villagertradelimiter.listeners;
|
|
|
|
import com.pretzel.dev.villagertradelimiter.VillagerTradeLimiter;
|
|
import com.pretzel.dev.villagertradelimiter.data.CustomRecipe;
|
|
import com.pretzel.dev.villagertradelimiter.data.PlayerData;
|
|
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
|
import com.pretzel.dev.villagertradelimiter.nms.NMSVillager;
|
|
import org.bukkit.*;
|
|
import org.bukkit.entity.*;
|
|
import org.bukkit.event.EventHandler;
|
|
import org.bukkit.event.Listener;
|
|
import org.bukkit.event.inventory.InventoryCloseEvent;
|
|
import org.bukkit.event.inventory.InventoryType;
|
|
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
|
import org.bukkit.inventory.*;
|
|
import org.bukkit.potion.PotionEffect;
|
|
import org.bukkit.potion.PotionEffectType;
|
|
|
|
import java.util.*;
|
|
|
|
public class PlayerListener implements Listener {
|
|
|
|
private final VillagerTradeLimiter instance;
|
|
private final HashMap<Villager, ArrayList<CustomRecipe>> customRecipes;
|
|
|
|
/**
|
|
* @param instance The instance of VillagerTradeLimiter.java
|
|
*/
|
|
public PlayerListener(final VillagerTradeLimiter instance) {
|
|
this.instance = instance;
|
|
this.customRecipes = new HashMap<>();
|
|
}
|
|
|
|
/** Handles when a player begins trading with a villager */
|
|
@EventHandler
|
|
public void onPlayerBeginTrading(final PlayerInteractEntityEvent event) {
|
|
if(event.isCancelled()) return; //Skips when the event is already cancelled
|
|
if(!(event.getRightClicked() instanceof final Villager villager)) return; //Skips non-villager entity interactions
|
|
if(villager.getRecipeCount() == 0) return; //Skips when the villager has no trades
|
|
|
|
final Player player = event.getPlayer();
|
|
final ItemStack heldItem = player.getInventory().getItem(event.getHand());
|
|
if(instance.getSettings().shouldSkipNPC(player, villager)) return; //Skips when the player or villager is a Citizens NPC or Shopkeeper
|
|
if(heldItem != null && instance.getSettings().getIgnoreHeldItems().contains(heldItem.getType().name().toLowerCase())) return; //Skips when player is holding an ignored item
|
|
if(instance.getSettings().getIgnoreWorlds().contains(villager.getWorld().getName())) return; //Skips when the villager is in an ignored world
|
|
|
|
//Skips and cancels the event when the player is in a disabled world
|
|
if(instance.getSettings().getDisableTrading().contains(player.getWorld().getName())) {
|
|
Util.sendMsg(instance.getLang("trading.noworld"), player);
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
|
|
//Skips and cancels the event when the villager has a disabled profession
|
|
String profession = villager.getProfession().name().toLowerCase();
|
|
if(instance.getSettings().getDisableProfessions().contains(profession)) {
|
|
Util.sendMsg(instance.getLang("trading.noprofession").replace("{profession}", profession), player);
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
|
|
//Cancel the original event, and open the adjusted trade view
|
|
event.setCancelled(true);
|
|
instance.getPlayerData().putIfAbsent(player.getUniqueId(), new PlayerData());
|
|
instance.getPlayerData().putIfAbsent(villager.getUniqueId(), new PlayerData());
|
|
this.see(villager, player, player);
|
|
}
|
|
|
|
/** Handles when a player stops trading with a villager */
|
|
@EventHandler
|
|
public void onPlayerStopTrading(final InventoryCloseEvent event) {
|
|
//Don't do anything unless the player is actually finished trading with a villager
|
|
if(event.getInventory().getType() != InventoryType.MERCHANT) return; //Skips non-merchant inventories
|
|
if(!(event.getPlayer() instanceof final Player player)) return; //Skips non-players
|
|
if(!(event.getInventory().getHolder() instanceof final Villager villager)) return; //Skips non-villagers
|
|
if(instance.getSettings().shouldSkipNPC(player, villager)) return; //Skips NPCs
|
|
|
|
//Reset the villager's recipes to vanilla when a player is finished trading
|
|
resetVillager(villager);
|
|
}
|
|
|
|
private void resetVillager(final Villager villager) {
|
|
if(!customRecipes.containsKey(villager)) return;
|
|
ArrayList<CustomRecipe> recipes = customRecipes.get(villager);
|
|
for(int i = 0; i < recipes.size(); i++) {
|
|
recipes.get(i).reset();
|
|
villager.setRecipe(i, recipes.get(i).getOriginal());
|
|
}
|
|
}
|
|
|
|
public void onDisable() {
|
|
//Reset the villager's recipes to vanilla when the plugin is disabled
|
|
for(Villager villager : customRecipes.keySet()) resetVillager(villager);
|
|
customRecipes.clear();
|
|
}
|
|
|
|
/**
|
|
* Opens the villager's trading menu, with the adjusted trades of another player (or the same player)
|
|
* @param villager The villager whose trades you want to see
|
|
* @param player The player who calls the command, or the player that has begun trading
|
|
* @param other The other player to view trades for, or the player that has just begun trading
|
|
*/
|
|
public void see(final Villager villager, final Player player, final OfflinePlayer other) {
|
|
//Skips when the villager is in a disabled world
|
|
if(instance.getSettings().getDisableTrading().contains(villager.getWorld().getName())) {
|
|
Util.sendMsg(instance.getLang("see.noworld"), player);
|
|
return;
|
|
}
|
|
|
|
//Wraps the villager and player into wrapper classes
|
|
if(other == null || instance.getSettings().shouldSkipNPC(player, villager)) return; //Skips NPCs
|
|
|
|
//Calculates the player's total reputation and Hero of the Village discount
|
|
final NMSVillager nmsVillager = new NMSVillager(villager);
|
|
int totalReputation = nmsVillager.getTotalReputation(other);
|
|
double hotvDiscount = getHotvDiscount(other);
|
|
|
|
//Adjusts the recipe prices, MaxUses, and ingredients
|
|
final ArrayList<MerchantRecipe> originalRecipes = new ArrayList<>(villager.getRecipes());
|
|
final ArrayList<CustomRecipe> recipes = new ArrayList<>();
|
|
for(int i = 0; i < originalRecipes.size(); i++) {
|
|
recipes.add(new CustomRecipe(instance, other, villager, originalRecipes.get(i)));
|
|
villager.setRecipe(i, recipes.get(i).getAdjusted());
|
|
}
|
|
customRecipes.put(villager, recipes);
|
|
|
|
//Opens the trading window
|
|
boolean multipleTraders = instance.getSettings().getMultipleTraders();
|
|
int delay = instance.getSettings().getTradeDelay();
|
|
if(delay >= 0) {
|
|
Bukkit.getScheduler().runTaskLater(instance, () -> player.openMerchant(villager, multipleTraders), delay);
|
|
} else {
|
|
player.openMerchant(villager, multipleTraders);
|
|
}
|
|
|
|
//Updates the prices
|
|
for(int i = 0; i < villager.getRecipes().size(); i++) {
|
|
int discount = recipes.get(i).getDiscount(totalReputation, hotvDiscount);
|
|
villager.getRecipe(i).setSpecialPrice(discount);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @param other The player to check the hotv effect for
|
|
* @return The Hero of the Village discount factor, adjusted by config
|
|
*/
|
|
private double getHotvDiscount(final OfflinePlayer other) {
|
|
final Player player = other.getPlayer();
|
|
if(player == null) return 0.0;
|
|
|
|
final PotionEffectType effectType = PotionEffectType.HERO_OF_THE_VILLAGE;
|
|
if(!player.hasPotionEffect(effectType)) return 0.0;
|
|
|
|
final PotionEffect effect = player.getPotionEffect(effectType);
|
|
if(effect == null) return 0.0;
|
|
|
|
//Calculates the discount factor from the player's current effect level or the defined maximum
|
|
int heroLevel = effect.getAmplifier()+1;
|
|
final int maxHeroLevel = instance.getGroupManager().getGroup(other).getMaxHeroLevel();;
|
|
if(maxHeroLevel == 0 || heroLevel == 0) return 0.0;
|
|
if(maxHeroLevel > 0 && heroLevel > maxHeroLevel) {
|
|
heroLevel = maxHeroLevel;
|
|
}
|
|
return 0.0625*(heroLevel-1) + 0.3;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param villager The villager
|
|
* @return The list of original recipes for the villager
|
|
*/
|
|
public ArrayList<CustomRecipe> getCustomRecipes(final Villager villager) { return this.customRecipes.get(villager); }
|
|
}
|