Food Synthesizers (#19569)

* Food Synthesizers

Initial port, need to figure out what madness you people have done to tgui... and sprite sheets.

At this moment, it compiles but I haven't tested it yet.  Too eepy

* iconforge spritesheet nonsense

* Cleaning up tgui menu

full disclosure, After six plus hours of trying to figure out how to do the spritesheets  and the tgui stuff I just gave up on this particular bit and threw it into Gemini for a looksie over.

As it's originally my code I figure it's worth at least a reasonable proof read. The side-by-side comparison is pretty wild.

* Passes tgu-lint building

* Appease Gripe err Grep checks

* Oh these for the grep as well whoops

* Sorts out the backend

also piss off linter check, it's COMMENTED OUT aaaaaaaaaa.

* changes from will

* changes from will and some UI splitting, needs proper testing and final adjustments

* passadata

* .

* can be combined

* .

* ml={1}

* id

* Crew category

* id handling

* .

* more cleanup

* smaller cleanup

* why not

* deps up, minor tgui core fix

* timer

* .

* .

* missed to put that back

* order

* this

* Change preference category for foodsynth cookies

* Enforce south facement for cookie image

* Maurice I am no longer asking

* .

---------

Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
Poojawa
2026-07-09 13:50:35 -05:00
committed by GitHub
parent 9a8be3b591
commit ba0fd0be33
26 changed files with 2378 additions and 47 deletions
+4
View File
@@ -724,6 +724,10 @@
#define REAGENT_ID_GELATIN "gelatin"
#define REAGENT_MUSTARDPODS "Mustard Pods"
#define REAGENT_ID_MUSTARDPODS "mustardpods"
#define REAGENT_NUTRIPASTE "Nutriment Paste"
#define REAGENT_ID_NUTRIPASTE "nutripaste"
#define REAGENT_NUTRIPASTE_SOYLENT "Soylent Agent Green"
#define REAGENT_ID_NUTRIPASTE_SOYLENT "synthsoygreen"
#define REAGENT_DRINK "Drink"
#define REAGENT_ID_DRINK "drink"
+69
View File
@@ -0,0 +1,69 @@
//Totally not just a blatant copypasta. Nooooo...
/datum/wires/synthesizer
holder_type = /obj/machinery/synthesizer
wire_count = 6
proper_name = "Food Synthesizer"
/datum/wires/synthesizer/New(atom/_holder)
wires = list(WIRE_LATHE_HACK, WIRE_ELECTRIFY, WIRE_LATHE_DISABLE)
var/obj/machinery/synthesizer/A = _holder
if(A.hacked)
cut_wires += WIRE_LATHE_HACK
return ..()
/datum/wires/synthesizer/get_status()
. = ..()
var/obj/machinery/synthesizer/A = holder
. += "The red light is [A.disabled ? "off" : "on"]."
. += "The green light is [A.shocked ? "off" : "on"]."
. += "The blue light is [A.hacked ? "off" : "on"]."
/datum/wires/synthesizer/interactable(mob/user)
var/obj/machinery/synthesizer/A = holder
if(A.panel_open)
return TRUE
return FALSE
/datum/wires/synthesizer/on_cut(wire, mend)
var/obj/machinery/synthesizer/A = holder
switch(wire)
if(WIRE_LATHE_HACK)
A.hacked = !mend
A.update_tgui_static_data(usr)
if(WIRE_ELECTRIFY)
A.shocked = !mend
if(WIRE_LATHE_DISABLE)
A.disabled = !mend
..()
/datum/wires/synthesizer/on_pulse(wire)
if(is_cut(wire))
return
var/obj/machinery/synthesizer/A = holder
switch(wire)
if(WIRE_LATHE_HACK)
A.hacked = !A.hacked
A.update_tgui_static_data(usr)
addtimer(CALLBACK(src, PROC_REF(reset_hacked), WIRE_LATHE_HACK, usr), 5 SECONDS)
if(WIRE_ELECTRIFY)
A.shocked = !A.shocked
addtimer(CALLBACK(src, PROC_REF(reset_electrify), WIRE_ELECTRIFY), 5 SECONDS)
if(WIRE_LATHE_DISABLE)
A.disabled = !A.disabled
addtimer(CALLBACK(src, PROC_REF(reset_disable), WIRE_LATHE_DISABLE), 5 SECONDS)
/datum/wires/synthesizer/proc/reset_hacked(wire, mob/user)
var/obj/machinery/synthesizer/A = holder
if(A && !is_cut(wire))
A.hacked = FALSE
A.update_tgui_static_data(user)
/datum/wires/synthesizer/proc/reset_electrify(wire)
var/obj/machinery/synthesizer/A = holder
if(A && !is_cut(wire))
A.shocked = FALSE
/datum/wires/synthesizer/proc/reset_disable(wire)
var/obj/machinery/synthesizer/A = holder
if(A && !is_cut(wire))
A.disabled = FALSE
+18 -1
View File
@@ -24,6 +24,7 @@ GLOBAL_LIST(construction_frame_floor)
var/frame_style = FRAME_STYLE_FLOOR // "floor" or "wall"
var/x_offset // For wall frames: pixel_x
var/y_offset // For wall frames: pixel_y
var/is_dense = TRUE // Lazy workaround for wall-mounted, upgradable machines
// Get the icon state to use at a given state. Default implementation is based on the frame's name
/datum/frame/frame_types/proc/get_icon_state(state)
@@ -217,6 +218,22 @@ GLOBAL_LIST(construction_frame_floor)
circuit = /obj/item/circuitboard/injector_maker
frame_size = 3
/datum/frame/frame_types/foodsynthesizer
name = "Food Synthesizer"
icon_override = 'icons/obj/machines/foodsynthesizer.dmi'
frame_class = FRAME_CLASS_MACHINE
frame_size = 5
frame_style = FRAME_STYLE_WALL
x_offset = 24
y_offset = 30
is_dense = FALSE
/datum/frame/frame_types/foodsynthesizer/mini
name = "Mini Food Synthesizer"
frame_class = FRAME_CLASS_MACHINE
frame_size = 3 // smaller, so slightly fewer sheets
frame_style = FRAME_STYLE_WALL
// Refinery machines
/datum/frame/frame_types/industrial_reagent_grinder
name = "Industrial Chemical Grinder"
@@ -350,7 +367,7 @@ GLOBAL_LIST(construction_frame_floor)
if(frame_type.name == "Computer")
density = TRUE
if(frame_type.frame_class == FRAME_CLASS_MACHINE)
if(frame_type.frame_class == FRAME_CLASS_MACHINE && frame_type.is_dense)
density = TRUE
update_icon()
@@ -0,0 +1,15 @@
//Let's be optimal with our menu preview icons, shall we?
/datum/asset/spritesheet_batched/synthesizer
name = "synthesizer"
/datum/asset/spritesheet_batched/synthesizer/create_spritesheets()
for(var/datum/category_item/synthesizer/path as anything in subtypesof(/datum/category_item/synthesizer))
var/datum/universal_icon/icon = get_display_icon_for(path::build_path)
if(!icon || !icon.icon_file)
continue
icon.scale(128, 128) //enbiggen for the menu UI
var/key = sanitize_css_class_name("[path::type]")
insert_icon(key, icon)
@@ -1,10 +1,10 @@
// Define a place to save in character setup
/datum/preferences
var/vantag_volunteer = 0 // What state I want to be in, in terms of being affected by antags.
var/vantag_volunteer = FALSE // What state I want to be in, in terms of being affected by antags.
var/vantag_preference = VANTAG_NONE // Whether I'd like to volunteer to be an antag at some point.
var/resleeve_lock = 0 // Whether movs should have OOC reslieving protection. Default false.
var/resleeve_scan = 1 // Whether mob should start with a pre-spawn body scan. Default true.
var/mind_scan = 1 // Whether mob should start with a pre-spawn mind scan. Default true.
var/resleeve_lock = FALSE // Whether movs should have OOC reslieving protection. Default false.
var/resleeve_scan = TRUE // Whether mob should start with a pre-spawn body scan. Default true.
var/mind_scan = TRUE // Whether mob should start with a pre-spawn mind scan. Default true.
var/custom_species // Custom species name, can't be changed due to it having been used in savefiles already.
@@ -95,7 +95,7 @@
var/want_body_save = pref.resleeve_scan
var/want_mind_save = pref.mind_scan
spawn(50)
spawn(5 SECONDS)
if(QDELETED(character) || QDELETED(pref))
return // They might have been deleted during the wait
if(!character.virtual_reality_mob && !(/mob/living/carbon/human/proc/perform_exit_vr in character.verbs)) //Janky fix to prevent resleeving VR avatars but beats refactoring transcore
@@ -155,6 +155,7 @@
data["borg_petting"] = pref.borg_petting
data["ignore_shoes"] = pref.read_preference(/datum/preference/toggle/human/ignore_shoes)
data["synth_cookie"] = pref.read_preference(/datum/preference/toggle/living/foodsynth_cookies)
data["resleeve_lock"] = pref.resleeve_lock
data["resleeve_scan"] = pref.resleeve_scan
@@ -236,6 +237,9 @@
if("toggle_resleeve_scan")
pref.resleeve_scan = pref.resleeve_scan ? 0 : 1;
return TOPIC_REFRESH
if("toggle_synth_cookie")
pref.update_preference_by_type(/datum/preference/toggle/living/foodsynth_cookies, !pref.read_preference(/datum/preference/toggle/living/foodsynth_cookies))
return TOPIC_REFRESH
if("toggle_mind_scan")
pref.mind_scan = pref.mind_scan ? 0 : 1;
return TOPIC_REFRESH
@@ -7,3 +7,14 @@
/datum/preference/toggle/human/ignore_shoes/apply_to_human(mob/living/carbon/human/target, value)
return
// Whether mob can be printed as a snack. requires body scan. Default false.
/datum/preference/toggle/living/foodsynth_cookies
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
savefile_key = "vore_fsynth_cookie"
default_value = FALSE
savefile_identifier = PREFERENCE_CHARACTER
can_randomize = FALSE
/datum/preference/toggle/living/foodsynth_cookies/apply_to_living(mob/living/target, value)
return
@@ -0,0 +1,715 @@
//Poojy's miracle 'I don't want generic pizza' / there's noone working kitchen machine
//Yes it's a generic food 3d printer. ~
// in here because makes sense, if really it's just a refillable autolathe of food
#define SYNTH_FOOD_COST 5
/obj/machinery/synthesizer
name = "Food Synthesizer"
desc = "Sabresnacks brand device able to produce an incredible array of conventional foods. Although only the most ascetic of users claim it produces truly good tasting products."
icon = 'icons/obj/machines/foodsynthesizer.dmi'
icon_state = "synthesizer"
density = FALSE
anchored = TRUE
use_power = USE_POWER_IDLE
idle_power_usage = 10
active_power_usage = 2000
clicksound = "keyboard"
clickvol = 30
var/hacked = FALSE
var/disabled = FALSE
var/shocked = FALSE
var/busy = FALSE
var/usage_amt = SYNTH_FOOD_COST
light_system = STATIC_LIGHT
light_range = 3
light_power = 1
light_on = FALSE
var/menu_grade //how tasty is it?
var/speed_grade //how fast can it be?
var/filtertext
circuit = /obj/item/circuitboard/synthesizer
//loaded cartridge
var/obj/item/reagent_containers/synthdispcart/cart = /obj/item/reagent_containers/synthdispcart
var/cart_type = ITEMSIZE_LARGE
//all of our food
var/static/datum/category_collection/synthesizer/synthesizer_recipes
var/active_menu = "appasnacc"
var/activefood
//Voice activation stuff
var/activator = "computer"
var/list/voicephrase
//crew printing required stuff.
var/tgui_icons
var/icon/crewpicture
var/activecrew
var/refresh_delay = 10 SECONDS
/obj/machinery/synthesizer/Initialize(mapload)
. = ..()
if(!synthesizer_recipes)
synthesizer_recipes = new()
cart = new cart(src)
wires = new/datum/wires/synthesizer(src)
default_apply_parts()
update_icon()
if(!pixel_x && !pixel_y)
offset_synth()
/obj/machinery/synthesizer/mini
name = "Mini Food Synthesizer"
icon_state = "portsynth"
cart_type = ITEMSIZE_NORMAL
circuit = /obj/item/circuitboard/synthesizer/mini
cart = /obj/item/reagent_containers/synthdispcart/small
/obj/machinery/synthesizer/Destroy()
QDEL_NULL(wires)
if(cart)
QDEL_NULL(cart)
clear_tgui_icons()
return ..()
/obj/machinery/synthesizer/examine(mob/user)
. = ..()
if(panel_open)
. += "A cartridge is [cart ? "installed" : "missing"]."
if(cart && (!(stat & (NOPOWER|BROKEN))))
var/obj/item/reagent_containers/synthdispcart/C = cart
if(istype(C) && C.reagents && C.reagents.total_volume)
var/percent = round((C.reagents.total_volume / C.volume) * 100)
. += "The installed cartridge has [percent]% remaining."
//offsets to make the machine squish to a wall. They're all south facing so it looks weird but every other direction is AWFUL
/obj/machinery/synthesizer/proc/offset_synth()
pixel_x = (dir & 3) ? 0 : (dir == 4 ? -24 : 24)
pixel_y = (dir & 3) ? (dir == 1 ? -22 : 30) : 0
// TGUI
// Crew Cookie backend stuff... I can't even fuckin' believe this is Janicart stuff
/obj/machinery/synthesizer/proc/set_tgui_icon(mob/L)
if(!isliving(L))
return
if(ishuman(L))
crewpicture = getFlatIcon(L, defdir = SOUTH, no_anim = TRUE, force_south = TRUE)
tgui_icons = "'data:image/png;base64,[icon2base64(crewpicture)]'"
else //Simple animals, Silicons, etc don't have records, so we'll just grab their current state.
var/icon/F = getFlatIcon(L, defdir = SOUTH, no_anim = TRUE, force_south = TRUE)
crewpicture = F
tgui_icons = "'data:image/png;base64,[icon2base64(F)]'"
SStgui.update_uis(src)
/obj/machinery/synthesizer/proc/clear_tgui_icons()
tgui_icons = null
crewpicture = null
// And literally this is all that's needed for conventional meals. lmao.
/obj/machinery/synthesizer/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet_batched/synthesizer),
)
/obj/machinery/synthesizer/tgui_interact(mob/user, datum/tgui/ui)
if(stat & (BROKEN|NOPOWER))
return
if(shocked)
shock(user, 100)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "FoodSynthesizer")
ui.open()
/obj/machinery/synthesizer/tgui_status(mob/user)
if(disabled)
return STATUS_CLOSE
return ..()
/obj/machinery/synthesizer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = list(
"busy" = busy,
"isThereCart" = cart,
"active_menu" = active_menu,
"activefood" = activefood,
"activecrew" = activecrew,
"crewicon" = tgui_icons
)
if(cart)
var/percent = round((cart.reagents.total_volume / cart.reagents.maximum_volume) * 100)
data["cartFillStatus"] = percent
return data
/obj/machinery/synthesizer/tgui_static_data(mob/user)
var/list/data = ..()
var/list/menucatagories = list()
for(var/datum/category_group/synthesizer/menulist in synthesizer_recipes.categories)
var/list/recipes = list()
for(var/datum/category_item/synthesizer/food in menulist.items)
UNTYPED_LIST_ADD(recipes, list(
"name" = food.name,
"type" = food.type,
"desc" = food.desc,
"hidden" = food.hidden,
"ref" = text_ref(food)
))
UNTYPED_LIST_ADD(menucatagories, list(
"name" = menulist.name,
"id" = menulist.id,
"sortorder" = menulist.sortorder,
"ref" = text_ref(menulist),
"recipes" = recipes
))
data["menucatagories"] = menucatagories
var/list/crew_cookies = list()
for(var/client/C in GLOB.clients)
// Check if the client allows crew cookies in their currently toggled prefs. Clients must be ingame to even be in GLOB.clients, so logged out players won't show up here.
if(!C.prefs?.read_preference(/datum/preference/toggle/living/foodsynth_cookies))
continue
var/name = null
var/species = null
if(ishuman(C.mob))
var/mob/living/carbon/human/H = C.mob
//Utilize the body records for humans to avoid metagaming problems (EX: using the cookie printer to check if someone is ghosting from the main menu)
var/datum/transcore_db/db = SStranscore.db_by_key()
if(db)
var/datum/transhuman/body_record/b_rec = db.body_scans[H.mind.name]
if(!b_rec) //extra check to make sure people have a body record, and no-one will immediately on start.
continue
name = H.real_name
species = "[H.custom_species ? H.custom_species : H.species.name]"
if(issilicon(C.mob))
if(isAI(C.mob))
var/mob/living/silicon/ai/A = C.mob
name = A.name
species = "Artificial Intelligence"
if(isrobot(C.mob))
var/mob/living/silicon/robot/R = C.mob
if(R.scrambledcodes || (R.module && R.module.hide_on_manifest)) //Not sure if admeme events want valid cookie print outs
continue
name = R.name
species = "[R.modtype] [R.braintype]"
if(isanimal(C.mob))
var/mob/living/simple_mob/SM = C.mob
name = SM.name
species = initial(SM.name) //most mobs are simply named the species they are, so this ought to be useful for named critters.
if(!name)
continue
// our crew cookies are only applicable on the crew menu, and we're reusing our category sorting as much as possible!
crew_cookies.Add(list(list(
"category" = "crew",
"name" = name,
"species" = species
)))
data["crew_cookies"] = crew_cookies
return data
/obj/machinery/synthesizer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return TRUE
if(stat & (BROKEN|NOPOWER))
return FALSE
if(ui.user.stat || ui.user.restrained())
return FALSE
add_fingerprint(ui.user)
if(busy)
to_chat(ui.user, span_notice("The synthesizer is busy. Please wait for completion of previous operation."))
return FALSE
switch(action)
if("setactive_menu")
if(active_menu == params["setactive_menu"])
return FALSE
active_menu = params["setactive_menu"]
activecrew = null
activefood = null
if(tgui_icons)
clear_tgui_icons()
return TRUE
if("setactive_food")
if(activefood == params["setactive_food"])
return FALSE
activefood = params["setactive_food"]
return TRUE
if("setactive_crew")
if(activecrew == params["setactive_crew"])
return FALSE
activecrew = params["setactive_crew"]
if(tgui_icons)
clear_tgui_icons()
var/mob/found
for(var/mob/living/L in GLOB.player_list)
if(L.real_name == activecrew)
found = L
break
if(!found)
return FALSE
if(!get_mob_for_picture(ui.user, found))
return FALSE
set_tgui_icon(found)
return TRUE
if("make")
var/datum/category_item/synthesizer/making = locate(params["make"])
if(!istype(making))
return
if(making.hidden && !hacked)
return
//Check if we still have the materials.
var/obj/item/reagent_containers/synthdispcart/C = cart
if(check_cart(C, ui.user))
//Sanity check.
if(!making || !src)
return
busy = TRUE
update_use_power(USE_POWER_ACTIVE)
update_icon() // light up time
C.reagents.remove_reagent(REAGENT_ID_NUTRIPASTE_SOYLENT, SYNTH_FOOD_COST) //Drain our fuel
var/obj/item/reagent_containers/food/snacks/food_mimic = new making.build_path(src) //Let's get this on a tray
addtimer(CALLBACK(src, PROC_REF(finish_production), food_mimic, ui.user), speed_grade, TIMER_DELETE_ME)
return TRUE
audible_message(span_notice("Error: Insufficent Materials. SabreSnacks recommends you have a genuine replacement cartridge available to install."), runemessage = "Error: Insufficent Materials!")
return FALSE
if("refresh")
update_tgui_static_data(ui.user, ui)
return TRUE
if("crewprint")
var/active_crew = params["crewprint"]
var/mob/found
for(var/mob/living/L in GLOB.player_list)
if(L.real_name == active_crew)
found = L
break
if(found && !get_mob_for_picture(ui.user, found)) //verify that we can still print this person
to_chat(ui.user, "Warning: Invalid selection.")
return FALSE
//Check if we still have the materials.
var/obj/item/reagent_containers/synthdispcart/C = cart
if(check_cart(C, ui.user))
//Sanity check.
busy = TRUE
update_use_power(USE_POWER_ACTIVE)
update_icon() // light up time
C.reagents.remove_reagent(REAGENT_ID_NUTRIPASTE_SOYLENT, SYNTH_FOOD_COST) //Drain our fuel
sleep(speed_grade) //machine go brrr
//Create the cookie base.
var/obj/item/reagent_containers/food/snacks/synthsized_meal/crewblock/meal = new /obj/item/reagent_containers/food/snacks/synthsized_meal/crewblock(loc)
//Begin mimicking the micro
var/vore_flavor
if(found?.vore_taste)
vore_flavor = found.vore_taste
else
vore_flavor = "Something impalpable"
meal.name = found.real_name
meal.desc = "A tiny replica of a crewmate!"
var/icon/F = crewpicture
F.Scale(16, 16) //Half size
meal.icon = F
meal.icon_state = null
//flavor mixing, make the cookie taste somewhat like the real thing!
for(var/datum/reagent/foodpaste in meal.reagents.reagent_list)
if(foodpaste.id == REAGENT_ID_NUTRIPASTE) //This should be the only reagent, actually.
foodpaste.taste_description += " as well as [vore_flavor]"
foodpaste.data = list(foodpaste.taste_description = 1)
meal.nutriment_desc = list(foodpaste.taste_description = 1)
if(menu_grade >= 2) //Is the machine upgraded?
meal.reagents.add_reagent(REAGENT_ID_NUTRIPASTE, ((1 + menu_grade) - 1)) //add the missing Nutriment bonus, subtracting the one we've already added in.
audible_message(span_notice("Please take your miniature [meal.name]."), runemessage = "Minature [meal.name] is complete!")
if(Adjacent(ui.user))
ui.user.put_in_any_hand_if_possible(meal) //Autoplace in hands to save a click
else
meal.forceMove(get_turf(src)) //otherwise we anti-clump layer onto the floor
meal.randpixel_xy()
busy = FALSE
update_icon() //turn off lights, please.
else
audible_message(span_notice("Error: Insufficent Materials. SabreSnacks recommends you have a genuine replacement cartridge available to install."), runemessage = "Error: Insufficent Materials!")
return FALSE
return TRUE
/obj/machinery/synthesizer/proc/finish_production(obj/item/reagent_containers/food/snacks/food_mimic, mob/user)
//Create the desired item.
var/obj/item/reagent_containers/food/snacks/synthsized_meal/meal = new /obj/item/reagent_containers/food/snacks/synthsized_meal(loc)
//Begin mimicking the food
meal.name = food_mimic.name
meal.desc = food_mimic.desc
meal.icon = food_mimic.icon
meal.icon_state = food_mimic.icon_state
meal.center_of_mass_x = food_mimic.center_of_mass_x
meal.center_of_mass_y = food_mimic.center_of_mass_y
//flavor mixing
var/taste_output = food_mimic.reagents.generate_taste_message()
for(var/datum/reagent/F in meal.reagents.reagent_list)
if(F.id == REAGENT_ID_NUTRIPASTE) //This should be the only reagent, actually.
F.taste_description += " as well as [taste_output]"
F.data = list(F.taste_description = 1)
meal.nutriment_desc = list(F.taste_description = 1)
if(menu_grade >= 2) //Is the machine upgraded?
meal.reagents.add_reagent(REAGENT_ID_NUTRIPASTE, ((1 + menu_grade) - 1)) //add the missing Nutriment bonus, subtracting the one we've already added in.
meal.bitesize = food_mimic?.bitesize //suffer your aerogel like 1 Nutriment turkey, nerds.
meal.filling_color = food_mimic?.filling_color
meal.trash = food_mimic?.trash //If this can lead to exploits then we'll remove it, but I like the idea.
qdel(food_mimic)
audible_message(span_notice("Please take your [meal.name]."), runemessage = "[meal.name] is complete!")
if(Adjacent(user))
user.put_in_any_hand_if_possible(meal) //Autoplace in hands to save a click
else
meal.forceMove(get_turf(src)) //otherwise we anti-clump layer onto the floor
meal.randpixel_xy()
busy = FALSE
update_icon() //turn off lights, please.
/obj/machinery/synthesizer/update_icon()
cut_overlays()
set_light_on(FALSE)
icon_state = initial(icon_state) //we use this to reduce code bloat. It's nice.
if(panel_open) //add service panels just above our machine
if(!(stat & (NOPOWER|BROKEN)))
add_overlay("[initial(icon_state)]_ppanel")
else
add_overlay("[initial(icon_state)]_panel")
if(cart)
var/obj/item/reagent_containers/synthdispcart/C = cart
if(C.reagents && C.reagents.total_volume)
var/image/filling_overlay = image("[icon]", src, "[initial(icon_state)]fill_0") //Modular filling
var/percent = round((C.reagents.total_volume / C.volume) * 100)
switch(percent)
if(0 to 9) filling_overlay.icon_state = "[initial(icon_state)]fill_0"
if(10 to 35) filling_overlay.icon_state = "[initial(icon_state)]fill_25"
if(36 to 74) filling_overlay.icon_state = "[initial(icon_state)]fill_50"
if(75 to 90) filling_overlay.icon_state = "[initial(icon_state)]fill_75"
if(91 to INFINITY) filling_overlay.icon_state = "[initial(icon_state)]fill_100"
filling_overlay.color = C.reagents.get_color()
//Add our filling, if any.
add_overlay(filling_overlay)
//Then add our cart so the filling is inside of the canister.
add_overlay("[initial(icon_state)]_cart")
return //don't stack additional panel screen states, please.
if((stat && BROKEN))
icon_state = "[initial(icon_state)]x"
return
if(stat & NOPOWER)
return
if(!cart)
add_overlay("[initial(icon_state)]_error")
return
if(cart && !busy)
var/obj/item/reagent_containers/synthdispcart/C = cart
if(C.reagents && C.reagents.total_volume)
add_overlay("[initial(icon_state)]_on")
else
add_overlay("[initial(icon_state)]_error")
return
if(busy)
add_overlay("[initial(icon_state)]_busy")
set_light_color("#faebd7") // "antique white"
set_light_on(TRUE)
//Cartridge Interactions in Machine
/obj/machinery/synthesizer/proc/add_cart(obj/item/C, mob/user)
if(!Adjacent(user))
return //How did you even try?
if(!panel_open) //just in case
to_chat(user, "The hatch must be open to insert a [C].")
return
if(cart) // let's hot swap that bad boy.
remove_cart(user)
return
user.drop_from_inventory(C)
cart = C
C.forceMove(src)
C.add_fingerprint(user)
to_chat(user, span_notice("You add [C] to \the [src]."))
update_icon()
SStgui.update_uis(src)
return
/obj/machinery/synthesizer/proc/remove_cart(mob/user)
var/obj/item/reagent_containers/synthdispcart/C = cart
if(!C)
to_chat(user, span_notice("There's no cartridge here...")) //Sanity checks aren't ever a bad thing
return
if(!Adjacent(user)) //gotta, y'know, be in touch range to pull a physical canister out
return
C.forceMove(get_turf(loc))
C.update_icon()
cart = null
to_chat(user, span_notice("You remove [C] from \the [src]."))
if(Adjacent(user))
user.put_in_hands(C) //pick up your trash, nerd. and don't hand it to the AI. They will be upset.
update_icon()
SStgui.update_uis(src)
/obj/machinery/synthesizer/proc/check_cart(obj/item/reagent_containers/synthdispcart/C, mob/user)
if(!istype(C))
to_chat(user, span_notice("The synthesizer cartridge is nonexistant."))
return FALSE
if((!(C.reagents)) || (C.reagents.total_volume <= 0) || (!C.reagents.has_reagent(REAGENT_ID_NUTRIPASTE_SOYLENT)))
to_chat(user, span_notice("The synthesizer cartridge depleted, replace with a genuine Sabresnack Co cartridge."))
return FALSE
if((C.reagents) && (C.reagents.total_volume <= 0) && (!C.reagents.has_reagent(REAGENT_ID_NUTRIPASTE_SOYLENT)))
to_chat(user, span_notice("Used or Counterfeit synthesizer cartridge detected."))
return FALSE
else if(C.reagents && C.reagents.has_reagent(REAGENT_ID_NUTRIPASTE_SOYLENT) && (C.reagents.total_volume >= SYNTH_FOOD_COST))
SStgui.update_uis(src)
return TRUE
/obj/machinery/synthesizer/proc/get_mob_for_picture(mob/user, mob/living/LM)
if(!istype(LM))
to_chat(user, span_warning("Warning: Invalid selection. Please refresh the database."))
return FALSE
if(LM.client?.prefs?.read_preference(/datum/preference/toggle/living/foodsynth_cookies))
return TRUE
to_chat(user, span_warning("Warning: Invalid selection. Please refresh the database."))
return FALSE
/obj/machinery/synthesizer/attackby(obj/item/W, mob/user)
if(busy)
audible_message(span_notice("\The [src] is busy. Please wait for completion of previous operation."), runemessage = "The Synthesizer is busy.")
return
if(default_part_replacement(user, W))
return
if(stat)
update_icon()
return
if(W.is_screwdriver())
panel_open = !panel_open
playsound(src, W.usesound, 50, 1)
user.visible_message(span_notice("[user] [panel_open ? "opens" : "closes"] the hatch on the [src]."), span_notice("You [panel_open ? "open" : "close"] the hatch on the [src]."))
update_icon()
return
if(panel_open)
if(istype(W, /obj/item/reagent_containers/synthdispcart))
if(!anchored)
to_chat(user, span_warning("Anchor its bolts first."))
return
if(W.w_class > cart_type) //since we confirmed it's a Cart, make sure it fits!
to_chat(user, span_warning("\The [src] only accepts smaller synthiziser cartridges."))
return
if(cart)
var/choice = alert(user, "Replace the loaded cartridge?", "", "Yes", "Cancel")
switch(choice)
if("Cancel")
return FALSE
if("Yes")
add_cart(W, user)
else
add_cart(W, user)
if(W.is_wrench())
playsound(src, W.usesound, 50, 1)
to_chat(user, span_notice("You begin to [anchored ? "un" : ""]fasten \the [src]."))
if (do_after(user, 20 * W.toolspeed))
user.visible_message(
span_notice("\The [user] [anchored ? "un" : ""]fastens \the [src]."),
span_notice("You have [anchored ? "un" : ""]fastened \the [src]."),
"You hear a ratchet.")
anchored = !anchored
update_icon()
else
to_chat(user, span_notice("You decide not to [anchored ? "un" : ""]fasten \the [src]."))
if(default_deconstruction_crowbar(user, W))
return
return ..()
/obj/machinery/synthesizer/attack_hand(mob/user as mob)
if(stat & (BROKEN|NOPOWER))
return
if(!panel_open)
tgui_interact(user)
else if(panel_open)
if(cart)
var/choice = alert(user, "Removing the Cartridge?", "", "Yes", "Cancel", "Wires Menu")
switch(choice)
if("Cancel")
return FALSE
if("Yes")
remove_cart(user)
if("Wires Menu")
wires.Interact(user)
else
wires.Interact(user)
/obj/machinery/synthesizer/attack_ai(mob/user)
return attack_hand(user)
/obj/machinery/synthesizer/interact(mob/user)
if(panel_open)
return wires.Interact(user)
if(disabled)
to_chat(user, span_danger("\The [src] is disabled!"))
return
if(shocked)
shock(user, 50)
tgui_interact(user)
//Updates performance
/obj/machinery/synthesizer/RefreshParts()
..()
menu_grade = 0
speed_grade = 0
for(var/obj/item/stock_parts/manipulator/M in component_parts)
speed_grade = (10 SECONDS) / M.rating //let's try to make it worthwhile to upgrade 'em 10s, 5s, 3.3s, 2.5s
for(var/obj/item/stock_parts/scanning_module/S in component_parts)
menu_grade = S.rating //how much bonus Nutriment is added to the printed food. the regular wafer is only 5.
// Science parts will be of help if they bother.
#undef SYNTH_FOOD_COST
//Cartridge Item handling
/obj/item/reagent_containers/synthdispcart
name = "Synthesizer cartridge"
desc = "Genuine replacement cartridge for SabreSnacks brand Food Synthesizers. It's too large for the Portable models."
icon = 'icons/obj/machines/foodsynthesizer.dmi'
icon_state = "bigcart"
w_class = ITEMSIZE_LARGE
volume = 250 //enough for feeding folk, but not so much it won't be needing replacment
//No way to refill or drain them. Like printer ink, it's only good in the machine!
amount_per_transfer_from_this = 0
max_transfer_amount = 0
min_transfer_amount = 0
flags = NONE
/obj/item/reagent_containers/synthdispcart/small
name = "Portable Synthesizer Cartridge"
desc = "Genuine replacement cartrifge SabreSnacks brand Portable Food Synthesizers. It can also fit within standard sized models."
icon_state = "Scart"
w_class = ITEMSIZE_NORMAL
volume = 100
/obj/item/reagent_containers/synthdispcart/Initialize(mapload)
. = ..()
reagents.add_reagent(REAGENT_ID_NUTRIPASTE_SOYLENT, volume)
update_icon()
/obj/item/reagent_containers/synthdispcart/update_icon()
cut_overlays()
if(reagents.total_volume)
var/image/filling_overlay = image("[icon]", src, "[initial(icon_state)]fill_0", layer = layer - 0.1)
var/percent = round((reagents.total_volume / volume) * 100)
switch(percent)
if(0 to 9) filling_overlay.icon_state = "[initial(icon_state)]fill_0"
if(10 to 35) filling_overlay.icon_state = "[initial(icon_state)]fill_25"
if(36 to 74) filling_overlay.icon_state = "[initial(icon_state)]fill_50"
if(75 to 90) filling_overlay.icon_state = "[initial(icon_state)]fill_75"
if(91 to 100) filling_overlay.icon_state = "[initial(icon_state)]fill_100"
if(100 to INFINITY) filling_overlay.icon_state = "[initial(icon_state)]fill_100"
filling_overlay.color = reagents.get_color()
add_overlay(filling_overlay)
/obj/item/reagent_containers/synthdispcart/examine(mob/user)
. = ..()
if(reagents && reagents.total_volume)
var/percent = round((reagents.total_volume / volume) * 100)
. += "The cartridge has [percent]% remaining."
/obj/item/reagent_containers/synthdispcart/is_open_container()
return FALSE //sealed, proprietary container. aka preventing alternative beaker memes.
//Circuits for contruction
/datum/design_techweb/board/synthesizer
SET_CIRCUIT_DESIGN_NAMEDESC("food synthesizer")
id = "food_synthesizer"
// req_tech = list(TECH_DATA = 3, TECH_BLUESPACE = 3, TECH_ENGINEERING = 2)
build_path = /obj/item/circuitboard/synthesizer
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_KITCHEN
)
departmental_flags = DEPARTMENT_BITFLAG_SERVICE | DEPARTMENT_BITFLAG_SCIENCE
/datum/design_techweb/board/synthesizer/mini
SET_CIRCUIT_DESIGN_NAMEDESC("compact food synthesizer")
id = "compactfood_synthesizer"
// req_tech = list(TECH_DATA = 3, TECH_BLUESPACE = 3, TECH_ENGINEERING = 2)
build_path = /obj/item/circuitboard/synthesizer/mini
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_KITCHEN
)
departmental_flags = DEPARTMENT_BITFLAG_SERVICE | DEPARTMENT_BITFLAG_SCIENCE
// Physical Boards for Food Synthesizer Construction
/obj/item/circuitboard/synthesizer
name = T_BOARD("Food Synthesizer")
desc = "The circuitboard for a Food Synthesizer."
build_path = /obj/machinery/synthesizer
board_type = new /datum/frame/frame_types/foodsynthesizer
matter = list(MAT_STEEL = 50, MAT_GLASS = 50)
req_components = list(
/obj/item/stock_parts/manipulator = 1,
/obj/item/stock_parts/scanning_module = 1)
/obj/item/circuitboard/synthesizer/mini
name = T_BOARD("Compact Food Synthesizer")
desc = "The circuitboard for a compact food synthesizer."
build_path = /obj/machinery/synthesizer/mini
board_type = new /datum/frame/frame_types/foodsynthesizer/mini
matter = list(MAT_STEEL = 50, MAT_GLASS = 50)
req_components = list(
/obj/item/stock_parts/manipulator = 1,
/obj/item/stock_parts/scanning_module = 1)
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -38,7 +38,7 @@ calculate text size per text.
for(var/datum/reagent/R in reagent_list)
if(!R.taste_mult)
continue
if(R.id == REAGENT_ID_NUTRIMENT) //this is ugly but apparently only nutriment (not subtypes) has taste data TODO figure out why
if(R.id == REAGENT_ID_NUTRIMENT || R.id == REAGENT_ID_NUTRIPASTE) //this is ugly but apparently only nutriment (not subtypes) has taste data TODO figure out why
var/list/taste_data = R.get_data()
for(var/taste in taste_data)
if(taste in tastes)
+2 -3
View File
@@ -101,7 +101,7 @@
var/aflags
var/breath_type = GAS_O2
/datum/transhuman/body_record/New(copyfrom, add_to_db = 0, ckeylock = 0)
/datum/transhuman/body_record/New(copyfrom, add_to_db = FALSE, ckeylock = FALSE)
..()
if(istype(copyfrom, /datum/transhuman/body_record))
init_from_br(copyfrom)
@@ -118,7 +118,7 @@
..()
return QDEL_HINT_HARDDEL // For now at least there is no easy way to clear references to this in GLOB.machines etc.
/datum/transhuman/body_record/proc/init_from_mob(mob/living/carbon/human/M, add_to_db = 0, ckeylock = 0, database_key)
/datum/transhuman/body_record/proc/init_from_mob(mob/living/carbon/human/M, add_to_db = FALSE, ckeylock = FALSE, database_key)
ASSERT(!QDELETED(M))
ASSERT(istype(M))
@@ -212,7 +212,6 @@
for(var/datum/modifier/mod as anything in M.modifiers)
if(mod.flags & MODIFIER_GENETIC)
genetic_modifiers.Add(mod.type)
if(add_to_db)
SStranscore.add_body(src, database_key = database_key)