Skip to content

Commit a2174d8

Browse files
XxFran10xXclaude
andauthored
fix: clear a plate's sauce once its last food is taken off (#48)
The sauce visual stayed on a plate after its food was taken off, so hasSauce rejected sauce for every later dish on that plate. Remove the sauce slot when the last food leaves, clear leftover sauce on plates saved before this fix, and keep the ladle full when no food on the plate can take the sauce. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent d7542c5 commit a2174d8

2 files changed

Lines changed: 123 additions & 0 deletions

File tree

‎src/main/java/net/tfminecraft/cooking/manager/PlateManager.java‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.bukkit.Sound;
1111
import org.bukkit.entity.Player;
1212
import org.bukkit.event.EventHandler;
13+
import org.bukkit.event.EventPriority;
1314
import org.bukkit.event.Listener;
1415
import org.bukkit.inventory.ItemStack;
1516
import org.bukkit.inventory.meta.ItemMeta;
@@ -31,6 +32,7 @@
3132
import net.tfminecraft.interactiblefurniture.events.FurnitureBreakEvent;
3233
import net.tfminecraft.interactiblefurniture.events.FurnitureInteractEvent;
3334
import net.tfminecraft.interactiblefurniture.events.FurnitureSlotItemAddEvent;
35+
import net.tfminecraft.interactiblefurniture.events.FurnitureSlotItemTakeEvent;
3436
import net.tfminecraft.interactiblefurniture.furniture.Furniture;
3537
import net.tfminecraft.interactiblefurniture.furniture.PlacedSlot;
3638
import net.tfminecraft.interactiblefurniture.furniture.data.DisplayData;
@@ -56,6 +58,11 @@ public void run() {
5658
}
5759

