Implements material wildcards for costs #14
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good

This commit is contained in:
Kristian Knarvik 2023-01-09 16:53:52 +01:00
parent 913cc5736e
commit a856aa03e0
5 changed files with 175 additions and 51 deletions

View File

@ -69,6 +69,10 @@ for a specific material/enchantment. For /blacksmithconfig <basePrice/pricePerDu
material/enchantment>, using -1 or null as the value will clear the cost for the specified material/enchantment, using
the default one instead.
basePrice and pricePerDurabilityPoint support using a wildcard "*" to set the price for multiple groups of materials at
once. `/blacksmithconfig basePrice golden* 43` would set the price of any material starting with "GOLDEN" to 43. You can
also set `netherite*: 34` directly in the config file to set the price of all netherite materials to 34.
### Presets and filters:
**Presets are a nice way to make specialized blacksmiths by specifying categories of materials, instead of manually
@ -138,13 +142,13 @@ All currently supported presets, and available filters for each preset:
### Global-only options
| Key | Value type | Description |
|-------------------------|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| basePrice | positive decimal number | The base price which has to be paid regardless of the durability remaining for an item. Setting this without specifying a material sets the basePrice for any item the basePrice has not been set for. |
| pricePerDurabilityPoint | positive decimal number | The price added for each durability point present/missing (depends on whether natural cost is set to true or false). Setting this without specifying a material sets the pricePerDurabilityPoint for any item the pricePerDurabilityPoint has not been set for. |
| enchantmentCost | positive decimal number | The added cost for each level of an enchantment present on the item. The cost can be set for specific enchantments. Not specifying an enchantment sets the value for all enchantments without a set value. |
| useNaturalCost | true/false | If true, each missing durability will add to the cost (price = basePrice + missingDurability * pricePerDurabilityPoint + enchantmentCost). If false, durability will be used to calculate the cost instead of missingDurability (this was the behavior before natural cost was added). |
| showExactTime | true/false | If true, blacksmiths will display exact time remaining in minutes and seconds, instead of vague expressions |
| Key | Value type | Description |
|-------------------------|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| basePrice | positive decimal number | The base price which has to be paid regardless of the durability remaining for an item. Setting this without specifying a material sets the basePrice for any item the basePrice has not been set for. You can use for example "netherite*: 10" to set the value for any material beginning with "netherite". |
| pricePerDurabilityPoint | positive decimal number | The price added for each durability point present/missing (depends on whether natural cost is set to true or false). Setting this without specifying a material sets the pricePerDurabilityPoint for any item the pricePerDurabilityPoint has not been set for. You can use for example "netherite*: 10" to set the value for any material beginning with "netherite". |
| enchantmentCost | positive decimal number | The added cost for each level of an enchantment present on the item. The cost can be set for specific enchantments. Not specifying an enchantment sets the value for all enchantments without a set value. |
| useNaturalCost | true/false | If true, each missing durability will add to the cost (price = basePrice + missingDurability * pricePerDurabilityPoint + enchantmentCost). If false, durability will be used to calculate the cost instead of missingDurability (this was the behavior before natural cost was added). |
| showExactTime | true/false | If true, blacksmiths will display exact time remaining in minutes and seconds, instead of vague expressions |
### Per-npc (with default values set in config.yml)

View File

