Files
BatrachophrenoandGitHub 4ecb0bc21c Repath obj/machinery to obj/structure/machinery [MDB Ignore] (#22500)
Repaths obj/machinery to obj/structure/machinery. **Note for
reviewers:** the only meaningful changed code exists within
**code/game/objects/structures.dm** and
**code/game/objects/structures/_machinery.dm**, largely concerning
damage procs. With the exception of moving airlock defines to their own
file, ALL OTHER CHANGES ARE STRICTLY PATH CHANGES.

Objects, _categorically_, are largely divided between those you can hold
in your hand/inventory and those you can't. Machinery objects are
already subtypes of Structures behaviorally, this PR just makes their
pathing reflect that, and allows for future work (tool actions, more
health/destruction functionality) to be developed without unnecessary
code duplication.

I have tested this PR by loading up the Horizon and dismantling various
machines and structures with tools, shooting guns of various types
throughout the ship, and detonating a bunch of explosions throughout the
ship.
2026-05-26 19:35:48 +00:00

623 lines
19 KiB
Plaintext

/* SmartFridge. Much todo
*/
/obj/structure/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/visible_takeout = FALSE
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/structure/machinery/smartfridge/secure
is_secure = 1
/obj/structure/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/structure/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/structure/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/structure/machinery/smartfridge/Destroy()
qdel(wires)
wires = null
return ..()
/obj/structure/machinery/smartfridge/proc/accept_check(var/obj/item/O)
if(is_type_in_list(O, accepted_items))
return TRUE
return FALSE
/obj/structure/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/structure/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/structure/machinery/smartfridge/foodheater/stand/powered()
return TRUE
/obj/structure/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/structure/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/structure/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/structure/machinery/smartfridge/secure/extract/Initialize()
. = ..()
new/obj/item/storage/slimes(src)
/obj/structure/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/structure/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/structure/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/structure/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/structure/machinery/smartfridge/chemistry/virology
name = "\improper Smart Virus Storage"
desc = "A refrigerated storage unit for volatile sample storage."
contents_path = "-viro"
/obj/structure/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/structure/machinery/smartfridge/drinks/bar
density = FALSE
layer = BELOW_TABLE_LAYER
/obj/structure/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/structure/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/structure/machinery/smartfridge/drying_rack/process()
..()
if(length(contents))
dry()
/obj/structure/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/structure/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/structure/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/structure/machinery/smartfridge/power_change()
..()
if(!anchored)
stat |= NOPOWER
update_icon()
/obj/structure/machinery/smartfridge/update_icon()
if(stat & (BROKEN|NOPOWER))
icon_state = "[initial(icon_state)]-off"
else
icon_state = "[initial(icon_state)]"
update_overlays()
/obj/structure/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/structure/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/structure/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/structure/machinery/smartfridge/foodheater(loc)
qdel(src)
if("MegaSeed Storage")
new /obj/structure/machinery/smartfridge/seeds(loc)
qdel(src)
if("Slime Extract Storage")
new /obj/structure/machinery/smartfridge/secure/extract(loc)
qdel(src)
if("Refrigerated Chemical Storage")
new /obj/structure/machinery/smartfridge/secure/medbay(loc)
qdel(src)
if("Refrigerated Virus Storage")
new /obj/structure/machinery/smartfridge/secure/virology(loc)
qdel(src)
if("Drink Showcase")
new /obj/structure/machinery/smartfridge/drinks(loc)
qdel(src)
if("Drying Rack")
new /obj/structure/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()
astype(P, /obj/item/storage/box/fancy)?.opened = TRUE
P.update_icon()
return TRUE
to_chat(user, SPAN_NOTICE("[src] smartly refuses [attacking_item]."))
return TRUE
/obj/structure/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/structure/machinery/smartfridge/attack_ai(mob/user)
if(!ai_can_interact(user))
return
attack_hand(user)
/obj/structure/machinery/smartfridge/attack_hand(mob/user)
if(stat & (NOPOWER|BROKEN))
return
if(panel_open)
wires.interact(user)
ui_interact(user)
/*******************
* SmartFridge Menu
********************/
/obj/structure/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/structure/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/structure/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/structure/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)
continue
if(Adjacent(user))
user.put_in_hands(O)
if(visible_takeout)
user.visible_message(
"[SPAN_BOLD("[user]")] takes \a [O] from \the [src].",
SPAN_NOTICE("You take \a [O] from \the [src]."))
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/structure/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/structure/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/structure/machinery/smartfridge/foodheater/buffet
name = "buffet trays"
icon = 'icons/obj/structure/urban/restaurant.dmi'
icon_state = "buffet"
contents_path = null
has_emissive = FALSE
/obj/structure/machinery/smartfridge/foodheater/buffet/Initialize()
. = ..()
contents_path = "[rand(1, 4)]" // overriding the update icon anyway, so this var is free.
/obj/structure/machinery/smartfridge/foodheater/buffet/update_icon()
if(stat & (BROKEN|NOPOWER))
icon_state = "[initial(icon_state)]"
else
icon_state = "[initial(icon_state)][contents_path]"