Customer robots can now order ice cream. (#66073)

This commit is contained in:
Ghom
2022-04-26 17:50:49 -05:00
committed by GitHub
parent 0c5e854023
commit 03900dcdf4
11 changed files with 241 additions and 81 deletions
@@ -1,4 +1,8 @@
///Restaurant
///(wanted_item) custom order signal sent when checking if the order is correct.
#define COMSIG_ITEM_IS_CORRECT_CUSTOM_ORDER "item_is_correct_order"
#define COMPONENT_CORRECT_ORDER (1<<0)
///(customer, container) venue signal sent when a venue sells an item. source is the thing sold, which can be a datum, so we send container for location checks
#define COMSIG_ITEM_SOLD_TO_CUSTOMER "item_sold_to_customer"
+2 -1
View File
@@ -117,4 +117,5 @@
#define ICE_CREAM_BLAND "bland"
#define DEFAULT_MAX_ICE_CREAM_SCOOPS 3
// the vertical distance in pixels from an ice cream scoop and another.
#define ICE_CREAM_SCOOP_OFFSET 4
@@ -104,7 +104,9 @@
var/mob/living/simple_animal/robot_customer/customer_pawn = controller.pawn
var/datum/customer_data/customer_data = controller.blackboard[BB_CUSTOMER_CUSTOMERINFO]
var/mob/living/greytider = controller.blackboard[BB_CUSTOMER_CURRENT_TARGET]
if(greytider) //usually if we stop waiting, it's because we're done with the venue. but in this case we're beating some dude up so don't switch to leaving
//usually if we stop waiting, it's because we're done with the venue. but here we're either beating some dude up
//or are being qdeleted and don't want runtime errors, so don't switch to leaving
if(greytider || QDELETED(src))
return
controller.blackboard[BB_CUSTOMER_LEAVING] = TRUE
customer_pawn.update_icon() //They might have a special leaving accesoiry (french flag)
@@ -11,6 +11,12 @@
BB_CUSTOMER_SAID_CANT_FIND_SEAT_LINE = FALSE)
planning_subtrees = list(/datum/ai_planning_subtree/robot_customer)
/datum/ai_controller/robot_customer/Destroy()
// clear possible datum refs
blackboard[BB_CUSTOMER_CURRENT_ORDER] = null
blackboard[BB_CUSTOMER_CUSTOMERINFO] = null
return ..()
/datum/ai_controller/robot_customer/TryPossessPawn(atom/new_pawn)
if(!istype(new_pawn, /mob/living/simple_animal/robot_customer))
return AI_CONTROLLER_INCOMPATIBLE
@@ -38,6 +44,10 @@
if(!blackboard[BB_CUSTOMER_EATING])
blackboard[BB_CUSTOMER_EATING] = TRUE
attending_venue.on_get_order(pawn, order_item)
var/our_order = blackboard[BB_CUSTOMER_CURRENT_ORDER]
if(isdatum(our_order))
qdel(our_order)
blackboard[BB_CUSTOMER_CURRENT_ORDER] = null
///Called when
@@ -1,4 +1,3 @@
#define SCOOP_OFFSET 4
#define SWEETENER_PER_SCOOP 10
#define EXTRA_MAX_VOLUME_PER_SCOOP 20
@@ -64,6 +63,10 @@
else
RegisterSignal(owner, COMSIG_ATOM_UPDATE_DESC, .proc/on_update_desc)
RegisterSignal(owner, COMSIG_ITEM_IS_CORRECT_CUSTOM_ORDER, .proc/check_food_order)
RegisterSignal(owner, COMSIG_ITEM_SOLD_TO_CUSTOMER, .proc/sell_ice_cream)
if(prefill_flavours)
for(var/entry in prefill_flavours)
var/list/flavour_args = list(src) + prefill_flavours[entry]
@@ -129,7 +132,7 @@
overlay.pixel_x = x_offset
overlay.pixel_y = y_offset + added_offset
new_overlays += overlay
added_offset += SCOOP_OFFSET
added_offset += ICE_CREAM_SCOOP_OFFSET
/// Attack the ice cream vat to get some ice cream. This will change as new ways of getting ice cream are added.
/datum/component/ice_cream_holder/proc/on_item_attack_obj(obj/item/source, obj/target, mob/user)
@@ -150,6 +153,35 @@
to_chat(user, span_warning("[source] can't hold anymore ice cream!"))
return COMPONENT_CANCEL_ATTACK_CHAIN
/datum/component/ice_cream_holder/proc/check_food_order(obj/item/source, datum/custom_order/our_order)
SIGNAL_HANDLER
if(!istype(our_order, /datum/custom_order/icecream))
return FALSE
var/datum/custom_order/icecream/icecream_order = our_order
if(parent.type != icecream_order.cone_type) //check that the cone type matches
return FALSE
// We don't want to stop ice creams from being sold because of their order. we aren't that finnicky.
var/our_scoops = scoops.Copy()
sortTim(our_scoops, cmp = /proc/cmp_text_asc)
//Make sure the flavors and number of scoops match.
if(compare_list(our_scoops, icecream_order.wanted_flavors))
return COMPONENT_CORRECT_ORDER
/datum/component/ice_cream_holder/proc/sell_ice_cream(obj/item/source, mob/living/simple_animal/robot_customer/sold_to, obj/item/container)
SIGNAL_HANDLER
//the price of ice cream scales with the number of scoops. Yummy.
var/venue_price = length(scoops) * FOOD_PRICE_TRASH * 2
var/datum/venue/venue_to_pay = sold_to.ai_controller?.blackboard[BB_CUSTOMER_ATTENDING_VENUE]
new /obj/item/holochip(get_turf(source), venue_price)
venue_to_pay.total_income += venue_price
playsound(get_turf(source), 'sound/effects/cashregister.ogg', 60, TRUE)
/////ICE CREAM FLAVOUR DATUM STUFF
GLOBAL_LIST_INIT_TYPED(ice_cream_flavours, /datum/ice_cream_flavour, init_ice_cream_flavours())
@@ -277,6 +309,5 @@ GLOBAL_LIST_INIT_TYPED(ice_cream_flavours, /datum/ice_cream_flavour, init_ice_cr
desc = "filled with anemic, flavorless icecream. You wonder why this was ever scooped..."
hidden = TRUE
#undef SCOOP_OFFSET
#undef SWEETENER_PER_SCOOP
#undef EXTRA_MAX_VOLUME_PER_SCOOP
@@ -67,12 +67,41 @@
current_visitors += new_customer
/datum/venue/proc/order_food(mob/living/simple_animal/robot_customer/customer_pawn, datum/customer_data/customer_data)
return
var/order = pick_weight(customer_data.orderable_objects[venue_type])
var/image/food_image
var/food_line
if(ispath(order, /datum/custom_order)) // generate the special order
var/datum/custom_order/custom_order = new order(src)
food_image = custom_order.get_order_appearance(src)
food_line = custom_order.get_order_line(src)
order = custom_order.dispense_order()
else
food_image = get_food_appearance(order)
food_line = order_food_line(order)
customer_pawn.say(food_line)
// common code for the food thoughts appearance
food_image.loc = customer_pawn
food_image.pixel_y = 32
food_image.pixel_x = 16
food_image.plane = HUD_PLANE
food_image.appearance_flags = RESET_COLOR
customer_pawn.hud_to_show_on_hover = customer_pawn.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/food_demands, "food_thoughts", food_image)
return order
///Checks if the object used is correct for the venue
/datum/venue/proc/is_correct_order(atom/movable/object_used, wanted_item)
if(istype(wanted_item, /datum/custom_order))
var/datum/custom_order/custom_order = wanted_item
return custom_order.is_correct_order(object_used)
return FALSE
///gets the appearance of the ordered object that shows up when hovering your cursor over the customer mob.
/datum/venue/proc/get_food_appearance(order)
return
///The line the robot says when ordering
/datum/venue/proc/order_food_line(order)
return "broken venue pls call a coder"
@@ -0,0 +1,112 @@
/**
* custom order datums
* Used for less generic orders (ice cream for example)
* without snowflaking venue and customer code too much.
*/
/datum/custom_order
/**
* Returns the object that will be stored in the controller blackboard for the customer bot.
* Normally but not necessarily, this would be the custom order itself. The moth clothing is a
* good example of an exception to that.
*/
/datum/custom_order/proc/dispense_order()
return src
///Whether or not the order is correct. Only relevant if dispense_order didn't return another object.
/datum/custom_order/proc/is_correct_order(obj/item/object_used)
if(SEND_SIGNAL(object_used, COMSIG_ITEM_IS_CORRECT_CUSTOM_ORDER, src) & COMPONENT_CORRECT_ORDER)
return TRUE
/// Returns the appearance of the order that appears when hovering over the mob with the cursor
/datum/custom_order/proc/get_order_appearance()
stack_trace("[type]/get_order_appearance() not set")
return image(icon = 'icons/obj/machines/restaurant_portal.dmi' , icon_state = "thought_bubble") // empty thought bubble
/// Returns the order line shout by the mob and also shown to the player when examining it.
/datum/custom_order/proc/get_order_line()
stack_trace("[type]/get_order_line() not set")
return "broken custom order pls call a coder"
/datum/custom_order/moth_clothing
/// The item type that we want to order, usually clothing
var/wanted_clothing_type
/datum/custom_order/moth_clothing/New(datum/venue/our_venue)
var/mob/living/carbon/buffet = our_venue.restaurant_portal?.turned_on_portal?.resolve()
if (!istype(buffet)) // Always asks for the clothes that you have on, but this is a fallback.
wanted_clothing_type = pick_weight(
/obj/item/clothing/head/chefhat = 3,
/obj/item/clothing/shoes/sneakers/black = 3,
/obj/item/clothing/gloves/color/black = 1,
)
return
var/list/orderable = list()
if (!QDELETED(buffet.head))
orderable[buffet.head.type] = 5
if (!QDELETED(buffet.gloves))
orderable[buffet.gloves.type] = 5
if (!QDELETED(buffet.shoes))
orderable[buffet.shoes.type] = 1
wanted_clothing_type = pick_weight(orderable)
/datum/custom_order/moth_clothing/get_order_appearance(datum/venue/our_venue)
return our_venue.get_food_appearance(wanted_clothing_type)
/datum/custom_order/moth_clothing/get_order_line(datum/venue/our_venue)
return our_venue.order_food_line(wanted_clothing_type)
/datum/custom_order/moth_clothing/dispense_order()
. = wanted_clothing_type
qdel(src) // This datum is no longer needed.
/datum/custom_order/icecream
/// The list of flavors we want.
var/list/wanted_flavors = list()
/// The type of cone we want the ice cream served in
var/obj/item/food/icecream/cone_type = /obj/item/food/icecream
/// stores tha name of our order generated on New()
var/icecream_name
/datum/custom_order/icecream/New()
if(prob(33))
cone_type = /obj/item/food/icecream/chocolate
var/static/list/possible_flavors = list()
for(var/flavour as anything in GLOB.ice_cream_flavours)
//only request standard flavors that are available from the ice cream vat
if(!GLOB.ice_cream_flavours[flavour].hidden && flavour != ICE_CREAM_CUSTOM)
possible_flavors += flavour
for(var/iteration in 1 to rand(1, DEFAULT_MAX_ICE_CREAM_SCOOPS))
var/chosen_flavor = pick(possible_flavors)
wanted_flavors += chosen_flavor
var/list/unique_list = unique_list(wanted_flavors)
if(wanted_flavors.len > 1 && length(unique_list) == 1)
icecream_name = "[make_tuple(wanted_flavors.len)] [wanted_flavors[1]] ice cream ([initial(cone_type.name)])"
else
sortTim(wanted_flavors, cmp = /proc/cmp_text_asc)
icecream_name = "[english_list(wanted_flavors)] ice cream ([initial(cone_type.name)])"
/datum/custom_order/icecream/get_order_line(datum/venue/our_venue)
return "I'll take \a [icecream_name]"
/datum/custom_order/icecream/get_order_appearance(datum/venue/our_venue)
var/image/food_image = image(icon = 'icons/obj/machines/restaurant_portal.dmi' , icon_state = "thought_bubble")
var/image/i_scream = image('icons/obj/kitchen.dmi', initial(cone_type.icon_state))
var/added_offset = 0
for(var/flavor in wanted_flavors)
var/image/scoop = image('icons/obj/kitchen.dmi', GLOB.ice_cream_flavours[flavor].icon_state)
scoop.pixel_y = added_offset
i_scream.overlays += scoop
added_offset += ICE_CREAM_SCOOP_OFFSET
food_image.add_overlay(i_scream)
return food_image
@@ -50,12 +50,6 @@
/datum/customer_data/proc/can_use(datum/venue/venue)
return TRUE
/// Gets the order of this customer.
/// You want to override this if you have dynamic orders, such as the moth tourists requesting the chef's clothes.
/// If the list of orders are static, just modify orderable_objects.
/datum/customer_data/proc/get_order(datum/venue/venue)
return pick_weight(orderable_objects[venue.venue_type])
/datum/customer_data/proc/get_overlays(mob/living/simple_animal/robot_customer/customer)
return
@@ -87,6 +81,7 @@
/obj/item/food/burger/baconburger = 10,
/obj/item/food/pancakes = 4,
/obj/item/food/eggsausage = 5,
/datum/custom_order/icecream = 14,
),
VENUE_BAR = list(
/datum/reagent/consumable/ethanol/b52 = 6,
@@ -123,9 +118,10 @@
/obj/item/food/pizza/margherita = 2,
/obj/item/food/lasagna = 4,
/obj/item/food/cannoli = 3,
/obj/item/food/salad/risotto =5,
/obj/item/food/salad/risotto = 5,
/obj/item/food/eggplantparm = 3,
/obj/item/food/cornuto = 2,
/datum/custom_order/icecream = 10,
),
VENUE_BAR = list(
/datum/reagent/consumable/ethanol/fanciulli = 5,
@@ -161,6 +157,7 @@
/obj/item/food/soup/onion = 4,
/obj/item/food/pie/berryclafoutis = 2,
/obj/item/food/omelette = 15,
/datum/custom_order/icecream = 6,
),
VENUE_BAR = list(
/datum/reagent/consumable/ethanol/champagne = 10,
@@ -206,6 +203,7 @@
/obj/item/food/chawanmushi = 4,
/obj/item/food/muffin/berry = 2,
/obj/item/food/beef_stroganoff = 2,
/datum/custom_order/icecream = 4,
),
VENUE_BAR = list(
/datum/reagent/consumable/ethanol/sake = 8,
@@ -273,12 +271,9 @@
speech_sound = 'sound/creatures/tourist/tourist_talk_moth.ogg'
// Always asks for the clothes that you have on, but this is a fallback.
orderable_objects = list(
VENUE_RESTAURANT = list(
/obj/item/clothing/head/chefhat = 3,
/obj/item/clothing/shoes/sneakers/black = 3,
/obj/item/clothing/gloves/color/black = 1,
/datum/custom_order/moth_clothing = 1,
),
)
@@ -292,27 +287,10 @@
// If it takes any more effort, it loses a bit of the comedy.
// Therefore, only show up if it's reasonable for that gag to happen.
/datum/customer_data/moth/can_use(datum/venue/venue)
return !isnull(get_dynamic_order(venue))
/datum/customer_data/moth/proc/get_dynamic_order(datum/venue/venue)
var/mob/living/carbon/buffet = venue.restaurant_portal?.turned_on_portal?.resolve()
if (!istype(buffet))
return
var/list/orderable = list()
if (!QDELETED(buffet.head))
orderable[buffet.head] = 5
if (!QDELETED(buffet.gloves))
orderable[buffet.gloves] = 5
if (!QDELETED(buffet.shoes))
orderable[buffet.shoes] = 1
if (orderable.len)
var/datum/order = pick_weight(orderable)
return order.type
if (!istype(buffet) || !QDELETED(buffet.head) || !QDELETED(buffet.gloves) || !QDELETED(buffet.shoes))
return FALSE
return TRUE
/datum/customer_data/moth/proc/get_wings(mob/living/simple_animal/robot_customer/customer)
var/customer_ref = WEAKREF(customer)
@@ -346,12 +324,6 @@
return overlays
/datum/customer_data/moth/get_order(datum/venue/venue)
var/dynamic_order = get_dynamic_order(venue)
// Fall back to basic clothing.
return dynamic_order || ..()
/datum/customer_data/mexican
base_icon_state = "mexican"
prefix_file = "strings/names/mexican_prefix.txt"
@@ -369,6 +341,7 @@
/obj/item/food/pie/dulcedebatata = 2,
/obj/item/food/cubannachos = 3,
/obj/item/food/stuffedlegion = 1,
/datum/custom_order/icecream = 2,
),
VENUE_BAR = list(
/datum/reagent/consumable/ethanol/whiskey = 6,
@@ -412,6 +385,7 @@
/obj/item/food/full_english = 2,
/obj/item/food/soup/indian_curry = 3,
/obj/item/food/beef_wellington_slice = 2,
/datum/custom_order/icecream = 8
),
VENUE_BAR = list(
/datum/reagent/consumable/ethanol/ale = 10,
@@ -19,36 +19,28 @@
/datum/customer_data/malfunction = 1,
)
/datum/venue/restaurant/order_food(mob/living/simple_animal/robot_customer/customer_pawn, datum/customer_data/customer_data)
var/obj/item/object_to_order = customer_data.get_order(src)
. = object_to_order
customer_pawn.say(order_food_line(object_to_order))
var/appearance = SSrestaurant.food_appearance_cache[object_to_order]
/datum/venue/restaurant/get_food_appearance(order)
var/appearance = SSrestaurant.food_appearance_cache[order]
if(!appearance) //We havn't made this one before, do so now.
var/obj/item/temp_object = new object_to_order() //Make a temp object so we can see it including any overlays
var/obj/item/temp_object = new order() //Make a temp object so we can see it including any overlays
appearance = temp_object.appearance //And then steal its appearance
SSrestaurant.food_appearance_cache[object_to_order] = appearance //and cache it for future orders
SSrestaurant.food_appearance_cache[order] = appearance //and cache it for future orders
qdel(temp_object)
var/image/I = image(icon = 'icons/obj/machines/restaurant_portal.dmi' , icon_state = "thought_bubble", loc = customer_pawn)
var/image/food_image = new
food_image.appearance = appearance
food_image.underlays += mutable_appearance(icon = 'icons/obj/machines/restaurant_portal.dmi' , icon_state = "thought_bubble")
I.appearance = appearance
I.underlays += mutable_appearance(icon = 'icons/obj/machines/restaurant_portal.dmi' , icon_state = "thought_bubble")
I.pixel_y = 32
I.pixel_x = 16
I.plane = HUD_PLANE
I.appearance_flags = RESET_COLOR
customer_pawn.hud_to_show_on_hover = customer_pawn.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/food_demands, "food_thoughts", I)
return food_image
/datum/venue/restaurant/is_correct_order(atom/movable/object_used, wanted_item)
return object_used.type == wanted_item
. = ..()
return . || object_used.type == wanted_item
/datum/venue/restaurant/order_food_line(obj/item/order)
return "I'll take \a [initial(order.name)]"
/datum/venue/restaurant/order_food_line(order)
var/obj/item/object_to_order = order
return "I'll take \a [initial(object_to_order.name)]"
/datum/venue/restaurant/on_get_order(mob/living/simple_animal/robot_customer/customer_pawn, obj/item/order_item)
. = ..()
@@ -90,10 +82,9 @@
/datum/customer_data/malfunction = 1,
)
/datum/venue/bar/order_food(mob/living/simple_animal/robot_customer/customer_pawn, datum/customer_data/customer_data)
var/datum/reagent/reagent_to_order = pick_weight(customer_data.orderable_objects[venue_type])
/datum/venue/bar/order_food/get_food_appearance(order)
var/glass_visual
var/datum/reagent/reagent_to_order = order
if(initial(reagent_to_order.glass_icon_state))
glass_visual = initial(reagent_to_order.glass_icon_state)
@@ -102,22 +93,16 @@
else if(initial(reagent_to_order.fallback_icon_state))
glass_visual = initial(reagent_to_order.fallback_icon_state)
else
CRASH("[reagent_to_order] has no icon sprite for restaurant code, please set a fallback_icon_state for this reagent.")
stack_trace("[reagent_to_order] has no icon sprite for restaurant code, please set a fallback_icon_state for this reagent.")
customer_pawn.say(order_food_line(reagent_to_order))
var/image/food_image = image(icon = 'icons/obj/machines/restaurant_portal.dmi' , icon_state = "thought_bubble")
food_image.add_overlay(mutable_appearance('icons/obj/drinks.dmi', glass_visual))
var/image/I = image(icon = 'icons/obj/machines/restaurant_portal.dmi' , icon_state = "thought_bubble", loc = customer_pawn)
I.add_overlay(mutable_appearance('icons/obj/drinks.dmi', glass_visual))
I.pixel_y = 32
I.pixel_x = 16
I.plane = HUD_PLANE
I.appearance_flags = RESET_COLOR
customer_pawn.hud_to_show_on_hover = customer_pawn.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/food_demands, "food_thoughts", I)
return food_image
return reagent_to_order
/datum/venue/bar/order_food_line(datum/reagent/order)
return "I'll take a glass of [initial(order.name)]"
/datum/venue/bar/order_food_line(order)
var/datum/reagent/reagent_to_order = order
return "I'll take a glass of [initial(reagent_to_order.name)]"
/datum/venue/bar/on_get_order(mob/living/simple_animal/robot_customer/customer_pawn, obj/item/order_item)
var/datum/reagent/consumable/ordered_reagent_type = customer_pawn.ai_controller.blackboard[BB_CUSTOMER_CURRENT_ORDER]
@@ -135,9 +120,13 @@
///The bar needs to have a minimum amount of the reagent
/datum/venue/bar/is_correct_order(object_used, wanted_item)
. = ..()
if(.)
return
if(istype(object_used, /obj/item/reagent_containers/food/drinks))
var/obj/item/reagent_containers/food/drinks/potential_drink = object_used
return potential_drink.reagents.has_reagent(wanted_item, VENUE_BAR_MINIMUM_REAGENTS)
/obj/machinery/restaurant_portal/bar
linked_venue = /datum/venue/bar
@@ -94,4 +94,11 @@
. = ..()
if(ai_controller.blackboard[BB_CUSTOMER_CURRENT_ORDER])
var/datum/venue/attending_venue = ai_controller.blackboard[BB_CUSTOMER_ATTENDING_VENUE]
. += span_notice("Their order was: \"[attending_venue.order_food_line(ai_controller.blackboard[BB_CUSTOMER_CURRENT_ORDER])].\"")
var/wanted_item = ai_controller.blackboard[BB_CUSTOMER_CURRENT_ORDER]
var/order
if(istype(wanted_item, /datum/custom_order))
var/datum/custom_order/custom_order = wanted_item
order = custom_order.get_order_line(attending_venue)
else
order = attending_venue.order_food_line(wanted_item)
. += span_notice("Their order was: \"[order].\"")
+1
View File
@@ -2816,6 +2816,7 @@
#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_soup.dm"
#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_spaghetti.dm"
#include "code\modules\food_and_drinks\restaurant\_venue.dm"
#include "code\modules\food_and_drinks\restaurant\custom_order.dm"
#include "code\modules\food_and_drinks\restaurant\generic_venues.dm"
#include "code\modules\food_and_drinks\restaurant\customers\_customer.dm"
#include "code\modules\games\cas.dm"