5860
public void update(Furniture f) {
61+
// Also clears plates saved with leftover sauce before the take handler existed.
62+
if(hasLeftoverSauce(f, null)) {
63+
f.removeActiveSlot("sauce");
64+
InteractibleFurniture.getInstance().getFurnitureManager().persistFurniture(f);
65+
}
5966
for(PlacedSlot slot : f.getActiveSlots().values()) {
6067
if(slot.getId().contains("display")) continue;
6168
ItemStack item = slot.getCurrentItem();
@@ -99,10 +106,51 @@ public boolean hasSauce(Furniture f) {
99106
return false;
100107
}
101108

109+
/**
110+
* True when the plate shows a sauce visual but no food would remain once {@code leavingSlot}
111+
* is gone. A leftover sauce visual makes hasSauce reject the next dish on the plate.
112+
*/
113+
boolean hasLeftoverSauce(Furniture f, String leavingSlot) {
114+
if(!f.hasActiveSlot("sauce")) return false;
115+
return !hasFood(f, leavingSlot);
116+
}
117+
118+
// An active food slot counts even without a loaded item: restore can leave the item unset
119+
// while the display still shows the food.
120+
private boolean hasFood(Furniture f, String ignoredSlot) {
121+
for(String id : f.getActiveSlots().keySet()) {
122+
if(id.equals("sauce") || id.contains("display") || id.equals(ignoredSlot)) continue;
123+
return true;
124+
}
125+
return false;
126+
}
127+
128+
// Food addSauce can actually apply the sauce to.
129+
private boolean hasSauceableFood(Furniture f) {
130+
for(PlacedSlot slot : f.getActiveSlots().values()) {
131+
if(slot.getId().equals("sauce") || slot.getId().contains("display")) continue;
132+
ItemStack item = slot.getCurrentItem();
133+
if(item != null && FoodItem.fromItem(item) != null) return true;
134+
}
135+
return false;
136+
}
137+
138+
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
139+
public void takeItem(FurnitureSlotItemTakeEvent e) {
140+
Furniture f = e.getFurniture();
141+
if(!FurnitureCache.isPlate(f)) return;
142+
// The taken slot is still active here; InteractibleFurniture removes it and saves the plate next.
143+
if(hasLeftoverSauce(f, e.getSlot().getId())) {
144+
f.removeActiveSlot("sauce");
145+
}
146+
}
147+
102148
// Keep the existing legacy text representation, formatting, and exact-string comparisons.
103149
@SuppressWarnings("deprecation")
104150
public void addSauce(Player p, Furniture f, FoodItem sauce, ItemStack base) {
105151
if (hasSauce(f)) return;
152+
// Sauce with no food to take it would never reach food added later, so keep the ladle full.
153+
if (!hasSauceableFood(f)) return;
106154

107155
// Replace player ladle with empty ladle
108156
p.getInventory().setItemInMainHand(

‎src/test/java/net/tfminecraft/cooking/manager/PlateManagerTest.java‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,79 @@ public boolean hasItemMeta() {
116116

117117
assertFalse(new PlateManager().hasSauce(plate));
118118
}
119+
120+
@Test
121+
void sauceIsLeftoverOnceTheLastFoodLeaves() {
122+
Furniture plate = plateWithSauceVisual();
123+
plate.getActiveSlots().put("item_1", new PlacedSlot(plate, "item_1"));
124+
125+
assertFalse(new PlateManager().hasLeftoverSauce(plate, null));
126+
assertTrue(new PlateManager().hasLeftoverSauce(plate, "item_1"));
127+
}
128+
129+
@Test
130+
void sauceStaysWhileOtherFoodRemains() {
131+
Furniture plate = plateWithSauceVisual();
132+
plate.getActiveSlots().put("item_1", new PlacedSlot(plate, "item_1"));
133+
plate.getActiveSlots().put("item_2", new PlacedSlot(plate, "item_2"));
134+
135+
assertFalse(new PlateManager().hasLeftoverSauce(plate, "item_1"));
136+
}
137+
138+
@Test
139+
void sauceAloneOnAPlateIsLeftover() {
140+
assertTrue(new PlateManager().hasLeftoverSauce(plateWithSauceVisual(), null));
141+
}
142+
143+
@Test
144+
void plateWithoutSauceHasNoLeftover() {
145+
Furniture plate = new Furniture("plate", null, UUID.randomUUID());
146+
plate.getActiveSlots().put("item_1", new PlacedSlot(plate, "item_1"));
147+
148+
assertFalse(new PlateManager().hasLeftoverSauce(plate, null));
149+
assertFalse(new PlateManager().hasLeftoverSauce(plate, "item_1"));
150+
}
151+
152+
@Test
153+
void sauceOnAnEmptyPlateReturnsBeforeTouchingThePlayerOrLadle() {
154+
Furniture plate = new Furniture("plate", null, UUID.randomUUID());
155+
156+
// Null interaction arguments ensure the empty-plate attempt exits before using them.
157+
assertDoesNotThrow(() -> new PlateManager().addSauce(null, plate, null, null));
158+
assertTrue(plate.getActiveSlots().isEmpty());
159+
}
160+
161+
@Test
162+
void sauceOnAPlateWithoutLoadedFoodReturnsBeforeTouchingThePlayerOrLadle() {
163+
Furniture plate = new Furniture("plate", null, UUID.randomUUID());
164+
plate.getActiveSlots().put("item_1", new PlacedSlot(plate, "item_1"));
165+
PlacedSlot unrecognized = new PlacedSlot(plate, "item_2");
166+
unrecognized.setModel(new ItemStack() {
167+
@Override
168+
public boolean hasItemMeta() {
169+
return false;
170+
}
171+
});
172+
plate.getActiveSlots().put("item_2", unrecognized);
173+
174+
assertDoesNotThrow(() -> new PlateManager().addSauce(null, plate, null, null));
175+
assertFalse(plate.hasActiveSlot("sauce"));
176+
}
177+
178+
@Test
179+
void displaySlotsDoNotCountAsFood() {
180+
Furniture plate = plateWithSauceVisual();
181+
plate.getActiveSlots().put("display_1", new PlacedSlot(plate, "display_1"));
182+
183+
assertTrue(new PlateManager().hasLeftoverSauce(plate, null));
184+
}
185+
186+
@Test
187+
void sauceOnADisplayOnlyPlateReturnsBeforeTouchingThePlayerOrLadle() {
188+
Furniture plate = new Furniture("plate", null, UUID.randomUUID());
189+
plate.getActiveSlots().put("display_1", new PlacedSlot(plate, "display_1"));
190+
191+
assertDoesNotThrow(() -> new PlateManager().addSauce(null, plate, null, null));
192+
assertFalse(plate.hasActiveSlot("sauce"));
193+
}
119194
}

0 commit comments

Comments
 (0)