Files
Aurora.3/code/modules/cooking/machinery/smartfridge.dm
T
b3fe100b25 Added some starting gear for medical. (#21939)
Supercedes #21909

Adds a single bottle of bicaridine, kelotane, and dexalin to the medical
smartfridge on the Horizon.

Also adds bicaridine, tricordrazine, and dermaline to the cargo order
screen. Butazoline and dermaline are priced as premium medicines.
Tricordrazine is priced cheap.

---------

Signed-off-by: Cody Brittain <1779662+Generalcamo@users.noreply.github.com>
Co-authored-by: VMSolidus <evilexecutive@gmail.com>
2026-03-05 10:41:27 +00:00

615 lines
18 KiB
Plaintext

/* SmartFridge. Much todo
*/
/obj/machinery/smartfridge
name = "\improper SmartFridge"
icon = 'icons/obj/machinery/smartfridge.dmi'
icon_state = "smartfridge"
layer = 2.9
density = 1
anchored = 1
idle_power_usage = 5
active_power_usage = 100
atom_flags = ATOM_FLAG_NO_REACT
opacity = FALSE
var/ui_sort_alphabetically = TRUE
var/global/max_n_of_items = 999 // Sorry but the BYOND infinite loop detector doesn't look things over 1000.
var/item_quants = list()
var/seconds_electrified = 0;
var/shoot_inventory = 0
var/locked = 0
var/scan_id = 1
var/is_secure = 0
var/machineselect = 0
var/list/accepted_items = list(/obj/item/reagent_containers/food/snacks/grown, /obj/item/seeds, /obj/item/mollusc)
/// List of items that the machine starts with upon spawn
var/list/initial_contents
/// Is this smartfridge going to have a glowing screen? (Drying Racks are not)
var/has_emissive = TRUE
var/cooling = 0 //Whether or not to vend products at the cooling temperature
var/heating = 0 //Whether or not to vend products at the heating temperature
var/cooling_temperature = T0C + 5 //Best temp for soda.
var/heating_temperature = T0C + 57 //Best temp for coffee.
// what icon overlay to use to show its contents - set to NULL if no contents.
var/contents_path = "-plant"
var/display_tiers = 3
var/display_tier_amt = 25
component_types = list(
/obj/item/circuitboard/smartfridge,
/obj/item/stock_parts/manipulator,
/obj/item/stock_parts/manipulator,
/obj/item/stock_parts/manipulator
)
var/datum/wires/smartfridge/wires = null
atmos_canpass = CANPASS_DENSITY
VAR_PRIVATE/static/list/base64_cache = list()
/obj/machinery/smartfridge/secure
is_secure = 1
/obj/machinery/smartfridge/stocked
var/list/starting_produce = list(
"apple" = 24,
"banana" = 24,
"berry" = 24,
"cabbage" = 24,
"carrot" = 24,
"chanterelle" = 16,
"cherry" = 8,
"chili" = 24,
"cacao" = 8,
"corn" = 24,
"earthen-root" = 16,
"eggplant" = 16,
"garlic" = 8,
"grape" = 8,
"lemon" = 16,
"lime" = 16,
"Messa's tear" = 16,
"dirt berries" = 16,
"onion" = 24,
"orange" = 24,
"peanut" = 24,
"plump helmet" = 24,
"poppy" = 16,
"potato" = 24,
"pumpkin" = 16,
"rice" = 24,
"soybean" = 24,
"tomato" = 24,
"wheat" = 8,
"watermelon" = 8,
"white-beet" = 8,
"dyn" = 24,
"wulumunusha" = 24
)
/obj/machinery/smartfridge/stocked/Initialize()
. = ..()
for(var/seed in starting_produce)
var/produce_amount = starting_produce[seed]
for(var/i in 1 to produce_amount)
var/datum/seed/chosen_seed = SSplants.seeds[seed]
if(chosen_seed)
chosen_seed.spawn_seed(src)
for(var/obj/item/reagent_containers/food/snacks/grown/g in contents)
item_quants[g.name]++
update_overlays()
update_static_data_for_all_viewers()
/obj/machinery/smartfridge/Initialize(mapload)
. = ..()
if(is_secure)
wires = new/datum/wires/smartfridge/secure(src)
else
wires = new(src)
if(islist(initial_contents))
for(var/obj/item/typekey as anything in initial_contents)
var/amount = initial_contents[typekey]
if(isnull(amount))
amount = 1
for(var/i in 1 to amount)
new typekey(src)
item_quants[typekey.name]++
update_icon()
/obj/machinery/smartfridge/Destroy()
qdel(wires)
wires = null
return ..()
/obj/machinery/smartfridge/proc/accept_check(var/obj/item/O)
if(is_type_in_list(O, accepted_items))
return TRUE
return FALSE
/obj/machinery/smartfridge/foodheater
name = "\improper SmartHeater"
desc = "To keep the food warm!"
contents_path = "-food"
accepted_items = list(/obj/item/reagent_containers/food/snacks)
/obj/machinery/smartfridge/foodheater/stand
desc = "A more commercialized version of your traditional SmartHeater. Nothing like stale, heat-lamp warmed food."
use_power = POWER_USE_OFF
idle_power_usage = 0
active_power_usage = 0
/obj/machinery/smartfridge/foodheater/stand/powered()
return TRUE
/obj/machinery/smartfridge/foodheater/abandoned
// badly stocked, with trash, junk, etc
desc = "Used to keep food nice and warm in the past, now it is all dirty, and doesn't look like it'll ever run again."
use_power = POWER_USE_OFF
/obj/machinery/smartfridge/seeds
name = "\improper MegaSeed Storage"
desc = "When you need seeds fast!"
icon = 'icons/obj/vending.dmi'
icon_state = "nutrimat"
opacity = TRUE
contents_path = null
accepted_items = list(/obj/item/seeds)
/obj/machinery/smartfridge/secure/extract
name = "\improper Slime Extract Storage"
desc = "A refrigerated storage unit for slime extracts"
contents_path = "-slime"
req_access = list(ACCESS_RESEARCH)
accepted_items = list(/obj/item/slime_extract)
/obj/machinery/smartfridge/secure/extract/Initialize()
. = ..()
new/obj/item/storage/slimes(src)
/obj/machinery/smartfridge/secure/medbay
name = "\improper Refrigerated Chemical Storage"
desc = "A refrigerated storage unit for storing medicine and chemicals."
contents_path = "-chem"
req_one_access = list(ACCESS_MEDICAL,ACCESS_PHARMACY)
accepted_items = list(/obj/item/reagent_containers/glass,
/obj/item/storage/pill_bottle,
/obj/item/reagent_containers/pill,
/obj/item/reagent_containers/inhaler,
/obj/item/reagent_containers/personal_inhaler_cartridge,
/obj/item/reagent_containers/hypospray/autoinjector,
/obj/item/personal_inhaler)
//Variant of medical fridge, but with some starting gear
/obj/machinery/smartfridge/secure/medbay/horizon
initial_contents = list(/obj/item/reagent_containers/glass/bottle/bicaridine = 1,
/obj/item/reagent_containers/glass/bottle/kelotane = 1,
/obj/item/reagent_containers/glass/bottle/dexalin = 1)
/obj/machinery/smartfridge/secure/virology
name = "\improper Refrigerated Virus Storage"
desc = "A refrigerated storage unit for storing viral material."
contents_path = "-viro"
req_access = list(ACCESS_VIROLOGY)
accepted_items = list(/obj/item/reagent_containers/glass/beaker/vial)
/obj/machinery/smartfridge/chemistry
name = "\improper Smart Chemical Storage"
desc = "A refrigerated storage unit for medicine and chemical storage."
contents_path = "-chem"
accepted_items = list(/obj/item/reagent_containers/glass,
/obj/item/storage/pill_bottle,
/obj/item/reagent_containers/pill,
/obj/item/reagent_containers/inhaler,
/obj/item/reagent_containers/personal_inhaler_cartridge,
/obj/item/reagent_containers/hypospray/autoinjector,
/obj/item/personal_inhaler)
/obj/machinery/smartfridge/chemistry/virology
name = "\improper Smart Virus Storage"
desc = "A refrigerated storage unit for volatile sample storage."
contents_path = "-viro"
/obj/machinery/smartfridge/drinks
name = "\improper Drink Showcase"
desc = "A refrigerated storage unit for tasty tasty alcohol."
contents_path = "-drink"
cooling = TRUE
accepted_items = list(/obj/item/reagent_containers/glass,
/obj/item/reagent_containers/food/drinks,
/obj/item/reagent_containers/food/condiment)
/obj/machinery/smartfridge/drinks/bar
density = FALSE
layer = BELOW_TABLE_LAYER
/obj/machinery/smartfridge/drying_rack
name = "\improper Drying Rack"
desc = "A machine for drying plants."
icon_state = "drying_rack"
accepted_items = list(/obj/item/reagent_containers/food/snacks)
contents_path = null
has_emissive = FALSE
/obj/machinery/smartfridge/drying_rack/accept_check(var/obj/item/O)
if(!..())
return FALSE
var/obj/item/reagent_containers/food/snacks/S = O
if (S.dried_type)
return TRUE
/obj/machinery/smartfridge/drying_rack/process()
..()
if(length(contents))
dry()
/obj/machinery/smartfridge/drying_rack/proc/dry()
for(var/obj/item/reagent_containers/food/snacks/S in contents)
if(S.dry) continue
var/old_name = S.name
if(S.on_dry(src)) //Drying rack keeps the item but changes the name. This prevents pre-dried item lingering in the UI as vendable
item_quants[S.name]++
item_quants[old_name]--
if(item_quants[old_name] <= 0 || item_quants[S.name] == 1)
update_static_data_for_all_viewers()
if(item_quants[old_name] <= 0)
item_quants -= old_name
return
/obj/machinery/smartfridge/drying_rack/update_overlays()
ClearOverlays()
if(length(contents))
AddOverlays("drying_rack_drying")
var/list/shown_contents = contents - component_parts
if(shown_contents.len)
AddOverlays("drying_rack_filled")
/obj/machinery/smartfridge/process()
if(stat & (BROKEN|NOPOWER))
seconds_electrified = 0
return
if(seconds_electrified > 0)
seconds_electrified--
if(shoot_inventory && prob(2))
throw_item()
if(!cooling && !heating)
return
var/mod = cooling ? -1 : 1
for(var/obj/item/I in contents)
if(!I.reagents)
continue
var/r_temperature = I.reagents.get_temperature()
if(mod == 1 && r_temperature <= heating_temperature)
continue
else if(mod == -1 && r_temperature <= cooling_temperature)
continue
var/thermal_energy_change = 0
if(mod == 1) //GOING UP
thermal_energy_change = min(active_power_usage,I.reagents.get_thermal_energy_change(r_temperature,heating_temperature))
else if (mod == -1) //GOING DOWN
thermal_energy_change = max(-active_power_usage,I.reagents.get_thermal_energy_change(r_temperature,cooling_temperature))
I.reagents.add_thermal_energy(thermal_energy_change)
use_power_oneoff(active_power_usage)
/obj/machinery/smartfridge/power_change()
..()
if(!anchored)
stat |= NOPOWER
update_icon()
/obj/machinery/smartfridge/update_icon()
if(stat & (BROKEN|NOPOWER))
icon_state = "[initial(icon_state)]-off"
else
icon_state = "[initial(icon_state)]"
update_overlays()
/obj/machinery/smartfridge/proc/update_overlays()
ClearOverlays()
if(panel_open)
AddOverlays("[initial(icon_state)]-panel")
var/list/shown_contents = contents - component_parts
if(contents_path && length(shown_contents) > 0)
var/contents_icon_state = change_display(length(shown_contents) - 1)
AddOverlays("[initial(icon_state)][contents_path][contents_icon_state]")
AddOverlays("[initial(icon_state)]-glass[(stat & BROKEN) ? "-broken" : ""]")
if(has_emissive && powered() && !(stat & BROKEN))
AddOverlays(emissive_appearance(icon, "[initial(icon_state)]-light-mask", src, alpha = src.alpha))
/obj/machinery/smartfridge/proc/change_display(var/length)
var/tier = clamp((floor(length / display_tier_amt) + 1), 1, display_tiers)
return "-[num2text(tier)]"
/*******************
* Item Adding
********************/
/obj/machinery/smartfridge/attackby(obj/item/attacking_item, mob/user)
if(attacking_item.tool_behaviour == TOOL_SCREWDRIVER)
panel_open = !panel_open
user.visible_message("\The [user] [panel_open ? "opens" : "closes"] the maintenance panel of \the [src].",
"You [panel_open ? "open" : "close"] the maintenance panel of \the [src].")
update_icon()
return
if(attacking_item.tool_behaviour == TOOL_WRENCH)
anchored = !anchored
user.visible_message("\The [user] [anchored ? "secures" : "unsecures"] the bolts holding \the [src] to the floor.",
"You [anchored ? "secure" : "unsecure"] the bolts holding \the [src] to the floor.")
attacking_item.play_tool_sound(get_turf(src), 50)
power_change()
return
if(attacking_item.tool_behaviour == TOOL_MULTITOOL || attacking_item.tool_behaviour == TOOL_WIRECUTTER)
if(panel_open)
switch(input(user, "What would you like to select?", "Machine Debug Software") as null|anything in list("SmartHeater", "MegaSeed Storage", "Slime Extract Storage", "Refrigerated Chemical Storage", "Refrigerated Virus Storage", "Drink Showcase", "Drying Rack"))
if("SmartHeater")
new /obj/machinery/smartfridge/foodheater(loc)
qdel(src)
if("MegaSeed Storage")
new /obj/machinery/smartfridge/seeds(loc)
qdel(src)
if("Slime Extract Storage")
new /obj/machinery/smartfridge/secure/extract(loc)
qdel(src)
if("Refrigerated Chemical Storage")
new /obj/machinery/smartfridge/secure/medbay(loc)
qdel(src)
if("Refrigerated Virus Storage")
new /obj/machinery/smartfridge/secure/virology(loc)
qdel(src)
if("Drink Showcase")
new /obj/machinery/smartfridge/drinks(loc)
qdel(src)
if("Drying Rack")
new /obj/machinery/smartfridge/drying_rack(loc)
qdel(src)
attack_hand(user)
return
if(stat & NOPOWER)
to_chat(user, SPAN_NOTICE("[src] is unpowered and useless."))
return
if(accept_check(attacking_item))
if(length(contents) >= max_n_of_items)
to_chat(user, SPAN_NOTICE("[src] is full."))
return TRUE
user.remove_from_mob(attacking_item)
attacking_item.forceMove(src)
item_quants[attacking_item.name]++
if(item_quants[attacking_item.name] == 1)
update_static_data_for_all_viewers()
user.visible_message("<b>[user]</b> adds \a [attacking_item] to [src].", SPAN_NOTICE("You add [attacking_item] to [src]."))
update_overlays()
return TRUE
if(istype(attacking_item, /obj/item/storage))
var/obj/item/storage/P = attacking_item
var/plants_loaded = 0
var/update_static_data = FALSE
for(var/obj/G in P.contents)
if(accept_check(G))
if(length(contents) >= max_n_of_items)
break
P.remove_from_storage(G,src)
item_quants[G.name]++
if(item_quants[G.name] == 1)
update_static_data = TRUE
plants_loaded++
if(plants_loaded)
user.visible_message("<b>[user]</b> loads [src] with [P].", SPAN_NOTICE("You load [src] with [P]."))
if(length(P.contents) > 0)
to_chat(user, SPAN_NOTICE("Some items are refused."))
update_overlays()
if(update_static_data)
update_static_data_for_all_viewers()
return TRUE
to_chat(user, SPAN_NOTICE("[src] smartly refuses [attacking_item]."))
return TRUE
/obj/machinery/smartfridge/secure/emag_act(var/remaining_charges, var/mob/user)
if(emagged)
return FALSE
emagged = 1
locked = -1
to_chat(user, SPAN_NOTICE("You short out the product lock on [src]."))
return TRUE
/obj/machinery/smartfridge/attack_ai(mob/user)
if(!ai_can_interact(user))
return
attack_hand(user)
/obj/machinery/smartfridge/attack_hand(mob/user)
if(stat & (NOPOWER|BROKEN))
return
if(panel_open)
wires.interact(user)
ui_interact(user)
/*******************
* SmartFridge Menu
********************/
/obj/machinery/smartfridge/ui_interact(mob/user, var/datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "SmartFridge", name, 400, 500)
ui.open()
/obj/machinery/smartfridge/ui_data(mob/user)
var/list/data = list()
data["electrified"] = seconds_electrified > 0
data["shoot_inventory"] = shoot_inventory
data["locked"] = locked
data["secure"] = is_secure
data["sort_alphabetically"] = ui_sort_alphabetically
var/list/stocks
for (var/i in 1 to length(item_quants))
var/K = item_quants[i]
var/count = item_quants[K]
if(count > 0)
LAZYADDASSOC(stocks, capitalize(K), count)
data["stocks"] = stocks
return data
/obj/machinery/smartfridge/ui_static_data(mob/user)
var/list/data = list()
data["contents"] = null
var/list/items = list()
for (var/i = 1 to length(item_quants))
var/K = item_quants[i]
var/obj/item/item_used = null
for (var/obj/item/O in contents - component_parts)
if(O.name == K)
item_used = O
break
if(!item_used)
continue
var/final_icon = base64_cache["[K]_[item_used.type]_[item_used.icon_state]"]
if(!final_icon)
var/icon/item_icon
if(istype(item_used, /obj/item/seeds))
var/obj/item/seeds/S = item_used
item_icon = S.update_appearance(TRUE)
else
item_icon = getFlatIcon(item_used)
final_icon = icon2base64(item_icon)
base64_cache["[K]_[item_used.type]_[item_used.icon_state]"] = final_icon
items.Add(list(list(
"display_name" = capitalize(K),
"vend" = i,
"icon" = final_icon,
)))
if(length(items) > 0)
data["contents"] = items
return data
/obj/machinery/smartfridge/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
var/mob/user = ui.user
add_fingerprint(user)
if(stat & (NOPOWER|BROKEN) || !anchored)
return
if(!allowed(user) && !emagged && locked != -1 && is_secure)
to_chat(usr, SPAN_WARNING("Access denied."))
return
switch(action)
if("vendItem")
var/index = text2num(params["vendItem"])
var/amount = text2num(params["amount"])
var/K = item_quants[index]
var/count = item_quants[K]
// Sanity check, there are probably ways to press the button when it shouldn't be possible.
if(count > 0 && anchored)
item_quants[K] = max(count - amount, 0)
var/i = amount
for(var/obj/O in contents)
if(O.name == K)
if(Adjacent(user))
user.put_in_hands(O)
else
O.forceMove(loc)
i--
update_overlays()
if(i <= 0)
break
if(item_quants[K] <= 0)
update_static_data_for_all_viewers()
item_quants -= K
if("switch_sort_alphabetically")
ui_sort_alphabetically = !ui_sort_alphabetically
. = TRUE
/obj/machinery/smartfridge/proc/throw_item()
var/obj/throw_item = null
var/mob/living/target = locate() in view(7,src)
if(!target)
return FALSE
for (var/O in item_quants)
if(item_quants[O] <= 0) //Try to use a record that actually has something to dump.
item_quants -= O
continue
item_quants[O]--
for(var/obj/T in contents)
if(T.name == O)
T.forceMove(loc)
throw_item = T
break
if(item_quants[O] <= 0)
update_static_data_for_all_viewers()
item_quants -= O
break
if(!throw_item)
return FALSE
throw_item.throw_at(target,16,3,src)
visible_message(SPAN_DANGER("[src] launches [throw_item.name] at [target.name]!"))
return TRUE
/************************
* Secure SmartFridges
*************************/
/obj/machinery/smartfridge/secure/Topic(href, href_list)
if(stat & (NOPOWER|BROKEN) || !anchored) return FALSE
if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)))
if(!allowed(usr) && !emagged && locked != -1 && href_list["vendItem"])
to_chat(usr, SPAN_WARNING("Access denied."))
return FALSE
return ..()
// Konyang
/obj/machinery/smartfridge/foodheater/buffet
name = "buffet trays"
icon = 'icons/obj/structure/urban/restaurant.dmi'
icon_state = "buffet"
contents_path = null
has_emissive = FALSE
/obj/machinery/smartfridge/foodheater/buffet/Initialize()
. = ..()
contents_path = "[rand(1, 4)]" // overriding the update icon anyway, so this var is free.
/obj/machinery/smartfridge/foodheater/buffet/update_icon()
if(stat & (BROKEN|NOPOWER))
icon_state = "[initial(icon_state)]"
else
icon_state = "[initial(icon_state)][contents_path]"