Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Cooking makes food an interactive part of everyday life. Players grow and gather
- **Pizzas** — place a pizza peel, put dough on it, spread a paste and add cheese, vegetables, fruit, sausage and spices, then carry the peel into a lit oven to bake. A baked pizza is taken off a slice at a time. Its toppings name it, and its food, quality and freshness come from its layers (`pizzas.yml`). Its toppings also earn it bonuses such as Saucy, Extra Cheesy or Well Seasoned (`bonuses` in `pizzas.yml`).
- **Salads** — right-click a placed bowl with chopped or cooked veg, fruit, cheese or meat, add a dressing or seasoning, and toss it with an empty hand. It stays in the bowl and is eaten from it. Its ingredients name it, its kind (fruit, greek, carrot, mixed) gives it its look, and it earns bonuses such as Dressed or Crisp (`salads.yml`).
- **Stock and mashed potatoes** — boil two bones in a cup of water in the saucepan and bottle the stock (`condiments.yml`); a soup started with stock instead of water is Rich. Put four boiled cut potatoes and a butter (with milk or salt if you like) in an empty pot, mash them without water or heat, and ladle the mash onto a plate beside the other food (`pot.yml`).
- **Pies** — line a pie tin with dough, fill it with fruit, meat, mashed potato, vegetables or cheese, and bake it in the oven like the bread tray. The whole pie comes out of the tin with an empty hand and is cut into four slices on the cutting board. Its fillings make its kind (Apple, Key Lime, Mixed Berry, Meat, Shepherd's) and name, and its food, quality and freshness come from its dough and fillings (`pies.yml`).
- **Serving and sharing** — plate dishes, ladle soup into bowls, and carve foods into portions for the table.
- **Quality and freshness** — ingredient quality and composition influence finished dishes, while aging and food tags carry preparation history through the kitchen.
- **Farming and husbandry** — crop fertility and harvest quality sit alongside animal care, breeding, genetics, and produce collection.
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/net/tfminecraft/cooking/Cooking.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
import net.tfminecraft.cooking.pizza.PizzaHandler;
import net.tfminecraft.cooking.condiment.CondimentConfig;
import net.tfminecraft.cooking.condiment.SapTapListener;
import net.tfminecraft.cooking.pie.PieConfig;
import net.tfminecraft.cooking.pie.PieHandler;
import net.tfminecraft.cooking.salad.SaladConfig;
import net.tfminecraft.cooking.pot.PotConfig;
import net.tfminecraft.cooking.salad.SaladHandler;
Expand Down Expand Up @@ -148,6 +150,8 @@ public class Cooking extends JavaPlugin {
private final SaladConfig saladConfig = new SaladConfig();
private final PotConfig potConfig = new PotConfig();
private final SaladHandler saladHandler = new SaladHandler();
private final PieConfig pieConfig = new PieConfig();
private final PieHandler pieHandler = new PieHandler();
private final MillingStoneHandler millingStoneHandler = new MillingStoneHandler();
private final TroughHandler troughHandler = new TroughHandler();
private final TroughAging troughAging = new TroughAging();
Expand Down Expand Up @@ -276,6 +280,7 @@ public void loadConfigs() {
condimentConfig.load(new File(getDataFolder(), "condiments.yml"));
saladConfig.load(new File(getDataFolder(), "salads.yml"));
potConfig.load(new File(getDataFolder(), "pot.yml"));
pieConfig.load(new File(getDataFolder(), "pies.yml"));
millingRecipeLoader.load(new File(getDataFolder(), "milling-recipes.yml"));
stationLoader.load(new File(getDataFolder(), "crafting-stations.yml"));
trackLoader.load(new File(getDataFolder(), "tags.yml"));
Expand Down Expand Up @@ -343,6 +348,7 @@ public void registerListeners() {
getServer().getPluginManager().registerEvents(pizzaHandler, this);
getServer().getPluginManager().registerEvents(new SapTapListener(), this);
getServer().getPluginManager().registerEvents(saladHandler, this);
getServer().getPluginManager().registerEvents(pieHandler, this);
getServer().getPluginManager().registerEvents(millingStoneHandler, this);
getServer().getPluginManager().registerEvents(troughHandler, this);
getServer().getPluginManager().registerEvents(ovenLifecycleHandler, this);
Expand Down Expand Up @@ -441,6 +447,7 @@ public void createConfigs() {
"condiments.yml",
"salads.yml",
"pot.yml",
"pies.yml",
"milling-recipes.yml",
"cookware.yml",
"tags.yml",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public class CraftingRecipe {
private int yield;
/** The output is the input cut differently: it keeps the input's origin, fillers and tags. One input at a time. */
private boolean inherit;
/** With inherit, each output takes an even share of its input's food (a pie's slices hold what the pie did). */
private boolean shareFood;
/** On a station with fill-models, its inputs show as the rising fill. False keeps them as themselves (olives in the cheese press). */
private boolean fill;

Expand All @@ -32,6 +34,7 @@ public CraftingRecipe(String id, ConfigurationSection config) {
this.keepOutputAge = config.getBoolean("keep-output-age", false);
this.yield = Math.max(1, config.getInt("yield", 1));
this.inherit = config.getBoolean("inherit", false);
this.shareFood = config.getBoolean("share-food", false);
this.fill = config.getBoolean("fill", true);
}

Expand Down Expand Up @@ -66,6 +69,9 @@ public int getYield() {
public boolean inherits() {
return inherit;
}
public boolean sharesFood() {
return shareFood;
}
public boolean fills() {
return fill;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,10 @@ private Built build(Player p, CraftingRecipe recipe, List<FoodItem> inputs, Stri
Map<String, Integer> ownAge = recipe.keepsOutputAge() ? ageOf(item) : null;
CompositionApplier.apply(item, composed);
if (ownAge != null) restoreAge(item, ownAge);
if (recipe.inherits() && !inputs.isEmpty()) inherit(item, inputs.get(0));
if (recipe.inherits() && !inputs.isEmpty()) {
inherit(item, inputs.get(0));
if (recipe.sharesFood()) share(item, inputs.get(0), recipe.getYield());
}
ItemStack output = ItemBuilder.buildSingleWithQuality(item, null, composed.getFinalQuality());
if (output == null || output.getType().isAir()) return null;
return new Built(item, output);
Expand Down Expand Up @@ -521,6 +524,12 @@ private static void inherit(FoodItem output, FoodItem input) {
}
}

/** A pie's slice holds its share of the pie: its food over the slices it's cut into, at the pie's nutrition. */
private static void share(FoodItem output, FoodItem input, int pieces) {
output.setBaseFood(input.getBaseFood() / pieces);
output.setBaseNutrition(input.getBaseNutrition());
}

private static Map<String, Integer> ageOf(FoodItem item) {
Map<String, Integer> age = new HashMap<>();
for (TagTrack track : item.getTagTracks()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import net.tfminecraft.cooking.baking.BakingTrayRegistry;
import net.tfminecraft.cooking.heat.HeatSources;
import net.tfminecraft.cooking.pie.PieConfig;
import net.tfminecraft.cooking.pie.PieTin;
import net.tfminecraft.cooking.pizza.PizzaConfig;
import net.tfminecraft.interactiblefurniture.events.FurnitureSlotFurnitureAddEvent;
import net.tfminecraft.interactiblefurniture.furniture.Furniture;
Expand All @@ -27,8 +29,13 @@ public void onFurnitureAdd(FurnitureSlotFurnitureAddEvent event) {
if (nested == null || BakingTrayRegistry.isTray(nested) || PizzaConfig.isPeel(nested)) {
return;
}
String refused = PieConfig.isTin(nested) ? PieTin.ovenRefusal(nested)
: "§cOnly baking trays, pie tins and pizza peels can go in the oven.";
if (refused == null) {
return;
}

event.setCancelled(true);
event.getPlayer().sendMessage("§cOnly baking trays and pizza peels can go in the oven.");
event.getPlayer().sendMessage(refused);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import net.tfminecraft.cooking.heat.HeatSources;
import net.tfminecraft.cooking.item.FoodItem;
import net.tfminecraft.cooking.item.tag.TagTrack;
import net.tfminecraft.cooking.pie.PieBaking;
import net.tfminecraft.cooking.pie.PieConfig;
import net.tfminecraft.cooking.pizza.PizzaBaking;
import net.tfminecraft.cooking.pizza.PizzaConfig;
import net.tfminecraft.interactiblefurniture.furniture.Furniture;
Expand Down Expand Up @@ -52,6 +54,10 @@ private void tick() {
PizzaBaking.tick(consumer, tray);
continue;
}
if (PieConfig.isTin(tray)) {
PieBaking.tick(consumer, tray);
continue;
}
BakingTrayRecipe recipe = BakingTrayRegistry.getByFurniture(tray);
if (recipe == null) {
continue;
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/net/tfminecraft/cooking/pie/PieBaking.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package net.tfminecraft.cooking.pie;

import net.tfminecraft.cooking.heat.HeatSources;
import net.tfminecraft.interactiblefurniture.InteractibleFurniture;
import net.tfminecraft.interactiblefurniture.furniture.Furniture;

/**
* Bakes the pie in a tin in an oven, one second at a time while the oven is hot: it cooks after pies.yml's
* cook-seconds and burns at burn-seconds, and its crust then shows cooked or burnt. Only a filled pie bakes.
*/
public final class PieBaking {

private PieBaking() {}

public static void tick(Furniture oven, Furniture tin) {
int baked = PieTin.baked(tin);
if (baked >= PieTin.BURNT || !PieTin.ready(tin) || !HeatSources.consumerHasHeat(oven)) return;
int elapsed = PieTin.elapsed(tin) + 1;
int next = baked;
if (elapsed >= PieConfig.burnSeconds()) next = PieTin.BURNT;
else if (elapsed >= PieConfig.cookSeconds()) next = PieTin.COOKED;
PieTin.setBake(tin, elapsed, next);
if (next != baked) PieTin.remodel(tin);
InteractibleFurniture.getInstance().getFurnitureManager().markDirty(tin);
}
}
139 changes: 139 additions & 0 deletions src/main/java/net/tfminecraft/cooking/pie/PieConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package net.tfminecraft.cooking.pie;

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;

import net.tfminecraft.cooking.item.FoodItem;
import net.tfminecraft.cooking.salad.SaladKind;
import net.tfminecraft.cooking.sandwich.DishBonus;
import net.tfminecraft.cooking.sandwich.SandwichLayer;
import net.tfminecraft.interactiblefurniture.furniture.Furniture;

/**
* pies.yml: the tins pies are baked in, the dough that lines them, the fillings they take, the kinds of pie those make,
* how long they bake and the bonuses they earn.
*/
public final class PieConfig {
private static Set<String> furniture = Set.of();
private static String slot = "pie";
private static int minFillings = 1;
private static int maxFillings = 3;
private static String output = "meal(type=pie;origin=Mixed)";
private static int cookSeconds = 5;
private static int burnSeconds = 10;
private static SandwichLayer crust;
private static List<SandwichLayer> fillings = List.of();
private static List<SaladKind> kinds = List.of();
private static List<DishBonus> bonuses = List.of();

public void load(File file) {
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
Set<String> tins = new HashSet<>();
for (String id : config.getStringList("furniture")) tins.add(id.toLowerCase(Locale.ROOT));
furniture = tins;
slot = config.getString("slot", "pie");
maxFillings = Math.max(1, config.getInt("max-fillings", 3));
minFillings = Math.max(1, Math.min(maxFillings, config.getInt("min-fillings", 1)));
output = config.getString("output", "meal(type=pie;origin=Mixed)");
cookSeconds = Math.max(1, config.getInt("bake.cook-seconds", 5));
burnSeconds = Math.max(cookSeconds + 1, config.getInt("bake.burn-seconds", 10));
ConfigurationSection crustSection = config.getConfigurationSection("crust");
crust = crustSection == null ? null : new SandwichLayer("crust", crustSection);
List<SandwichLayer> loaded = new ArrayList<>();
ConfigurationSection section = config.getConfigurationSection("fillings");
if (section != null) {
for (String id : section.getKeys(false)) {
ConfigurationSection filling = section.getConfigurationSection(id);
if (filling != null) loaded.add(new SandwichLayer(id, filling));
}
}
fillings = loaded;
List<SaladKind> kindsLoaded = new ArrayList<>();
ConfigurationSection kindSection = config.getConfigurationSection("kinds");
if (kindSection != null) {
for (String origin : kindSection.getKeys(false)) {
ConfigurationSection kind = kindSection.getConfigurationSection(origin);
if (kind != null) kindsLoaded.add(new SaladKind(origin, kind));
}
}
kinds = kindsLoaded;
bonuses = DishBonus.load(config.getConfigurationSection("bonuses"));
}

public static boolean isTin(Furniture f) {
return f != null && f.getType() != null && furniture.contains(f.getType().getId().toLowerCase(Locale.ROOT));
}

/** The tin's slot the dough lies in, showing the pie. */
public static String slot() {
return slot;
}

/** The dough that lines the tin, or null when this food isn't it. */
public static SandwichLayer crustFor(FoodItem food) {
return crust != null && crust.matches(food) ? crust : null;
}

/** The dough lining the tin, not a filling. */
public static boolean isCrust(SandwichLayer layer) {
return layer != null && layer == crust;
}

/** The filling this food makes, or null when it doesn't go in a pie. */
public static SandwichLayer fillingFor(FoodItem food) {
for (SandwichLayer filling : fillings) {
if (filling.matches(food)) return filling;
}
return null;
}

/** The crust or filling this food makes, or null. */
public static SandwichLayer layerFor(FoodItem food) {
SandwichLayer layer = crustFor(food);
return layer != null ? layer : fillingFor(food);
}

/** The first kind these fillings make, or null when none takes them. */
public static SaladKind kindFor(List<FoodItem> foods, List<SandwichLayer> made) {
for (SaladKind kind : kinds) {
if (kind.matches(foods, made)) return kind;
}
return null;
}

/** Fillings a pie needs before it bakes. */
public static int minFillings() {
return minFillings;
}

public static int maxFillings() {
return maxFillings;
}

/** The whole pie (types.yml type, origin, tags); its food and nutrition are replaced by what's in it. */
public static String output() {
return output;
}

/** Seconds in a hot oven until the pie is cooked. */
public static int cookSeconds() {
return cookSeconds;
}

/** Seconds in a hot oven, counted from when it went in, until the pie burns. */
public static int burnSeconds() {
return burnSeconds;
}

/** The tags a pie earns from its fillings (Hearty...). */
public static List<DishBonus> bonuses() {
return bonuses;
}
}
Loading
Loading