forked from Upstream/VillagerTradeLimiter
Version 1.5.0-pre4:
* Add per-villager restock cooldowns To-do: * Add enchantments, data, etc. to ingredients and result * Add config editor GUI in-game
This commit is contained in:
parent
3aba0ff1c7
commit
8720e840e2
@ -5,6 +5,7 @@ import com.pretzel.dev.villagertradelimiter.commands.CommandBase;
|
|||||||
import com.pretzel.dev.villagertradelimiter.data.PlayerData;
|
import com.pretzel.dev.villagertradelimiter.data.PlayerData;
|
||||||
import com.pretzel.dev.villagertradelimiter.database.DatabaseManager;
|
import com.pretzel.dev.villagertradelimiter.database.DatabaseManager;
|
||||||
import com.pretzel.dev.villagertradelimiter.listeners.InventoryListener;
|
import com.pretzel.dev.villagertradelimiter.listeners.InventoryListener;
|
||||||
|
import com.pretzel.dev.villagertradelimiter.listeners.VillagerListener;
|
||||||
import com.pretzel.dev.villagertradelimiter.settings.ConfigUpdater;
|
import com.pretzel.dev.villagertradelimiter.settings.ConfigUpdater;
|
||||||
import com.pretzel.dev.villagertradelimiter.lib.Metrics;
|
import com.pretzel.dev.villagertradelimiter.lib.Metrics;
|
||||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||||
@ -76,6 +77,7 @@ public class VillagerTradeLimiter extends JavaPlugin {
|
|||||||
|
|
||||||
//Load/reload database manager
|
//Load/reload database manager
|
||||||
if(this.databaseManager == null) this.databaseManager = new DatabaseManager(this);
|
if(this.databaseManager == null) this.databaseManager = new DatabaseManager(this);
|
||||||
|
else onDisable();
|
||||||
this.databaseManager.load();
|
this.databaseManager.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +101,7 @@ public class VillagerTradeLimiter extends JavaPlugin {
|
|||||||
this.playerListener = new PlayerListener(this, settings);
|
this.playerListener = new PlayerListener(this, settings);
|
||||||
this.getServer().getPluginManager().registerEvents(this.playerListener, this);
|
this.getServer().getPluginManager().registerEvents(this.playerListener, this);
|
||||||
this.getServer().getPluginManager().registerEvents(new InventoryListener(this, settings), this);
|
this.getServer().getPluginManager().registerEvents(new InventoryListener(this, settings), this);
|
||||||
|
this.getServer().getPluginManager().registerEvents(new VillagerListener(this, settings), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,10 +39,18 @@ public class Cooldown {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param date The date to format
|
||||||
|
* @return The date as a 'yyyy-MM-dd HH:mm:ss' formatted string
|
||||||
|
*/
|
||||||
public static String formatTime(Date date) {
|
public static String formatTime(Date date) {
|
||||||
return FORMAT.format(date);
|
return FORMAT.format(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param timeStr The string to format, in 'yyyy-MM-dd HH:mm:ss' format
|
||||||
|
* @return The date that the string represents
|
||||||
|
*/
|
||||||
public static Date parseTime(final String timeStr) {
|
public static Date parseTime(final String timeStr) {
|
||||||
try {
|
try {
|
||||||
return FORMAT.parse(timeStr);
|
return FORMAT.parse(timeStr);
|
||||||
|
@ -4,7 +4,9 @@ import com.pretzel.dev.villagertradelimiter.VillagerTradeLimiter;
|
|||||||
import com.pretzel.dev.villagertradelimiter.data.Cooldown;
|
import com.pretzel.dev.villagertradelimiter.data.Cooldown;
|
||||||
import com.pretzel.dev.villagertradelimiter.data.PlayerData;
|
import com.pretzel.dev.villagertradelimiter.data.PlayerData;
|
||||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.entity.Villager;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -52,18 +54,20 @@ public class DatabaseManager {
|
|||||||
if(date == null) continue;
|
if(date == null) continue;
|
||||||
long time = date.getTime();
|
long time = date.getTime();
|
||||||
|
|
||||||
PlayerData playerData = instance.getPlayerData().get(uuid);
|
PlayerData data = instance.getPlayerData().get(uuid);
|
||||||
if(playerData == null) {
|
if(data == null) {
|
||||||
playerData = new PlayerData();
|
data = new PlayerData();
|
||||||
instance.getPlayerData().put(uuid, playerData);
|
instance.getPlayerData().put(uuid, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String key = (Bukkit.getEntity(uuid) instanceof Villager ? "Restock" : "Cooldown");
|
||||||
|
String cooldownStr = instance.getCfg().getString(key, "0");
|
||||||
|
cooldownStr = instance.getCfg().getString("Overrides."+item+"."+key, cooldownStr);
|
||||||
|
long cooldown = Cooldown.parseCooldown(cooldownStr);
|
||||||
|
|
||||||
final Date now = Date.from(Instant.now());
|
final Date now = Date.from(Instant.now());
|
||||||
final String global = instance.getCfg().getString("Cooldown", "0");
|
|
||||||
final String local = instance.getCfg().getString("Overrides."+item+".Cooldown", global);
|
|
||||||
long cooldown = Cooldown.parseCooldown(local);
|
|
||||||
if(cooldown != 0 && now.getTime()/1000L < time/1000L + cooldown) {
|
if(cooldown != 0 && now.getTime()/1000L < time/1000L + cooldown) {
|
||||||
playerData.getTradingCooldowns().put(item, Cooldown.formatTime(date));
|
data.getTradingCooldowns().put(item, Cooldown.formatTime(date));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,7 +92,7 @@ public class DatabaseManager {
|
|||||||
|
|
||||||
String values = "";
|
String values = "";
|
||||||
for(String item : playerData.getTradingCooldowns().keySet()) {
|
for(String item : playerData.getTradingCooldowns().keySet()) {
|
||||||
String time = playerData.getTradingCooldowns().get(item);
|
final String time = playerData.getTradingCooldowns().get(item);
|
||||||
|
|
||||||
if(!values.isEmpty()) values += ",";
|
if(!values.isEmpty()) values += ",";
|
||||||
values += "('"+uuid+"','"+item+"','"+time+"')";
|
values += "('"+uuid+"','"+item+"','"+time+"')";
|
||||||
|
@ -79,13 +79,12 @@ public class InventoryListener implements Listener {
|
|||||||
if(overrides == null) return;
|
if(overrides == null) return;
|
||||||
|
|
||||||
final String type = settings.getType(result, ingredient1, ingredient2);
|
final String type = settings.getType(result, ingredient1, ingredient2);
|
||||||
if(instance.getCfg().getString("Cooldown", "0").equals("0")) {
|
String cooldownStr = instance.getCfg().getString("Cooldown", "0");
|
||||||
Util.consoleMsg("global cooldown is 0");
|
cooldownStr = overrides.getString(type+".Cooldown", cooldownStr);
|
||||||
if(overrides.getString(type+".Cooldown", "0").equals("0")) {
|
String restockStr = instance.getCfg().getString("Restock", "0");
|
||||||
Util.consoleMsg("local cooldown is 0");
|
restockStr = overrides.getString(type+".Restock", restockStr);
|
||||||
return;
|
|
||||||
}
|
if(cooldownStr.equals("0") && restockStr.equals("0")) return;
|
||||||
}
|
|
||||||
|
|
||||||
//Get the selected recipe by the items in the slots
|
//Get the selected recipe by the items in the slots
|
||||||
final MerchantRecipe selectedRecipe = getSelectedRecipe((Villager)event.getInventory().getHolder(), ingredient1, ingredient2, result);
|
final MerchantRecipe selectedRecipe = getSelectedRecipe((Villager)event.getInventory().getHolder(), ingredient1, ingredient2, result);
|
||||||
@ -96,11 +95,18 @@ public class InventoryListener implements Listener {
|
|||||||
|
|
||||||
//Add a cooldown to the trade if the player has reached the max uses
|
//Add a cooldown to the trade if the player has reached the max uses
|
||||||
final PlayerData playerData = instance.getPlayerData().get(player.getUniqueId());
|
final PlayerData playerData = instance.getPlayerData().get(player.getUniqueId());
|
||||||
|
final PlayerData villagerData = instance.getPlayerData().get(((Villager)event.getInventory().getHolder()).getUniqueId());
|
||||||
if(playerData == null || playerData.getTradingVillager() == null) return;
|
if(playerData == null || playerData.getTradingVillager() == null) return;
|
||||||
Bukkit.getScheduler().runTaskLater(instance, () -> {
|
Bukkit.getScheduler().runTaskLater(instance, () -> {
|
||||||
int uses = selectedRecipe.getUses();
|
int uses = selectedRecipe.getUses();
|
||||||
if(!playerData.getTradingCooldowns().containsKey(type) && uses >= selectedRecipe.getMaxUses()) {
|
final String time = Cooldown.formatTime(Date.from(Instant.now()));
|
||||||
playerData.getTradingCooldowns().put(type, Cooldown.formatTime(Date.from(Instant.now())));
|
if(uses >= selectedRecipe.getMaxUses()) {
|
||||||
|
if(!playerData.getTradingCooldowns().containsKey(type)) {
|
||||||
|
playerData.getTradingCooldowns().put(type, time);
|
||||||
|
}
|
||||||
|
if(villagerData != null && !villagerData.getTradingCooldowns().containsKey(type)) {
|
||||||
|
villagerData.getTradingCooldowns().put(type, time);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,9 @@ public class PlayerListener implements Listener {
|
|||||||
if(!instance.getPlayerData().containsKey(player.getUniqueId())) {
|
if(!instance.getPlayerData().containsKey(player.getUniqueId())) {
|
||||||
instance.getPlayerData().put(player.getUniqueId(), new PlayerData());
|
instance.getPlayerData().put(player.getUniqueId(), new PlayerData());
|
||||||
}
|
}
|
||||||
|
if(!instance.getPlayerData().containsKey(villager.getUniqueId())) {
|
||||||
|
instance.getPlayerData().put(villager.getUniqueId(), new PlayerData());
|
||||||
|
}
|
||||||
this.see(villager, player, player);
|
this.see(villager, player, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,6 +146,7 @@ public class PlayerListener implements Listener {
|
|||||||
* @return The total discount for the recipe, which is added to the base price to get the final price
|
* @return The total discount for the recipe, which is added to the base price to get the final price
|
||||||
*/
|
*/
|
||||||
private int getDiscount(final RecipeWrapper recipe, int totalReputation, double hotvDiscount) {
|
private int getDiscount(final RecipeWrapper recipe, int totalReputation, double hotvDiscount) {
|
||||||
|
//Calculates the total discount
|
||||||
int basePrice = getBasePrice(recipe);
|
int basePrice = getBasePrice(recipe);
|
||||||
int demand = getDemand(recipe);
|
int demand = getDemand(recipe);
|
||||||
float priceMultiplier = recipe.getPriceMultiplier();
|
float priceMultiplier = recipe.getPriceMultiplier();
|
||||||
@ -150,10 +154,12 @@ public class PlayerListener implements Listener {
|
|||||||
|
|
||||||
double maxDiscount = settings.fetchDouble(recipe, "MaxDiscount", 0.3);
|
double maxDiscount = settings.fetchDouble(recipe, "MaxDiscount", 0.3);
|
||||||
if(maxDiscount >= 0.0 && maxDiscount <= 1.0) {
|
if(maxDiscount >= 0.0 && maxDiscount <= 1.0) {
|
||||||
|
//Change the discount to the smaller MaxDiscount
|
||||||
if(basePrice + discount < basePrice * (1.0 - maxDiscount)) {
|
if(basePrice + discount < basePrice * (1.0 - maxDiscount)) {
|
||||||
discount = -(int)(basePrice * maxDiscount);
|
discount = -(int)(basePrice * maxDiscount);
|
||||||
}
|
}
|
||||||
} else if(maxDiscount > 1.0) {
|
} else if(maxDiscount > 1.0) {
|
||||||
|
//Change the discount to the larger MaxDiscount
|
||||||
//TODO: Allow for better fine-tuning
|
//TODO: Allow for better fine-tuning
|
||||||
discount = (int)(discount * maxDiscount);
|
discount = (int)(discount * maxDiscount);
|
||||||
}
|
}
|
||||||
@ -169,6 +175,7 @@ public class PlayerListener implements Listener {
|
|||||||
int maxUses = settings.fetchInt(recipe, "MaxUses", -1);
|
int maxUses = settings.fetchInt(recipe, "MaxUses", -1);
|
||||||
boolean disabled = settings.fetchBoolean(recipe, "Disabled", false);
|
boolean disabled = settings.fetchBoolean(recipe, "Disabled", false);
|
||||||
|
|
||||||
|
//Disables the trade if the player has an active cooldown for the trade
|
||||||
final PlayerData playerData = instance.getPlayerData().get(player.getUniqueId());
|
final PlayerData playerData = instance.getPlayerData().get(player.getUniqueId());
|
||||||
if(playerData != null && playerData.getTradingVillager() != null) {
|
if(playerData != null && playerData.getTradingVillager() != null) {
|
||||||
final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides");
|
final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides");
|
||||||
@ -210,6 +217,7 @@ public class PlayerListener implements Listener {
|
|||||||
final PotionEffect effect = player.getPotionEffect(effectType);
|
final PotionEffect effect = player.getPotionEffect(effectType);
|
||||||
if(effect == null) return 0.0;
|
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;
|
int heroLevel = effect.getAmplifier()+1;
|
||||||
final int maxHeroLevel = instance.getCfg().getInt("MaxHeroLevel", -1);
|
final int maxHeroLevel = instance.getCfg().getInt("MaxHeroLevel", -1);
|
||||||
if(maxHeroLevel == 0 || heroLevel == 0) return 0.0;
|
if(maxHeroLevel == 0 || heroLevel == 0) return 0.0;
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
package com.pretzel.dev.villagertradelimiter.listeners;
|
||||||
|
|
||||||
|
import com.pretzel.dev.villagertradelimiter.VillagerTradeLimiter;
|
||||||
|
import com.pretzel.dev.villagertradelimiter.data.Cooldown;
|
||||||
|
import com.pretzel.dev.villagertradelimiter.data.PlayerData;
|
||||||
|
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||||
|
import com.pretzel.dev.villagertradelimiter.settings.Settings;
|
||||||
|
import org.bukkit.entity.Villager;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.entity.VillagerReplenishTradeEvent;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.MerchantRecipe;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class VillagerListener implements Listener {
|
||||||
|
private final VillagerTradeLimiter instance;
|
||||||
|
private final Settings settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param instance The instance of VillagerTradeLimiter.java
|
||||||
|
* @param settings The settings instance
|
||||||
|
*/
|
||||||
|
public VillagerListener(final VillagerTradeLimiter instance, final Settings settings) {
|
||||||
|
this.instance = instance;
|
||||||
|
this.settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handles villager restocks */
|
||||||
|
@EventHandler
|
||||||
|
public void onVillagerRestock(final VillagerReplenishTradeEvent event) {
|
||||||
|
if(!(event.getEntity() instanceof Villager)) return;
|
||||||
|
if(Util.isNPC((Villager) event.getEntity())) return;
|
||||||
|
|
||||||
|
//Get the items involved in the restock
|
||||||
|
final MerchantRecipe recipe = event.getRecipe();
|
||||||
|
final ItemStack result = recipe.getResult();
|
||||||
|
ItemStack ingredient1 = recipe.getIngredients().get(0);
|
||||||
|
ItemStack ingredient2 = recipe.getIngredients().get(1);
|
||||||
|
final String type = settings.getType(result, ingredient1, ingredient2);
|
||||||
|
|
||||||
|
//Get the villager's data container
|
||||||
|
final UUID uuid = event.getEntity().getUniqueId();
|
||||||
|
final PlayerData villagerData = instance.getPlayerData().get(uuid);
|
||||||
|
if(villagerData == null) return;
|
||||||
|
|
||||||
|
//Get the time of the last trade, restock cooldown setting, and now
|
||||||
|
final String lastTradeStr = villagerData.getTradingCooldowns().get(type);
|
||||||
|
if(lastTradeStr == null) return;
|
||||||
|
|
||||||
|
String cooldownStr = instance.getCfg().getString("Restock", "0");
|
||||||
|
cooldownStr = instance.getCfg().getString("Overrides."+type+".Restock", cooldownStr);
|
||||||
|
|
||||||
|
final Date now = Date.from(Instant.now());
|
||||||
|
final Date lastTrade = Cooldown.parseTime(lastTradeStr);
|
||||||
|
if(lastTrade == null) return;
|
||||||
|
final long cooldown = Cooldown.parseCooldown(cooldownStr);
|
||||||
|
|
||||||
|
//Cancel the event if there is an active restock cooldown, otherwise remove the restock cooldown
|
||||||
|
if(now.getTime()/1000L >= lastTrade.getTime()/1000L + cooldown) {
|
||||||
|
villagerData.getTradingCooldowns().remove(type);
|
||||||
|
} else {
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -61,6 +61,12 @@ MaxUses: -1
|
|||||||
# * w = weeks (e.g. 2w)
|
# * w = weeks (e.g. 2w)
|
||||||
Cooldown: 0
|
Cooldown: 0
|
||||||
|
|
||||||
|
# The per-villager, per-trade cooldown in real-world time.
|
||||||
|
# This is the same as Cooldown, but applies to a villager's restocking function
|
||||||
|
# * Set to 0 to disable this feature and keep vanilla behavior
|
||||||
|
# * Set to a number and interval to add a per-villager, per-trade cooldown for all trades (see below)
|
||||||
|
Restock: 0
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------- PER-ITEM SETTINGS --------------------------------#
|
#-------------------------------- PER-ITEM SETTINGS --------------------------------#
|
||||||
# Override the global settings for individual items. To disable, set like this --> Overrides: none
|
# Override the global settings for individual items. To disable, set like this --> Overrides: none
|
||||||
@ -91,4 +97,5 @@ Overrides:
|
|||||||
clock:
|
clock:
|
||||||
MaxDemand: 12
|
MaxDemand: 12
|
||||||
paper:
|
paper:
|
||||||
Disabled: true
|
MaxUses: 1
|
||||||
|
Restock: 1h
|
Loading…
Reference in New Issue
Block a user