Adds an option which mimics the vanilla book copying behavior
This commit is contained in:
parent
a7c284ade2
commit
f243bf32e7
@ -6,6 +6,7 @@ import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.EconomyHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.TabCompletionHelper;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
@ -40,33 +41,108 @@ public class CommandCopy implements TabExecutor {
|
||||
return false;
|
||||
}
|
||||
|
||||
ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
|
||||
//TODO: Rewrite this so the resulting book becomes a COPY, or COPY OF COPY
|
||||
|
||||
|
||||
try {
|
||||
ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
|
||||
int copies = Integer.parseInt(args[0]);
|
||||
if (copies > 0) {
|
||||
if (BooksWithoutBordersConfig.getAuthorOnlyCopy() && !player.hasPermission("bookswithoutborders.bypassAuthorOnlyCopy")) {
|
||||
if (BookHelper.isNotAuthor(player, (BookMeta) Objects.requireNonNull(heldBook.getItemMeta()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (BooksWithoutBordersConfig.booksHavePrice() &&
|
||||
!player.hasPermission("bookswithoutborders.bypassBookPrice") &&
|
||||
EconomyHelper.cannotPayForBookPrinting(player, copies)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
heldBook.setAmount(heldBook.getAmount() + copies);
|
||||
BooksWithoutBorders.sendSuccessMessage(player, "Book copied!");
|
||||
return true;
|
||||
if (copies <= 0) {
|
||||
throw new NumberFormatException("Number of copies must be larger than 0");
|
||||
}
|
||||
return performCopy(copies, player, heldBook);
|
||||
} catch (NumberFormatException ignored) {
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Book not copied!");
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Number specified was invalid!");
|
||||
return false;
|
||||
}
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Book not copied!");
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Number specified was invalid!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual copying
|
||||
*
|
||||
* @param copies <p>The number of copies to be made</p>
|
||||
* @param player <p>The player requesting the copies</p>
|
||||
* @param heldBook <p>The book to be copied</p>
|
||||
* @return <p>True if the copying was successful</p>
|
||||
*/
|
||||
private boolean performCopy(int copies, Player player, ItemStack heldBook) {
|
||||
//Make sure the player owns the book if authorOnlyCopy is enabled
|
||||
if (BooksWithoutBordersConfig.getAuthorOnlyCopy() &&
|
||||
!player.hasPermission("bookswithoutborders.bypassAuthorOnlyCopy")) {
|
||||
if (BookHelper.isNotAuthor(player, (BookMeta) Objects.requireNonNull(heldBook.getItemMeta()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Make sure the player can pay for the copying
|
||||
if (BooksWithoutBordersConfig.booksHavePrice() &&
|
||||
!player.hasPermission("bookswithoutborders.bypassBookPrice") &&
|
||||
EconomyHelper.cannotPayForBookPrinting(player, copies)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BookMeta bookMeta = (BookMeta) heldBook.getItemMeta();
|
||||
if (BooksWithoutBordersConfig.changeGenerationOnCopy() && bookMeta != null && bookMeta.hasGeneration()) {
|
||||
return copyNextGenerationBook(bookMeta, player, copies);
|
||||
} else {
|
||||
//Make sure the player can pay for the copying
|
||||
if (paymentUnSuccessful(player, copies)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
heldBook.setAmount(heldBook.getAmount() + copies);
|
||||
BooksWithoutBorders.sendSuccessMessage(player, "Book copied!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to take payment from a player
|
||||
*
|
||||
* @param player <p>The player to take the payment from</p>
|
||||
* @param copies <p>The number of copies to create for the player</p>
|
||||
* @return <p>True if the payment failed</p>
|
||||
*/
|
||||
private boolean paymentUnSuccessful(Player player, int copies) {
|
||||
return BooksWithoutBordersConfig.booksHavePrice() &&
|
||||
!player.hasPermission("bookswithoutborders.bypassBookPrice") &&
|
||||
EconomyHelper.cannotPayForBookPrinting(player, copies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a book with the next generation relative to the input book
|
||||
*
|
||||
* @param bookMeta <p>The book to copy</p>
|
||||
* @param player <p>The player copying the book</p>
|
||||
* @param copies <p>The number of copies requested</p>
|
||||
* @return <p>True if the book was successfully copied</p>
|
||||
*/
|
||||
private boolean copyNextGenerationBook(BookMeta bookMeta, Player player, int copies) {
|
||||
//Copy the vanilla behavior of refusing copying any further
|
||||
if (bookMeta.getGeneration() == BookMeta.Generation.COPY_OF_COPY) {
|
||||
player.sendMessage("You cannot copy this book any further. You must have the original or a direct copy.");
|
||||
return false;
|
||||
}
|
||||
//Make sure the player can fit the book in their inventory
|
||||
int nextAvailableSlot = player.getInventory().firstEmpty();
|
||||
if (nextAvailableSlot == -1) {
|
||||
player.sendMessage("You need an available slot in your inventory.");
|
||||
return false;
|
||||
}
|
||||
//Make sure the player can pay for the copying
|
||||
if (paymentUnSuccessful(player, copies)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ItemStack itemStack = new ItemStack(Material.WRITTEN_BOOK);
|
||||
itemStack.setItemMeta(bookMeta);
|
||||
//Increase the generation of the book
|
||||
BookHelper.increaseGeneration(itemStack);
|
||||
itemStack.setAmount(copies);
|
||||
|
||||
player.getInventory().addItem(itemStack);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,6 +69,20 @@ public class CommandGive implements TabExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
//Try and find the target player
|
||||
Player receivingPlayer = booksWithoutBorders.getServer().getPlayer(receivingPlayerName);
|
||||
if (receivingPlayer == null) {
|
||||
BooksWithoutBorders.sendErrorMessage(sender, "Player not found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
//Make sure the receiver is able to fit the book
|
||||
if (receivingPlayer.getInventory().firstEmpty() == -1) {
|
||||
BooksWithoutBorders.sendErrorMessage(sender, "Receiving player must have space in their inventory" +
|
||||
" to receive books!");
|
||||
return false;
|
||||
}
|
||||
|
||||
//Load books available to the player
|
||||
try {
|
||||
Integer.parseInt(bookIdentifier);
|
||||
@ -76,29 +90,8 @@ public class CommandGive implements TabExecutor {
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
|
||||
Player receivingPlayer = booksWithoutBorders.getServer().getPlayer(receivingPlayerName);
|
||||
if (receivingPlayer == null) {
|
||||
BooksWithoutBorders.sendErrorMessage(sender, "Player not found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (receivingPlayer.getInventory().firstEmpty() == -1) {
|
||||
BooksWithoutBorders.sendErrorMessage(sender, "Receiving player must have space in their inventory to receive books!");
|
||||
return false;
|
||||
}
|
||||
|
||||
String bookToLoad = InputCleaningHelper.cleanString(bookIdentifier);
|
||||
try {
|
||||
ItemStack newBook = BookLoader.loadBook(sender, bookToLoad, isSigned, folder, Integer.parseInt(copies));
|
||||
if (newBook != null) {
|
||||
receivingPlayer.getInventory().addItem(newBook);
|
||||
BooksWithoutBorders.sendSuccessMessage(sender, "Book sent!");
|
||||
BooksWithoutBorders.sendSuccessMessage(receivingPlayer, "Book received!");
|
||||
return true;
|
||||
} else {
|
||||
BooksWithoutBorders.sendErrorMessage(sender, "Book failed to load!");
|
||||
return false;
|
||||
}
|
||||
return loadAndGiveBook(bookIdentifier, sender, receivingPlayer, isSigned, folder, copies);
|
||||
} catch (NumberFormatException e) {
|
||||
BooksWithoutBorders.sendErrorMessage(sender, "Invalid number of book copies specified!");
|
||||
return false;
|
||||
@ -152,4 +145,31 @@ public class CommandGive implements TabExecutor {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a book and gives it to the correct player
|
||||
*
|
||||
* @param bookIdentifier <p>The file name specified by the user</p>
|
||||
* @param sender <p>The player trying to give the book</p>
|
||||
* @param receivingPlayer <p>The player which is the receiver of the book</p>
|
||||
* @param isSigned <p>The value given for if the given book should be signed or not</p>
|
||||
* @param folder <p>The folder containing the book to load</p>
|
||||
* @param copies <p>The number of copies the player wants to give</p>
|
||||
* @return <p>True if the book was successfully given</p>
|
||||
*/
|
||||
private boolean loadAndGiveBook(String bookIdentifier, CommandSender sender, Player receivingPlayer,
|
||||
String isSigned, String folder, String copies) throws NumberFormatException {
|
||||
String bookToLoad = InputCleaningHelper.cleanString(bookIdentifier);
|
||||
ItemStack newBook = BookLoader.loadBook(sender, bookToLoad, isSigned, folder, Integer.parseInt(copies));
|
||||
if (newBook != null) {
|
||||
//NOTE: As this method bypasses cost, it should also bypass the generation change
|
||||
receivingPlayer.getInventory().addItem(newBook);
|
||||
BooksWithoutBorders.sendSuccessMessage(sender, "Book sent!");
|
||||
BooksWithoutBorders.sendSuccessMessage(receivingPlayer, "Book received!");
|
||||
return true;
|
||||
} else {
|
||||
BooksWithoutBorders.sendErrorMessage(sender, "Book failed to load!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ public class BooksWithoutBordersConfig {
|
||||
private static boolean useYml;
|
||||
private static boolean adminDecrypt;
|
||||
private static boolean formatBooks;
|
||||
private static boolean changeGenerationOnCopy;
|
||||
|
||||
/**
|
||||
* Initializes the books without borders settings class
|
||||
@ -194,6 +195,15 @@ public class BooksWithoutBordersConfig {
|
||||
return bookDuplicateLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether books should change their generation during copy
|
||||
*
|
||||
* @return <p>True if books should change their generation</p>
|
||||
*/
|
||||
public static boolean changeGenerationOnCopy() {
|
||||
return changeGenerationOnCopy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the separator used to split book title from book author
|
||||
*
|
||||
@ -268,6 +278,7 @@ public class BooksWithoutBordersConfig {
|
||||
config.set(ConfigOption.ADMIN_AUTO_DECRYPT.getConfigNode(), adminDecrypt);
|
||||
config.set(ConfigOption.AUTHOR_ONLY_COPY.getConfigNode(), authorOnlyCopy);
|
||||
config.set(ConfigOption.AUTHOR_ONLY_UNSIGN.getConfigNode(), authorOnlyUnsign);
|
||||
config.set(ConfigOption.CHANGE_GENERATION_ON_COPY.getConfigNode(), changeGenerationOnCopy);
|
||||
|
||||
//Handles old book and quill settings
|
||||
if (config.contains("Options.Require_book_and_quill_to_create_book")) {
|
||||
@ -296,34 +307,26 @@ public class BooksWithoutBordersConfig {
|
||||
BooksWithoutBorders.getInstance().reloadConfig();
|
||||
Configuration config = BooksWithoutBorders.getInstance().getConfig();
|
||||
try {
|
||||
useYml = config.getBoolean(ConfigOption.USE_YAML.getConfigNode(),
|
||||
(Boolean) ConfigOption.USE_YAML.getDefaultValue());
|
||||
bookDuplicateLimit = config.getInt(ConfigOption.MAX_DUPLICATES.getConfigNode(),
|
||||
(Integer) ConfigOption.MAX_DUPLICATES.getDefaultValue());
|
||||
titleAuthorSeparator = config.getString(ConfigOption.TITLE_AUTHOR_SEPARATOR.getConfigNode(),
|
||||
(String) ConfigOption.TITLE_AUTHOR_SEPARATOR.getDefaultValue());
|
||||
loreSeparator = config.getString(ConfigOption.LORE_LINE_SEPARATOR.getConfigNode(),
|
||||
(String) ConfigOption.LORE_LINE_SEPARATOR.getDefaultValue());
|
||||
adminDecrypt = config.getBoolean(ConfigOption.ADMIN_AUTO_DECRYPT.getConfigNode(),
|
||||
(Boolean) ConfigOption.ADMIN_AUTO_DECRYPT.getDefaultValue());
|
||||
authorOnlyCopy = config.getBoolean(ConfigOption.AUTHOR_ONLY_COPY.getConfigNode(),
|
||||
(Boolean) ConfigOption.AUTHOR_ONLY_COPY.getDefaultValue());
|
||||
authorOnlyUnsign = config.getBoolean(ConfigOption.AUTHOR_ONLY_UNSIGN.getConfigNode(),
|
||||
(Boolean) ConfigOption.AUTHOR_ONLY_UNSIGN.getDefaultValue());
|
||||
useYml = getBoolean(config, ConfigOption.USE_YAML);
|
||||
bookDuplicateLimit = getInt(config, ConfigOption.MAX_DUPLICATES);
|
||||
titleAuthorSeparator = getString(config, ConfigOption.TITLE_AUTHOR_SEPARATOR);
|
||||
loreSeparator = getString(config, ConfigOption.LORE_LINE_SEPARATOR);
|
||||
adminDecrypt = getBoolean(config, ConfigOption.ADMIN_AUTO_DECRYPT);
|
||||
authorOnlyCopy = getBoolean(config, ConfigOption.AUTHOR_ONLY_COPY);
|
||||
authorOnlyUnsign = getBoolean(config, ConfigOption.AUTHOR_ONLY_UNSIGN);
|
||||
firstBooks = config.getStringList(ConfigOption.BOOKS_FOR_NEW_PLAYERS.getConfigNode());
|
||||
welcomeMessage = config.getString(ConfigOption.MESSAGE_FOR_NEW_PLAYERS.getConfigNode(),
|
||||
(String) ConfigOption.MESSAGE_FOR_NEW_PLAYERS.getDefaultValue());
|
||||
formatBooks = config.getBoolean(ConfigOption.FORMAT_AFTER_SIGNING.getConfigNode(),
|
||||
(Boolean) ConfigOption.FORMAT_AFTER_SIGNING.getDefaultValue());
|
||||
welcomeMessage = getString(config, ConfigOption.MESSAGE_FOR_NEW_PLAYERS);
|
||||
formatBooks = getBoolean(config, ConfigOption.FORMAT_AFTER_SIGNING);
|
||||
changeGenerationOnCopy = getBoolean(config, ConfigOption.CHANGE_GENERATION_ON_COPY);
|
||||
|
||||
//Convert string into material
|
||||
String paymentMaterial = config.getString(ConfigOption.PRICE_ITEM_TYPE.getConfigNode(),
|
||||
(String) ConfigOption.PRICE_ITEM_TYPE.getDefaultValue());
|
||||
String paymentMaterial = getString(config, ConfigOption.PRICE_ITEM_TYPE);
|
||||
if (paymentMaterial.equalsIgnoreCase("Economy")) {
|
||||
if (EconomyHelper.setupEconomy()) {
|
||||
bookPriceType = Material.AIR;
|
||||
} else {
|
||||
sendErrorMessage(consoleSender, "BooksWithoutBorders failed to hook into Vault! Book price not set!");
|
||||
sendErrorMessage(consoleSender,
|
||||
"BooksWithoutBorders failed to hook into Vault! Book price not set!");
|
||||
bookPriceType = null;
|
||||
}
|
||||
} else if (!paymentMaterial.trim().isEmpty()) {
|
||||
@ -332,8 +335,7 @@ public class BooksWithoutBordersConfig {
|
||||
bookPriceType = material;
|
||||
}
|
||||
}
|
||||
bookPriceQuantity = config.getDouble(ConfigOption.PRICE_QUANTITY.getConfigNode(),
|
||||
(Double) ConfigOption.PRICE_QUANTITY.getDefaultValue());
|
||||
bookPriceQuantity = getDouble(config, ConfigOption.PRICE_QUANTITY);
|
||||
|
||||
//Make sure titleAuthorSeparator is a valid value
|
||||
titleAuthorSeparator = cleanString(titleAuthorSeparator);
|
||||
@ -352,4 +354,48 @@ public class BooksWithoutBordersConfig {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the double value of the given config option
|
||||
*
|
||||
* @param config <p>The configuration to read from</p>
|
||||
* @param configOption <p>The configuration option to get the value for</p>
|
||||
* @return <p>The value of the option</p>
|
||||
*/
|
||||
private static double getDouble(Configuration config, ConfigOption configOption) {
|
||||
return config.getDouble(configOption.getConfigNode(), (Double) configOption.getDefaultValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the integer value of the given config option
|
||||
*
|
||||
* @param config <p>The configuration to read from</p>
|
||||
* @param configOption <p>The configuration option to get the value for</p>
|
||||
* @return <p>The value of the option</p>
|
||||
*/
|
||||
private static int getInt(Configuration config, ConfigOption configOption) {
|
||||
return config.getInt(configOption.getConfigNode(), (Integer) configOption.getDefaultValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string value of the given config option
|
||||
*
|
||||
* @param config <p>The configuration to read from</p>
|
||||
* @param configOption <p>The configuration option to get the value for</p>
|
||||
* @return <p>The value of the option</p>
|
||||
*/
|
||||
private static String getString(Configuration config, ConfigOption configOption) {
|
||||
return config.getString(configOption.getConfigNode(), (String) configOption.getDefaultValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the boolean value of the given config option
|
||||
*
|
||||
* @param config <p>The configuration to read from</p>
|
||||
* @param configOption <p>The configuration option to get the value for</p>
|
||||
* @return <p>The value of the option</p>
|
||||
*/
|
||||
private static boolean getBoolean(Configuration config, ConfigOption configOption) {
|
||||
return config.getBoolean(configOption.getConfigNode(), (Boolean) configOption.getDefaultValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -60,6 +60,11 @@ public enum ConfigOption {
|
||||
*/
|
||||
AUTHOR_ONLY_UNSIGN("Author_Only_Unsign", false),
|
||||
|
||||
/**
|
||||
* Whether to turn Original into Copy when copying books
|
||||
*/
|
||||
CHANGE_GENERATION_ON_COPY("Decrease_Generation_On_Copy", false),
|
||||
|
||||
/**
|
||||
* Whether to automatically format every signed book
|
||||
*/
|
||||
|
@ -3,6 +3,7 @@ package net.knarcraft.bookswithoutborders.utility;
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
|
||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.cleanString;
|
||||
@ -17,6 +18,36 @@ public final class BookHelper {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the generation of the given book, if necessary
|
||||
*
|
||||
* @param bookItem <p>The book item to increase the generation of</p>
|
||||
*/
|
||||
public static void increaseGeneration(ItemStack bookItem) {
|
||||
BookMeta bookMeta = (BookMeta) bookItem.getItemMeta();
|
||||
if (BooksWithoutBordersConfig.changeGenerationOnCopy() && bookMeta != null && bookMeta.hasGeneration()) {
|
||||
bookMeta.setGeneration(BookHelper.getNextGeneration(bookMeta.getGeneration()));
|
||||
bookItem.setItemMeta(bookMeta);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next generation of the given book
|
||||
*
|
||||
* <p>If an original book is given, this will yield a copy of the original. If a copy of original is given, this
|
||||
* will yield a copy of a copy. In all other cases, the generation will stay the same</p>
|
||||
*
|
||||
* @param currentGeneration <p>The current generation of the book</p>
|
||||
* @return <p>The next generation of the book</p>
|
||||
*/
|
||||
public static BookMeta.Generation getNextGeneration(BookMeta.Generation currentGeneration) {
|
||||
return switch (currentGeneration) {
|
||||
case ORIGINAL -> BookMeta.Generation.COPY_OF_ORIGINAL;
|
||||
case COPY_OF_ORIGINAL -> BookMeta.Generation.COPY_OF_COPY;
|
||||
default -> currentGeneration;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file name of the given book
|
||||
*
|
||||
|
@ -25,4 +25,7 @@ Options:
|
||||
Author_Only_Unsign: false
|
||||
# Whether to automatically format every book when it's signed
|
||||
Format_Book_After_Signing: false
|
||||
# Whether to display "COPY" or "COPY_OF_COPY" instead of "ORIGINAL" when a book is copied. This also uses the
|
||||
# vanilla behavior where a copy of a copy cannot be copied further.
|
||||
Change_Generation_On_Copy: false
|
||||
|
@ -88,7 +88,7 @@ commands:
|
||||
reload:
|
||||
description: Reloads BwB's configuration file
|
||||
usage: /<command>
|
||||
permission: bookswithoutborders.admin
|
||||
permission: bookswithoutborders.reload
|
||||
permissions:
|
||||
bookswithoutborders.*:
|
||||
description: Grants all permissions
|
||||
@ -114,6 +114,7 @@ permissions:
|
||||
bookswithoutborders.bypassauthoronlyunsign: true
|
||||
bookswithoutborders.bypassbookprice: true
|
||||
bookswithoutborders.setbookprice: true
|
||||
bookswithoutborders.reload: true
|
||||
bookswithoutborders.use:
|
||||
description: Allows player to use commands to save/load/delete in their personal directory
|
||||
children:
|
||||
@ -169,4 +170,6 @@ permissions:
|
||||
bookswithoutborders.bypassbookprice:
|
||||
description: Allows player to ignore Price_to_create_book config setting
|
||||
bookswithoutborders.setbookprice:
|
||||
description: Allows player to set the cost of creating a book
|
||||
description: Allows player to set the cost of creating a book
|
||||
bookswithoutborders.reload:
|
||||
description: Allows player to reload this plugin
|
Loading…
Reference in New Issue
Block a user