@ -8,6 +8,7 @@ import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import net.knarcraft.blacksmith.formatting.ItemType;
import net.knarcraft.blacksmith.util.InputParsingHelper;
import net.knarcraft.blacksmith.util.ItemHelper;
import net.knarcraft.blacksmith.util.TypeValidationHelper;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Material;
@ -232,39 +233,88 @@ public class BlackSmithConfigCommand implements CommandExecutor {
return true;
}
double newPrice = Double.parseDouble(args[2]);
String itemChanged;
ItemType itemType;
String newValue = String.valueOf(newPrice);
if (detectedGlobalSetting == GlobalSetting.BASE_PRICE ||
detectedGlobalSetting == GlobalSetting.PRICE_PER_DURABILITY_POINT) {
Material material = InputParsingHelper.matchMaterial(args[1]);
return updatePriceSpecialCase(settings, detectedGlobalSetting, args[1], newPrice, sender);
} else if (detectedGlobalSetting == GlobalSetting.ENCHANTMENT_COST) {
//Update enchantment cost for an item
Enchantment enchantment = InputParsingHelper.matchEnchantment(args[1]);
if (enchantment == null) {
return false;
}
ItemType itemType = ItemType.ENCHANTMENT;
String itemChanged = enchantment.getKey().getKey();
settings.setEnchantmentCost(enchantment, newPrice);
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
BlacksmithTranslatableMessage.getItemValueChangedMessage(detectedGlobalSetting.getCommandName(),
itemType, itemChanged, newValue));
return true;
} else {
return false;
}
}
/**
* Updates a special case price configuration value if a special case is encountered
*
* @param settings <p>The settings to modify</p>
* @param detectedGlobalSetting <p>The global setting specified</p>
* @param materialName <p>The material name to update the price for</p>
* @param newPrice <p>The new price to update to</p>
* @param sender <p>The command sender to respond to</p>
* @return <p>True if the input was valid, and the item(s) was/were updated</p>
*/
private boolean updatePriceSpecialCase(GlobalSettings settings, GlobalSetting detectedGlobalSetting,
String materialName, double newPrice, CommandSender sender) {
ItemType itemType = ItemType.MATERIAL;
String itemChanged;
//Update base price or price per durability point for an item
if (materialName.contains("*")) {
itemChanged = materialName;
updateAllMatchedPrices(settings, detectedGlobalSetting, materialName, newPrice);
} else {
Material material = InputParsingHelper.matchMaterial(materialName);
if (material == null) {
return false;
}
itemType = ItemType.MATERIAL;
itemChanged = material.name();
if (detectedGlobalSetting == GlobalSetting.BASE_PRICE) {
settings.setBasePrice(material, newPrice);
} else {
settings.setPricePerDurabilityPoint(material, newPrice);
}
} else if (detectedGlobalSetting == GlobalSetting.ENCHANTMENT_COST) {
Enchantment enchantment = InputParsingHelper.matchEnchantment(args[1]);
if (enchantment == null) {
return false;
}
itemType = ItemType.ENCHANTMENT;
itemChanged = enchantment.getKey().getKey();
settings.setEnchantmentCost(enchantment, newPrice);
} else {
return false;
}
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
BlacksmithTranslatableMessage.getItemValueChangedMessage(detectedGlobalSetting.getCommandName(),
itemType, itemChanged, newValue));
itemType, itemChanged, String.valueOf(newPrice)));
return true;
}
/**
* Updates all materials matching the material name wildcard
*
* @param settings <p>The settings to modify</p>
* @param detectedGlobalSetting <p>The global setting specified</p>
* @param materialName <p>The wildcard material name to update cost for</p>
* @param newPrice <p>The new cost for the matched items</p>
*/
private void updateAllMatchedPrices(GlobalSettings settings, GlobalSetting detectedGlobalSetting,
String materialName, double newPrice) {
String search = InputParsingHelper.regExIfy(materialName);
for (Material material : ItemHelper.getAllReforgeAbleMaterials()) {
if (!material.name().matches(search)) {
continue;
}
if (detectedGlobalSetting == GlobalSetting.BASE_PRICE) {
settings.setBasePrice(material, newPrice);
} else {
settings.setPricePerDurabilityPoint(material, newPrice);
}
}
}
}

View File

