fix cooking step failures (#30349)

* fix cooking step failures

* a timing issue one hopes
This commit is contained in:
warriorstar-orion
2025-09-07 05:57:02 +00:00
committed by GitHub
parent 28a2381628
commit fd7e71d0b4
5 changed files with 114 additions and 17 deletions
+47 -16
View File
@@ -52,6 +52,24 @@
/// Core function that checks if a object meets all the requirements for certain
/// recipe actions.
///
/// This is one of the thornier and grosser parts of the cooking system and most
/// people working with it or implementing recipes should never have to look at
/// this. The core idea is:
///
/// * we keep track of what recipes are still valid outcomes by testing the used
/// item against the list of recipes which are valid so far.
/// * each valid recipe is at a certain step, and check the used object against
/// [/datum/cooking/recipe_step/proc/check_conditions_met]. if we meet the
/// conditions, we track the recipe and the step.
/// * for each unique step type that we're tracking, call
/// [/datum/cooking/recipe_step/proc/follow_step] on the first instance of
/// that step type, then [/datum/cooking/recipe_step/proc/is_complete] on
/// all recipe step instances of that type, to see if we advance their
/// respective recipes.
///
/// Once a recipe reaches its final step, the tracker completes the recipe and
/// typically stops existing at that point.
/datum/cooking/recipe_tracker/proc/process_item(mob/user, obj/used)
// TODO: I *hate* passing in a user here and want to move all the necessary
// UI interactions (selecting which recipe to complete, selecting which step
@@ -61,8 +79,6 @@
var/list/completed_recipes = list()
var/list/silent_recipes = list()
var/list/attempted_step_per_recipe = list()
var/datum/cooking/recipe_step/use_step_type
for(var/datum/cooking/recipe/recipe in recipes_last_completed_step)
var/current_idx = recipes_last_completed_step[recipe]
@@ -73,11 +89,9 @@
next_step = recipe.steps[++current_idx]
var/conditions = next_step.check_conditions_met(used, src)
if(conditions == PCWJ_CHECK_VALID)
LAZYADD(valid_steps[next_step], next_step)
LAZYADD(valid_steps[next_step.type], next_step)
LAZYADD(valid_recipes[next_step.type], recipe)
attempted_step_per_recipe[recipe] = current_idx
if(!use_step_type)
use_step_type = next_step.type
match = TRUE
break
else if(conditions == PCWJ_CHECK_SILENT)
@@ -94,16 +108,33 @@
return PCWJ_PARTIAL_SUCCESS
return PCWJ_NO_STEPS
var/datum/cooking/recipe_step/sample_step = valid_steps[1]
var/step_data = sample_step.follow_step(used, src)
step_reaction_message = step_data["message"]
var/list/recipes_with_completed_steps = list()
var/list/step_data
var/complete_steps = 0
for(var/i in 1 to length(valid_recipes[use_step_type]))
var/datum/cooking/recipe/recipe = valid_recipes[use_step_type][i]
var/datum/cooking/recipe_step/recipe_step = valid_steps[i]
if(recipe_step.is_complete(used, src, step_data))
recipes_last_completed_step[recipe] = attempted_step_per_recipe[recipe]
complete_steps++
for(var/step_type in valid_steps)
// For each valid step type we only call follow_step() once since it's
// pointless to e.g. add an item to the container more than once.
//
// However, we are still calling follow_step more than once. which means
// we have to deal with the possibility that two valid steps may do two
// different things with the used item and may expect different results.
// Sojurn tried to handle this by adding a user prompt at this point,
// asking which step the player wanted to perform. I want to avoid
// throwing up interfaces during cooking, especially when unexpected, so
// for now, we do nothing, and just watch out for situations where two
// different recipe steps with incompatible end states are valid with
// the same object.
var/datum/cooking/recipe_step/sample_step = valid_steps[step_type][1]
step_data = sample_step.follow_step(used, src)
step_reaction_message = step_data["message"]
for(var/i in 1 to length(valid_recipes[step_type]))
var/datum/cooking/recipe/recipe = valid_recipes[step_type][i]
var/datum/cooking/recipe_step/recipe_step = valid_steps[step_type][i]
if(recipe_step.is_complete(used, src, step_data))
recipes_last_completed_step[recipe] = attempted_step_per_recipe[recipe]
recipes_with_completed_steps |= recipe
complete_steps++
var/obj/item/reagent_containers/cooking/container = locateUID(container_uid)
if(complete_steps)
@@ -121,8 +152,8 @@
else
return PCWJ_PARTIAL_SUCCESS
for(var/datum/cooking/recipe in recipes_last_completed_step)
if(!(recipe in valid_recipes[sample_step.type]))
for(var/recipe in recipes_last_completed_step)
if(!(recipe in recipes_with_completed_steps))
recipes_last_completed_step -= recipe
var/datum/cooking/recipe/recipe_to_complete
+8 -1
View File
@@ -8,6 +8,12 @@ RESTRICT_TYPE(/datum/cooking/recipe_step)
if("optional" in options)
optional = options["optional"]
/// See if the *used_item* meets the conditions for this recipe step. This will
/// typically be something like ensuring that a recipe step for adding a
/// specific kind of item has been passed an item of that type.
///
/// Returns one of [PCWJ_CHECK_INVALID], [PCWJ_CHECK_VALID], [PCWJ_CHECK_FULL],
/// [PCWJ_CHECK_SILENT].
/datum/cooking/recipe_step/proc/check_conditions_met(obj/used_item, datum/cooking/recipe_tracker/tracker)
SHOULD_CALL_PARENT(FALSE)
SHOULD_BE_PURE(TRUE)
@@ -26,7 +32,8 @@ RESTRICT_TYPE(/datum/cooking/recipe_step)
/datum/cooking/recipe_step/proc/follow_step(obj/used_item, datum/cooking/recipe_tracker/tracker, mob/user)
return list()
/// Special function to check if the step has been satisfied. Sometimed just following the step is enough, but not always.
/// Special function to check if the step has been satisfied. Sometimes just
/// following the step is enough, but not always.
/datum/cooking/recipe_step/proc/is_complete(obj/added_item, datum/cooking/recipe_tracker/tracker, list/step_data)
return TRUE
+5
View File
@@ -73,6 +73,11 @@
puppet.next_click = world.time
puppet.next_move = world.time
/datum/test_puppeteer/proc/alt_click_on(target, params)
var/plist = params2list(params)
plist["alt"] = TRUE
click_on(target, list2params(plist))
/datum/test_puppeteer/proc/spawn_mob_nearby(mob_type)
for(var/turf/T in RANGE_TURFS(1, puppet))
if(!T.is_blocked_turf())
+1
View File
@@ -27,6 +27,7 @@
#include "test_apc_construction.dm"
#include "test_components.dm"
#include "test_config_sanity.dm"
#include "test_cooking.dm"
#include "test_crafting_lists.dm"
#include "test_dynamic_budget.dm"
#include "test_elements.dm"
+53
View File
@@ -0,0 +1,53 @@
/datum/cooking/recipe/test_soylent
container_type = /obj/item/reagent_containers/cooking/pot
product_type = /obj/item/food/soylentgreen
steps = list(
PCWJ_ADD_ITEM(/obj/item/food/meat/human),
PCWJ_ADD_ITEM(/obj/item/food/meat/human),
PCWJ_ADD_REAGENT("water", 10),
PCWJ_USE_STOVE(J_MED, 1 SECONDS),
)
appear_in_default_catalog = FALSE
/datum/game_test/room_test/cooking/Run()
var/datum/test_puppeteer/player = new(src)
player.puppet.name = "Player"
// Burger
var/obj/structure/table/table = player.spawn_obj_nearby(__IMPLIED_TYPE__, SOUTH)
var/obj/item/reagent_containers/cooking/board/board = player.spawn_obj_nearby(__IMPLIED_TYPE__, SOUTH)
player.spawn_obj_in_hand(/obj/item/food/bun)
player.click_on(board)
TEST_ASSERT_LAST_CHATLOG(player, "You add the bun")
player.spawn_obj_in_hand(/obj/item/food/meat/patty)
player.click_on(board)
TEST_ASSERT_LAST_CHATLOG(player, "You add the patty")
player.spawn_obj_in_hand(/obj/item/food/grown/lettuce)
player.click_on(board)
TEST_ASSERT_LAST_CHATLOG(player, "You finish cooking with the cutting board")
player.alt_click_on(board)
TEST_ASSERT(locate(/obj/item/food/burger) in get_turf(board), "could not find completed burger")
// Soylent
var/obj/machinery/cooking/stovetop/stove = player.spawn_obj_nearby(__IMPLIED_TYPE__, EAST)
var/obj/item/reagent_containers/cooking/pot/pot = player.spawn_obj_in_hand(__IMPLIED_TYPE__)
player.click_on(table)
var/obj/item/food/meat/human/meat = player.spawn_obj_in_hand(__IMPLIED_TYPE__)
player.click_on(pot)
TEST_ASSERT_LAST_CHATLOG(player, "You add [meat]")
player.spawn_obj_in_hand(/obj/item/food/meat/human)
player.click_on(pot)
var/obj/item/reagent_containers/glass/beaker/beaker = player.spawn_obj_in_hand(__IMPLIED_TYPE__)
beaker.reagents.add_reagent("water", 10)
player.click_on(pot)
player.put_away(beaker)
player.click_on(pot)
player.click_on(stove, "icon-x=18&icon-y=18")
var/surface_idx = stove.clickpos_to_surface(list("icon-x" = 18, "icon-y" = 18))
var/datum/cooking_surface/surface = stove.surfaces[surface_idx]
surface.temperature = J_MED
surface.turn_on(player.puppet)
sleep(3 SECONDS)
surface.turn_off(player.puppet)
player.alt_click_on(stove, "icon-x=18&icon-y=18")
TEST_ASSERT(locate(/obj/item/food/soylentgreen) in stove.loc, "could not find complete soylent")