From eacaec171af3476599fa13856b7f469ddea4d2be Mon Sep 17 00:00:00 2001 From: XxFran10xX <318299142+XxFran10xX@users.noreply.github.com> Date: Sun, 27 Sep 2026 01:50:27 +0200 Subject: [PATCH 1/3] fix: clear a plate's sauce once its last food is taken off The sauce visual stayed on a plate after its food was removed, so hasSauce rejected sauce for every later dish on that plate (reported with chicken legs, but any food was affected). Remove the sauce slot when the last food leaves, clear leftover sauce on plates saved before this fix, and keep the ladle full when sauce is ladled onto an empty plate. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../cooking/manager/PlateManager.java | 32 +++++++++++++++ .../cooking/manager/PlateManagerTest.java | 41 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java b/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java index 15f08a8..8892edf 100644 --- a/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java +++ b/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java @@ -10,6 +10,7 @@ import org.bukkit.Sound; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; @@ -31,6 +32,7 @@ import net.tfminecraft.interactiblefurniture.events.FurnitureBreakEvent; import net.tfminecraft.interactiblefurniture.events.FurnitureInteractEvent; import net.tfminecraft.interactiblefurniture.events.FurnitureSlotItemAddEvent; +import net.tfminecraft.interactiblefurniture.events.FurnitureSlotItemTakeEvent; import net.tfminecraft.interactiblefurniture.furniture.Furniture; import net.tfminecraft.interactiblefurniture.furniture.PlacedSlot; import net.tfminecraft.interactiblefurniture.furniture.data.DisplayData; @@ -56,6 +58,11 @@ public void run() { } public void update(Furniture f) { + // Also clears plates saved with leftover sauce before the take handler existed. + if(hasLeftoverSauce(f, null)) { + f.removeActiveSlot("sauce"); + InteractibleFurniture.getInstance().getFurnitureManager().persistFurniture(f); + } for(PlacedSlot slot : f.getActiveSlots().values()) { if(slot.getId().contains("display")) continue; ItemStack item = slot.getCurrentItem(); @@ -99,10 +106,35 @@ public boolean hasSauce(Furniture f) { return false; } + /** + * True when the plate shows a sauce visual but no food would remain once {@code leavingSlot} + * is gone. A leftover sauce visual makes hasSauce reject the next dish on the plate. + */ + boolean hasLeftoverSauce(Furniture f, String leavingSlot) { + if(!f.hasActiveSlot("sauce")) return false; + for(String id : f.getActiveSlots().keySet()) { + if(id.equals("sauce") || id.equals(leavingSlot)) continue; + return false; + } + return true; + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void takeItem(FurnitureSlotItemTakeEvent e) { + Furniture f = e.getFurniture(); + if(!FurnitureCache.isPlate(f)) return; + // The taken slot is still active here; InteractibleFurniture removes it and saves the plate next. + if(hasLeftoverSauce(f, e.getSlot().getId())) { + f.removeActiveSlot("sauce"); + } + } + // Keep the existing legacy text representation, formatting, and exact-string comparisons. @SuppressWarnings("deprecation") public void addSauce(Player p, Furniture f, FoodItem sauce, ItemStack base) { if (hasSauce(f)) return; + // Sauce on an empty plate would never reach food added later, so keep the ladle full. + if (f.getActiveSlots().isEmpty()) return; // Replace player ladle with empty ladle p.getInventory().setItemInMainHand( diff --git a/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java b/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java index 1522813..16547d1 100644 --- a/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java +++ b/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java @@ -116,4 +116,45 @@ public boolean hasItemMeta() { assertFalse(new PlateManager().hasSauce(plate)); } + + @Test + void sauceIsLeftoverOnceTheLastFoodLeaves() { + Furniture plate = plateWithSauceVisual(); + plate.getActiveSlots().put("item_1", new PlacedSlot(plate, "item_1")); + + assertFalse(new PlateManager().hasLeftoverSauce(plate, null)); + assertTrue(new PlateManager().hasLeftoverSauce(plate, "item_1")); + } + + @Test + void sauceStaysWhileOtherFoodRemains() { + Furniture plate = plateWithSauceVisual(); + plate.getActiveSlots().put("item_1", new PlacedSlot(plate, "item_1")); + plate.getActiveSlots().put("item_2", new PlacedSlot(plate, "item_2")); + + assertFalse(new PlateManager().hasLeftoverSauce(plate, "item_1")); + } + + @Test + void sauceAloneOnAPlateIsLeftover() { + assertTrue(new PlateManager().hasLeftoverSauce(plateWithSauceVisual(), null)); + } + + @Test + void plateWithoutSauceHasNoLeftover() { + Furniture plate = new Furniture("plate", null, UUID.randomUUID()); + plate.getActiveSlots().put("item_1", new PlacedSlot(plate, "item_1")); + + assertFalse(new PlateManager().hasLeftoverSauce(plate, null)); + assertFalse(new PlateManager().hasLeftoverSauce(plate, "item_1")); + } + + @Test + void sauceOnAnEmptyPlateReturnsBeforeTouchingThePlayerOrLadle() { + Furniture plate = new Furniture("plate", null, UUID.randomUUID()); + + // Null interaction arguments ensure the empty-plate attempt exits before using them. + assertDoesNotThrow(() -> new PlateManager().addSauce(null, plate, null, null)); + assertTrue(plate.getActiveSlots().isEmpty()); + } } From 8c829a72a83afba21641f1d6fa41c2bb2826eefb Mon Sep 17 00:00:00 2001 From: XxFran10xX <318299142+XxFran10xX@users.noreply.github.com> Date: Sun, 27 Sep 2026 08:45:51 +0200 Subject: [PATCH 2/3] fix: ignore plate display slots when deciding whether food remains Co-Authored-By: Claude Opus 5.5 (1M context) --- .../cooking/manager/PlateManager.java | 14 ++++++++++---- .../cooking/manager/PlateManagerTest.java | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java b/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java index 8892edf..f7638ab 100644 --- a/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java +++ b/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java @@ -112,11 +112,17 @@ public boolean hasSauce(Furniture f) { */ boolean hasLeftoverSauce(Furniture f, String leavingSlot) { if(!f.hasActiveSlot("sauce")) return false; + return !hasFood(f, leavingSlot); + } + + // An active food slot counts even without a loaded item: restore can leave the item unset + // while the display still shows the food. + private boolean hasFood(Furniture f, String ignoredSlot) { for(String id : f.getActiveSlots().keySet()) { - if(id.equals("sauce") || id.equals(leavingSlot)) continue; - return false; + if(id.equals("sauce") || id.contains("display") || id.equals(ignoredSlot)) continue; + return true; } - return true; + return false; } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @@ -134,7 +140,7 @@ public void takeItem(FurnitureSlotItemTakeEvent e) { public void addSauce(Player p, Furniture f, FoodItem sauce, ItemStack base) { if (hasSauce(f)) return; // Sauce on an empty plate would never reach food added later, so keep the ladle full. - if (f.getActiveSlots().isEmpty()) return; + if (!hasFood(f, null)) return; // Replace player ladle with empty ladle p.getInventory().setItemInMainHand( diff --git a/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java b/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java index 16547d1..d4ea122 100644 --- a/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java +++ b/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java @@ -157,4 +157,21 @@ void sauceOnAnEmptyPlateReturnsBeforeTouchingThePlayerOrLadle() { assertDoesNotThrow(() -> new PlateManager().addSauce(null, plate, null, null)); assertTrue(plate.getActiveSlots().isEmpty()); } + + @Test + void displaySlotsDoNotCountAsFood() { + Furniture plate = plateWithSauceVisual(); + plate.getActiveSlots().put("display_1", new PlacedSlot(plate, "display_1")); + + assertTrue(new PlateManager().hasLeftoverSauce(plate, null)); + } + + @Test + void sauceOnADisplayOnlyPlateReturnsBeforeTouchingThePlayerOrLadle() { + Furniture plate = new Furniture("plate", null, UUID.randomUUID()); + plate.getActiveSlots().put("display_1", new PlacedSlot(plate, "display_1")); + + assertDoesNotThrow(() -> new PlateManager().addSauce(null, plate, null, null)); + assertFalse(plate.hasActiveSlot("sauce")); + } } From 7ce7eb961417773b557c2d531328015c2c32a17a Mon Sep 17 00:00:00 2001 From: XxFran10xX <318299142+XxFran10xX@users.noreply.github.com> Date: Sun, 27 Sep 2026 09:08:36 +0200 Subject: [PATCH 3/3] fix: keep the ladle full when no plate food can take the sauce Co-Authored-By: Claude Opus 5.5 (1M context) --- .../cooking/manager/PlateManager.java | 14 ++++++++++++-- .../cooking/manager/PlateManagerTest.java | 17 +++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java b/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java index f7638ab..4ff66a5 100644 --- a/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java +++ b/src/main/java/net/tfminecraft/cooking/manager/PlateManager.java @@ -125,6 +125,16 @@ private boolean hasFood(Furniture f, String ignoredSlot) { return false; } + // Food addSauce can actually apply the sauce to. + private boolean hasSauceableFood(Furniture f) { + for(PlacedSlot slot : f.getActiveSlots().values()) { + if(slot.getId().equals("sauce") || slot.getId().contains("display")) continue; + ItemStack item = slot.getCurrentItem(); + if(item != null && FoodItem.fromItem(item) != null) return true; + } + return false; + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void takeItem(FurnitureSlotItemTakeEvent e) { Furniture f = e.getFurniture(); @@ -139,8 +149,8 @@ public void takeItem(FurnitureSlotItemTakeEvent e) { @SuppressWarnings("deprecation") public void addSauce(Player p, Furniture f, FoodItem sauce, ItemStack base) { if (hasSauce(f)) return; - // Sauce on an empty plate would never reach food added later, so keep the ladle full. - if (!hasFood(f, null)) return; + // Sauce with no food to take it would never reach food added later, so keep the ladle full. + if (!hasSauceableFood(f)) return; // Replace player ladle with empty ladle p.getInventory().setItemInMainHand( diff --git a/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java b/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java index d4ea122..92723f8 100644 --- a/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java +++ b/src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java @@ -158,6 +158,23 @@ void sauceOnAnEmptyPlateReturnsBeforeTouchingThePlayerOrLadle() { assertTrue(plate.getActiveSlots().isEmpty()); } + @Test + void sauceOnAPlateWithoutLoadedFoodReturnsBeforeTouchingThePlayerOrLadle() { + Furniture plate = new Furniture("plate", null, UUID.randomUUID()); + plate.getActiveSlots().put("item_1", new PlacedSlot(plate, "item_1")); + PlacedSlot unrecognized = new PlacedSlot(plate, "item_2"); + unrecognized.setModel(new ItemStack() { + @Override + public boolean hasItemMeta() { + return false; + } + }); + plate.getActiveSlots().put("item_2", unrecognized); + + assertDoesNotThrow(() -> new PlateManager().addSauce(null, plate, null, null)); + assertFalse(plate.hasActiveSlot("sauce")); + } + @Test void displaySlotsDoNotCountAsFood() { Furniture plate = plateWithSauceVisual();