diff --git a/src/main/java/net/tfminecraft/cooking/cache/CategoryDictionary.java b/src/main/java/net/tfminecraft/cooking/cache/CategoryDictionary.java index 3c5189e..dd506ad 100644 --- a/src/main/java/net/tfminecraft/cooking/cache/CategoryDictionary.java +++ b/src/main/java/net/tfminecraft/cooking/cache/CategoryDictionary.java @@ -25,6 +25,8 @@ public static String getName(String key) { } public static String getSauceItemPath(String colour, int index) { + // Uncoloured names (water-only mixes, or older "000000Mixed Sauce" items) have no hex to read. + if (colour == null) return ItemCache.liquidFallback; int rTotal = 0, gTotal = 0, bTotal = 0, count = 0; String hex = colour.replace("#", ""); if (hex.equalsIgnoreCase("000000")) return ItemCache.liquidFallback; diff --git a/src/main/java/net/tfminecraft/cooking/cooking/CookingReference.java b/src/main/java/net/tfminecraft/cooking/cooking/CookingReference.java index 4d37724..224f0ad 100644 --- a/src/main/java/net/tfminecraft/cooking/cooking/CookingReference.java +++ b/src/main/java/net/tfminecraft/cooking/cooking/CookingReference.java @@ -310,7 +310,7 @@ protected String applyNameTemplate(FoodItem product, String colour, String typeL if (fillers.isEmpty()) { fillers = "Mixed "; } - displayName = displayName.replace("{colour}", colour == null ? "" : colour); + displayName = displayName.replace("{colour}", DisplayUtils.getNameColour(colour)); displayName = displayName.replace("{prefixes}", NameComposer.formatPrefixes(product)); displayName = displayName.replace("{fillers}", fillers); displayName = displayName.replace("{ingredients}", getName(typeLabel)); diff --git a/src/main/java/net/tfminecraft/cooking/cooking/SauceReference.java b/src/main/java/net/tfminecraft/cooking/cooking/SauceReference.java index 3f38cbb..55182ac 100644 --- a/src/main/java/net/tfminecraft/cooking/cooking/SauceReference.java +++ b/src/main/java/net/tfminecraft/cooking/cooking/SauceReference.java @@ -20,6 +20,8 @@ import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataType; + import net.tfminecraft.tlibs.TLibs; @@ -58,6 +60,8 @@ import net.tfminecraft.cooking.utils.ItemBuilder; +import net.tfminecraft.cooking.utils.Keys; + import net.tfminecraft.cooking.utils.StationAddonRules; import net.tfminecraft.interactiblefurniture.events.FurnitureInteractEvent; @@ -306,11 +310,14 @@ public void scoop(Player p, ItemStack ladle) { - String displayName = applyNameTemplate(sauce, DisplayUtils.getMergedColour(colours), "Sauce"); + String colour = DisplayUtils.getMergedColour(colours); + String displayName = applyNameTemplate(sauce, colour, "Sauce"); ItemMeta m = output.getItemMeta(); m.setDisplayName(StringFormatter.formatHex(displayName)); + // The name can't carry every colour (Paper stores #ffffff as named white), so keep it here too. + m.getPersistentDataContainer().set(Keys.SAUCE_COLOUR, PersistentDataType.STRING, colour); output.setItemMeta(m); diff --git a/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java b/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java index 4ff66a5..63a2907 100644 --- a/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java +++ b/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java @@ -181,12 +181,19 @@ public void addSauce(Player p, Furniture f, FoodItem sauce, ItemStack base) { if (f.getType() == null || f.getType().getSlot("sauce") == null) return; if(f.hasActiveSlot("sauce")) return; - String saucePath = CategoryDictionary.getSauceItemPath( - StringFormatter.extractHexColor(base.getItemMeta().getDisplayName()), 1); + ItemMeta baseMeta = base.getItemMeta(); + String saucePath = CategoryDictionary.getSauceItemPath(sauceColour( + baseMeta.getPersistentDataContainer().get(Keys.SAUCE_COLOUR, PersistentDataType.STRING), + baseMeta.getDisplayName()), 1); f.getOrCreatePlacedSlot("sauce").forceModel(TLibs.getItemAPI().getCreator().getItemFromPath(saucePath)); f.getLoc().getWorld().playSound(f.getLoc(), Sound.ITEM_BUCKET_FILL, 1f, 1f); //TODO SOUND } + /** Sauce colour stored at scoop time; ladles scooped before it existed fall back to the name's colour. */ + static String sauceColour(String stored, String displayName) { + return stored != null ? stored : StringFormatter.extractHexColor(displayName); + } + public void addSoup(Player p, Furniture f, FoodItem soup, ItemStack base) { p.getInventory().setItemInMainHand( TLibs.getItemAPI().getCreator().getItemFromPath(ItemCache.ladle) diff --git a/src/main/java/net/tfminecraft/cooking/utils/DisplayUtils.java b/src/main/java/net/tfminecraft/cooking/utils/DisplayUtils.java index 130c48f..35fa7f8 100644 --- a/src/main/java/net/tfminecraft/cooking/utils/DisplayUtils.java +++ b/src/main/java/net/tfminecraft/cooking/utils/DisplayUtils.java @@ -56,9 +56,10 @@ public static String getDisplayString(String name, double foodMultiplier, double return name + " §8(" + extra.toString() + "§8)"; } + /** Average of the stored {@code rrggbb} colours, always as {@code #rrggbb}. */ public static String getMergedColour(List colours) { - if (colours.size() == 0) return "000000"; - if (colours.size() == 1) return colours.get(0); + if (colours.size() == 0) return "#000000"; + if (colours.size() == 1) return "#" + colours.get(0).replace("#", ""); int r = 0, g = 0, b = 0; @@ -78,6 +79,15 @@ public static String getMergedColour(List colours) { return String.format("#%02x%02x%02x", r, g, b); } + /** + * Colour code for a sauce or soup name. A mix of only uncoloured liquids (water) merges to + * {@code #000000}, which would print black, so it uses white instead. + */ + public static String getNameColour(String mergedColour) { + if (mergedColour == null || mergedColour.replace("#", "").equalsIgnoreCase("000000")) return "&f"; + return mergedColour; + } + public static String getSauceStatString(double food, double nutrition) { StringBuilder sb = new StringBuilder(); diff --git a/src/main/java/net/tfminecraft/cooking/utils/Keys.java b/src/main/java/net/tfminecraft/cooking/utils/Keys.java index 4432e5c..ca5fc93 100644 --- a/src/main/java/net/tfminecraft/cooking/utils/Keys.java +++ b/src/main/java/net/tfminecraft/cooking/utils/Keys.java @@ -41,6 +41,9 @@ public class Keys { new NamespacedKey(Cooking.plugin, "sauce"); public static final NamespacedKey SAUCE_NAME = new NamespacedKey(Cooking.plugin, "sauce_name"); + /** Merged {@code #rrggbb} colour of a scooped sauce; plates pick their sauce visual from it. */ + public static final NamespacedKey SAUCE_COLOUR = + new NamespacedKey(Cooking.plugin, "sauce_colour"); public static final NamespacedKey CARVE_SEQUENCE = new NamespacedKey(Cooking.plugin, "carve_sequence"); public static final NamespacedKey CARVE_NEXT_INDEX = diff --git a/src/test/java/net/tfminecraft/cooking/cache/SauceColourTest.java b/src/test/java/net/tfminecraft/cooking/cache/SauceColourTest.java new file mode 100644 index 0000000..d6c32f7 --- /dev/null +++ b/src/test/java/net/tfminecraft/cooking/cache/SauceColourTest.java @@ -0,0 +1,74 @@ +package net.tfminecraft.cooking.cache; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.HashMap; +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import net.tfminecraft.cooking.utils.DisplayUtils; +import net.tfminecraft.tlibs.objects.api.subapi.StringFormatter; + +class SauceColourTest { + private static final String WATER = "ia.test:water"; + private static final String MILK_PLATED = "ia.test:milk_plated"; + + private String previousFallback; + private HashMap previousDict; + + @BeforeEach + void setUp() { + previousFallback = ItemCache.liquidFallback; + previousDict = CategoryDictionary.sauceDict; + ItemCache.liquidFallback = WATER; + CategoryDictionary.sauceDict = new HashMap<>(); + CategoryDictionary.sauceDict.put("ffffff", "ia.test:milk|" + MILK_PLATED); + } + + @AfterEach + void tearDown() { + ItemCache.liquidFallback = previousFallback; + CategoryDictionary.sauceDict = previousDict; + } + + @Test + void mergedColourAlwaysHasAHash() { + assertEquals("#000000", DisplayUtils.getMergedColour(List.of())); + assertEquals("#000000", DisplayUtils.getMergedColour(List.of("000000"))); + assertEquals("#ffffff", DisplayUtils.getMergedColour(List.of("ffffff"))); + assertEquals("#abcdef", DisplayUtils.getMergedColour(List.of("#abcdef"))); + assertEquals("#185d15", DisplayUtils.getMergedColour(List.of("000000", "30ba2b"))); + } + + @Test + void uncolouredMixesAreNamedWhite() { + assertEquals("&f", DisplayUtils.getNameColour("#000000")); + assertEquals("&f", DisplayUtils.getNameColour(null)); + assertEquals("#185d15", DisplayUtils.getNameColour("#185d15")); + } + + @Test + void waterOnlySauceNameShowsTheWaterVisual() { + String name = StringFormatter.formatHex( + DisplayUtils.getNameColour(DisplayUtils.getMergedColour(List.of("000000"))) + "Mixed Sauce"); + + assertEquals("§fMixed Sauce", name); + assertNull(StringFormatter.extractHexColor(name)); + assertEquals(WATER, CategoryDictionary.getSauceItemPath(StringFormatter.extractHexColor(name), 1)); + } + + @Test + void olderUncolouredSauceNameShowsTheWaterVisual() { + assertEquals(WATER, CategoryDictionary.getSauceItemPath(StringFormatter.extractHexColor("000000Mixed Sauce"), 1)); + } + + @Test + void milkOnlySauceColourShowsTheMilkVisual() { + // Scooping stores this merged colour on the ladle; the name alone can't carry it (Paper keeps #ffffff as named white). + assertEquals(MILK_PLATED, CategoryDictionary.getSauceItemPath(DisplayUtils.getMergedColour(List.of("ffffff")), 1)); + } +} diff --git a/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java b/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java index 92723f8..9d7a132 100644 --- a/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java +++ b/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java @@ -1,7 +1,9 @@ package net.tfminecraft.cooking.manager; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -14,6 +16,7 @@ import net.tfminecraft.interactiblefurniture.furniture.Furniture; import net.tfminecraft.interactiblefurniture.furniture.PlacedSlot; +import net.tfminecraft.tlibs.objects.api.subapi.StringFormatter; class PlateManagerTest { @@ -175,6 +178,17 @@ public boolean hasItemMeta() { assertFalse(plate.hasActiveSlot("sauce")); } + @Test + void storedSauceColourWinsOverTheName() { + assertEquals("#ffffff", PlateManager.sauceColour("#ffffff", "§fMixed Sauce")); + } + + @Test + void ladlesWithoutAStoredColourFallBackToTheName() { + assertEquals("#185d15", PlateManager.sauceColour(null, StringFormatter.formatHex("#185d15Basil Sauce"))); + assertNull(PlateManager.sauceColour(null, "000000Mixed Sauce")); + } + @Test void displaySlotsDoNotCountAsFood() { Furniture plate = plateWithSauceVisual();