package com.forgeessentials.economy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.UUID;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.common.config.ConfigCategory;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import net.minecraftforge.event.CommandEvent;
import net.minecraftforge.event.entity.living.LivingDeathEvent;
import net.minecraftforge.event.entity.player.PlayerPickupXpEvent;
import org.apache.commons.lang3.StringUtils;
import com.forgeessentials.api.APIRegistry;
import com.forgeessentials.api.UserIdent;
import com.forgeessentials.api.economy.Economy;
import com.forgeessentials.api.economy.Wallet;
import com.forgeessentials.core.ForgeEssentials;
import com.forgeessentials.core.misc.FECommandManager;
import com.forgeessentials.core.misc.TranslatedCommandException;
import com.forgeessentials.core.misc.Translator;
import com.forgeessentials.core.moduleLauncher.FEModule;
import com.forgeessentials.core.moduleLauncher.config.ConfigLoader;
import com.forgeessentials.data.v2.DataManager;
import com.forgeessentials.economy.commands.CommandCalculatePriceList;
import com.forgeessentials.economy.commands.CommandPaidCommand;
import com.forgeessentials.economy.commands.CommandPay;
import com.forgeessentials.economy.commands.CommandSell;
import com.forgeessentials.economy.commands.CommandSellCommand;
import com.forgeessentials.economy.commands.CommandTrade;
import com.forgeessentials.economy.commands.CommandWallet;
import com.forgeessentials.economy.plots.PlotManager;
import com.forgeessentials.util.output.ChatOutputHandler;
import com.forgeessentials.util.ServerUtil;
import com.forgeessentials.util.events.FEModuleEvent.FEModuleInitEvent;
import com.forgeessentials.util.events.FEModuleEvent.FEModuleServerInitEvent;
import com.forgeessentials.util.events.FEModuleEvent.FEModuleServerStopEvent;
import com.forgeessentials.util.events.ServerEventHandler;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.PlayerEvent;
import cpw.mods.fml.common.registry.GameData;
/**
* Economy module.
*
* Handles wallets for each player, transactions and plot management.
*/
@FEModule(name = "Economy", parentMod = ForgeEssentials.class)
public class ModuleEconomy extends ServerEventHandler implements Economy, ConfigLoader
{
public static final String PERM = "fe.economy";
public static final String PERM_COMMAND = PERM + ".command";
public static final String PERM_XP_MULTIPLIER = PERM + ".xpmultiplier";
public static final String PERM_STARTBUDGET = PERM + ".startbudget";
public static final String PERM_CURRENCY = PERM + ".currency";
public static final String PERM_CURRENCY_SINGULAR = PERM_CURRENCY + ".singular";
public static final String PERM_DEATHTOLL = PERM + ".deathtoll";
public static final String PERM_COMMANDPRICE = PERM + ".cmdprice";
public static final String PERM_PRICE = PERM + ".price";
public static final String CONFIG_CATEGORY = "Economy";
public static final String CATEGORY_ITEM = CONFIG_CATEGORY + Configuration.CATEGORY_SPLITTER + "ItemPrices";
public static final int DEFAULT_ITEM_PRICE = 0;
/* ------------------------------------------------------------ */
protected PlotManager plotManager;
protected HashMap<UserIdent, PlayerWallet> wallets = new HashMap<>();
/* ------------------------------------------------------------ */
/* Module events */
@SubscribeEvent
public void load(FEModuleInitEvent e)
{
APIRegistry.economy = this;
plotManager = new PlotManager();
FECommandManager.registerCommand(new CommandWallet());
FECommandManager.registerCommand(new CommandPay());
FECommandManager.registerCommand(new CommandSell());
FECommandManager.registerCommand(new CommandPaidCommand());
FECommandManager.registerCommand(new CommandSellCommand());
FECommandManager.registerCommand(new CommandTrade());
FECommandManager.registerCommand(new CommandCalculatePriceList());
}
@SubscribeEvent
public void serverStarting(FEModuleServerInitEvent e)
{
APIRegistry.perms.registerPermissionProperty(PERM_XP_MULTIPLIER, "1",
"XP to currency conversion rate (integer, a zombie drops around 5 XP, 0 to disable)");
APIRegistry.perms.registerPermissionProperty(PERM_CURRENCY, "coins", "Name of currency (plural)");
APIRegistry.perms.registerPermissionProperty(PERM_CURRENCY_SINGULAR, "coin", "Name of currency (singular)");
APIRegistry.perms.registerPermissionProperty(PERM_STARTBUDGET, "100", "Starting amount of money for players");
APIRegistry.perms.registerPermissionDescription(PERM_PRICE, "Default prices for items in economy");
APIRegistry.perms.registerPermissionProperty(PERM_DEATHTOLL, "",
"Penalty for players to pay when they die. If set to lesser than 1, value is taken as a factor of the player's wallet balance.");
PlotManager.serverStarting();
}
@SubscribeEvent
public void serverStop(FEModuleServerStopEvent e)
{
for (Entry<UserIdent, PlayerWallet> wallet : wallets.entrySet())
saveWallet(wallet.getKey().getOrGenerateUuid(), wallet.getValue());
}
/* ------------------------------------------------------------ */
/* Utility */
public static void saveWallet(UUID uuid, PlayerWallet wallet)
{
DataManager.getInstance().save(wallet, uuid.toString());
}
public static void confirmNewWalletAmount(UserIdent ident, Wallet wallet)
{
if (ident.hasPlayer())
ChatOutputHandler.chatConfirmation(ident.getPlayerMP(), Translator.format("You have now %s", wallet.toString()));
}
public static int tryRemoveItems(EntityPlayerMP player, ItemStack itemStack, int amount)
{
int foundStacks = 0;
for (int slot = 0; slot < player.inventory.mainInventory.length; slot++)
{
ItemStack stack = player.inventory.mainInventory[slot];
if (stack != null && stack.getItem() == itemStack.getItem()
&& (itemStack.getItemDamage() == -1 || stack.getItemDamage() == itemStack.getItemDamage()))
foundStacks += stack.stackSize;
}
foundStacks = amount = Math.min(foundStacks, amount);
for (int slot = 0; slot < player.inventory.mainInventory.length; slot++)
{
ItemStack stack = player.inventory.mainInventory[slot];
if (stack != null && stack.getItem() == itemStack.getItem()
&& (itemStack.getItemDamage() == -1 || stack.getItemDamage() == itemStack.getItemDamage()))
{
int removeCount = Math.min(stack.stackSize, foundStacks);
player.inventory.decrStackSize(slot, removeCount);
foundStacks -= removeCount;
}
}
return amount;
}
/* ------------------------------------------------------------ */
/* Events */
@SubscribeEvent
public void onPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event)
{
UserIdent ident = UserIdent.get(event.player);
PlayerWallet wallet = getWallet(ident);
if (wallet != null)
saveWallet(ident.getOrGenerateUuid(), wallet);
}
@SubscribeEvent
public void onXPPickup(PlayerPickupXpEvent e)
{
if (e.entityPlayer instanceof EntityPlayerMP)
{
UserIdent ident = UserIdent.get(e.entityPlayer);
double xpMultiplier = ServerUtil.parseDoubleDefault(APIRegistry.perms.getUserPermissionProperty(ident, PERM_XP_MULTIPLIER), 0);
if (xpMultiplier <= 0)
return;
PlayerWallet wallet = getWallet(ident);
wallet.add(xpMultiplier * e.orb.xpValue);
}
}
@SubscribeEvent
public void onDeath(LivingDeathEvent e)
{
if (e.entity instanceof EntityPlayerMP)
{
UserIdent ident = UserIdent.get((EntityPlayerMP) e.entity);
Long deathtoll = ServerUtil.tryParseLong(APIRegistry.perms.getUserPermissionProperty(ident, PERM_DEATHTOLL));
if (deathtoll == null || deathtoll <= 0)
return;
Wallet wallet = APIRegistry.economy.getWallet(ident);
if (deathtoll < 1)
wallet.set(wallet.get() * deathtoll);
else if (deathtoll >= 1)
wallet.set(Math.min(0, wallet.get() - deathtoll));
}
}
@SubscribeEvent(priority = EventPriority.HIGH)
public void commandEvent(CommandEvent event)
{
if (!(event.sender instanceof EntityPlayerMP))
return;
UserIdent ident = UserIdent.get((EntityPlayerMP) event.sender);
for (int i = event.parameters.length; i >= 0; i--)
{
String permission = PERM_COMMANDPRICE + '.' + event.command.getCommandName() + //
(i == 0 ? "" : ('.' + StringUtils.join(Arrays.copyOf(event.parameters, i), '.')));
Long price = ServerUtil.tryParseLong(APIRegistry.perms.getUserPermissionProperty(ident, permission));
if (price == null)
continue;
Wallet wallet = APIRegistry.economy.getWallet(ident);
if (!wallet.withdraw(price))
event.setCanceled(true);
break;
}
}
/* ------------------------------------------------------------ */
/* Economy interface */
@Override
public PlayerWallet getWallet(UserIdent ident)
{
PlayerWallet wallet = wallets.get(ident);
if (wallet == null)
wallet = DataManager.getInstance().load(PlayerWallet.class, ident.getOrGenerateUuid().toString());
if (wallet == null)
wallet = new PlayerWallet(ServerUtil.parseIntDefault(APIRegistry.perms.getUserPermissionProperty(ident, PERM_STARTBUDGET), 0));
wallets.put(ident, wallet);
return wallet;
}
@Override
public PlayerWallet getWallet(EntityPlayerMP player)
{
return getWallet(UserIdent.get(player));
}
@Override
public String currency(long value)
{
if (value == 1)
return APIRegistry.perms.getGlobalPermissionProperty(PERM_CURRENCY_SINGULAR);
else
return APIRegistry.perms.getGlobalPermissionProperty(PERM_CURRENCY);
}
@Override
public String toString(long amount)
{
return Long.toString(amount) + " " + currency(amount);
}
/* ------------------------------------------------------------ */
public static String getItemIdentifier(ItemStack itemStack)
{
String id = GameData.getItemRegistry().getNameForObject(itemStack.getItem());
if (itemStack.getItemDamage() == 0 || itemStack.getItemDamage() == 32767)
return id;
else
return id + ":" + itemStack.getItemDamage();
}
public static String getItemPricePermission(ItemStack itemStack)
{
return PERM_PRICE + "." + getItemIdentifier(itemStack);
}
public static Long getItemPrice(ItemStack itemStack, UserIdent ident)
{
return ServerUtil.tryParseLong(APIRegistry.perms.getUserPermissionProperty(ident, getItemPricePermission(itemStack)));
}
public static void setItemPrice(ItemStack itemStack, long price)
{
APIRegistry.perms.registerPermissionProperty(getItemPricePermission(itemStack), Long.toString(price));
}
@Override
public void load(Configuration config, boolean isReload)
{
if (config.hasCategory("ItemTables"))
{
ConfigCategory category = config.getCategory("ItemTables");
for (Entry<String, Property> entry : category.entrySet())
{
for (Item item : GameData.getItemRegistry().typeSafeIterable())
if (entry.getKey().equals(item.getUnlocalizedName()))
{
String id = GameData.getItemRegistry().getNameForObject(item);
config.get(CATEGORY_ITEM, id, DEFAULT_ITEM_PRICE).set(entry.getValue().getInt(DEFAULT_ITEM_PRICE));;
break;
}
}
config.removeCategory(category);
}
ConfigCategory category = config.getCategory(CATEGORY_ITEM);
for (Entry<String, Property> entry : category.entrySet())
{
APIRegistry.perms.registerPermissionProperty(PERM_PRICE + "." + entry.getKey(), Integer.toString(entry.getValue().getInt(DEFAULT_ITEM_PRICE)));
}
}
@Override
public void save(Configuration config)
{
/* do nothing */
}
@Override
public boolean supportsCanonicalConfig()
{
return false;
}
/* ------------------------------------------------------------ */
public static class CantAffordException extends TranslatedCommandException
{
public CantAffordException()
{
super("You can't afford that");
}
}
}