@ -5,6 +5,7 @@ import net.citizensnpcs.api.util.YamlStorage;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.util.ConfigHelper;
import net.knarcraft.blacksmith.util.InputParsingHelper;
import net.knarcraft.blacksmith.util.ItemHelper;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
@ -307,48 +308,98 @@ public class GlobalSettings {
}
//Load all base prices
DataKey basePriceNode = root.getRelative(GlobalSetting.BASE_PRICE.getParent());
Map<String, String> relevantKeys = getRelevantKeys(basePriceNode);
for (String key : relevantKeys.keySet()) {
String materialName = relevantKeys.get(key);
Material material = InputParsingHelper.matchMaterial(materialName);
if (material != null) {
materialBasePrices.put(material, basePriceNode.getDouble(key));
} else {
BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING,
"Unable to find a material matching " + materialName);
}
}
loadBasePrices(root);
//Load all per-durability-point prices
DataKey basePerDurabilityPriceNode = root.getRelative(GlobalSetting.PRICE_PER_DURABILITY_POINT.getParent());
relevantKeys = getRelevantKeys(basePerDurabilityPriceNode);
for (String key : relevantKeys.keySet()) {
String materialName = relevantKeys.get(key);
Material material = InputParsingHelper.matchMaterial(materialName);
if (material != null) {
materialPricePerDurabilityPoints.put(material, basePerDurabilityPriceNode.getDouble(key));
} else {
BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING,
"Unable to find a material matching " + materialName);
}
}
loadPricesPerDurabilityPoint(root);
//Load all enchantment prices
DataKey enchantmentCostNode = root.getRelative(GlobalSetting.ENCHANTMENT_COST.getParent());
relevantKeys = getRelevantKeys(basePerDurabilityPriceNode);
Map<String, String> relevantKeys = getRelevantKeys(enchantmentCostNode);
for (String key : relevantKeys.keySet()) {
String enchantmentName = relevantKeys.get(key);
Enchantment enchantment = InputParsingHelper.matchEnchantment(enchantmentName);
if (enchantment != null) {
enchantmentCosts.put(enchantment, enchantmentCostNode.getDouble(key));
setItemPrice(enchantmentCosts, enchantmentName, enchantment, enchantmentCostNode.getDouble(key));
}
}
/**
* Loads all prices per durability point for all materials
*
* @param root <p>The configuration root node to search from</p>
*/
private void loadPricesPerDurabilityPoint(DataKey root) {
DataKey basePerDurabilityPriceNode = root.getRelative(GlobalSetting.PRICE_PER_DURABILITY_POINT.getParent());
Map<String, String> relevantKeys = getRelevantKeys(basePerDurabilityPriceNode);
for (String key : relevantKeys.keySet()) {
String materialName = relevantKeys.get(key);
double price = basePerDurabilityPriceNode.getDouble(key);
if (materialName.contains("*")) {
//Treat *CHESTPLATE as a regular expression to match all chest-plates
setMatchedMaterialPrices(materialPricePerDurabilityPoints, materialName, price);
} else {
BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING,
"Unable to find an enchantment matching " + enchantmentName);
Material material = InputParsingHelper.matchMaterial(materialName);
setItemPrice(materialPricePerDurabilityPoints, materialName, material, price);
}
}
}
/**
* Loads base prices for all materials
*
* @param root <p>The configuration root node to search from</p>
*/
private void loadBasePrices(DataKey root) {
DataKey basePriceNode = root.getRelative(GlobalSetting.BASE_PRICE.getParent());
Map<String, String> relevantKeys = getRelevantKeys(basePriceNode);
for (String key : relevantKeys.keySet()) {
String materialName = relevantKeys.get(key);
double price = basePriceNode.getDouble(key);
if (materialName.contains("*")) {
//Treat *CHESTPLATE as a regular expression to match all chest-plates
setMatchedMaterialPrices(materialBasePrices, materialName, price);
} else {
Material material = InputParsingHelper.matchMaterial(materialName);
setItemPrice(materialBasePrices, materialName, material, price);
}
}
}
/**
* Sets the price for any materials matching the given wildcard material name
*
* @param prices <p>The map to store the prices in</p>
* @param materialName <p>The material name to match</p>
* @param price <p>The price to set for the matched materials</p>
*/
private void setMatchedMaterialPrices(Map<Material, Double> prices, String materialName, double price) {
String search = InputParsingHelper.regExIfy(materialName);
for (Material material : ItemHelper.getAllReforgeAbleMaterials()) {
if (material.name().matches(search)) {
setItemPrice(prices, material.name(), material, price);
}
}
}
/**
* Sets the price for the given material
*
* @param prices <p>The map to store the price in</p>
* @param itemName <p>The name of the material to add a price for</p>
* @param item <p>The material parsed from the name</p>
* @param price <p>The price to set</p>
*/
private <K> void setItemPrice(Map<K, Double> prices, String itemName, K item, double price) {
if (item != null) {
prices.put(item, price);
} else {
BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING,
"Unable to find a material/enchantment matching " + itemName);
}
}
/**
* Gets a map between relevant keys and their normalized name
*

View File

@ -44,4 +44,14 @@ public final class InputParsingHelper {
return Enchantment.getByKey(NamespacedKey.minecraft(input.replace("-", "_")));
}
/**
* Converts a material name like "*helmet" into a regular expression ".*HELMET"
*
* @param input <p>The input to RegExIfy</p>
* @return <p>The converted input</p>
*/
public static String regExIfy(String input) {
return input.replace("*", ".*").toUpperCase().replace("-", "_");
}
}

View File

@ -63,6 +63,15 @@ public final class TabCompleteValuesHelper {
for (Material material : ItemHelper.getAllReforgeAbleMaterials()) {
reforgeAbleMaterials.add(material.name());
}
reforgeAbleMaterials.add("NETHERITE_*");
reforgeAbleMaterials.add("DIAMOND_*");
reforgeAbleMaterials.add("GOLDEN_*");
reforgeAbleMaterials.add("IRON_*");
reforgeAbleMaterials.add("CHAINMAIL_*");
reforgeAbleMaterials.add("STONE_*");
reforgeAbleMaterials.add("*BOW");
reforgeAbleMaterials.add("LEATHER_*");
reforgeAbleMaterials.add("WOODEN_*");
return reforgeAbleMaterials;
}