VillagerTradeLimiter/src/com/pretzel/dev/villagertradelimiter/listeners/PlayerListener.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); }
}