diff --git a/GainStation13/code/modules/mob/living/nutribot.dm b/GainStation13/code/modules/mob/living/nutribot.dm
new file mode 100644
index 00000000..c85fcff0
--- /dev/null
+++ b/GainStation13/code/modules/mob/living/nutribot.dm
@@ -0,0 +1,735 @@
+//nutribot
+//nutribot PATHFINDING
+//nutribot ASSEMBLY
+#define NUTRIBOT_PANIC_NONE 0
+#define NUTRIBOT_PANIC_LOW 15
+#define NUTRIBOT_PANIC_MED 35
+#define NUTRIBOT_PANIC_HIGH 55
+#define NUTRIBOT_PANIC_FUCK 70
+#define NUTRIBOT_PANIC_ENDING 90
+#define NUTRIBOT_PANIC_END 100
+
+/mob/living/simple_animal/bot/nutribot
+ name = "\improper Nutribot"
+ desc = "A little nutritional robot. He looks somewhat underwhelmed."
+ icon = 'icons/mob/aibots.dmi'
+ icon_state = "nutribot0"
+ density = FALSE
+ anchored = FALSE
+ health = 20
+ maxHealth = 20
+ pass_flags = PASSMOB
+
+ status_flags = (CANPUSH | CANSTUN)
+
+ radio_key = /obj/item/encryptionkey/headset_med
+ radio_channel = RADIO_CHANNEL_MEDICAL
+
+ bot_type = MED_BOT
+ model = "Nutribot"
+ bot_core_type = /obj/machinery/bot_core/nutribot
+ window_id = "automed"
+ window_name = "Automatic Nutritional Unit v1.1"
+ data_hud_type = DATA_HUD_MEDICAL_ADVANCED
+ path_image_color = "#DDDDFF"
+
+ var/obj/item/reagent_containers/glass/reagent_glass = null //Can be set to draw from this for reagents.
+ var/healthanalyzer = /obj/item/healthanalyzer
+ var/firstaid = /obj/item/storage/firstaid
+ var/skin = null //Set to "tox", "ointment" or "o2" for the other two firstaid kits.
+ var/mob/living/carbon/patient = null
+ var/mob/living/carbon/oldpatient = null
+ var/oldloc = null
+ var/last_found = 0
+ var/last_newpatient_speak = 0 //Don't spam the "HEY I'M COMING" messages
+ var/injection_amount = 15 //How much reagent do we inject at a time?
+ var/heal_threshold = 10 //Start healing when they have this much damage in a category
+ var/feed_threshold = 200 // The weight that people should be fed to!
+ var/use_beaker = 0 //Use reagents in beaker instead of default treatment agents.
+ var/declare_crit = 1 //If active, the bot will transmit a critical patient alert to MedHUD users.
+ var/declare_cooldown = 0 //Prevents spam of critical patient alerts.
+ var/stationary_mode = 0 //If enabled, the Nutribot will not move automatically.
+ //Setting which reagents to use to treat what by default. By id.
+ var/treatment_thin = /datum/reagent/consumable/nutriment
+ var/treatment_thirsty = /datum/reagent/water
+ var/hunger_check = NUTRITION_LEVEL_FED
+ /*var/treatment_brute_avoid = /datum/reagent/medicine/tricordrazine
+ var/treatment_brute = /datum/reagent/medicine/bicaridine
+ var/treatment_oxy_avoid = null
+ var/treatment_oxy = /datum/reagent/medicine/dexalin
+ var/treatment_fire_avoid = /datum/reagent/medicine/tricordrazine
+ var/treatment_fire = /datum/reagent/medicine/kelotane
+ var/treatment_tox_avoid = /datum/reagent/medicine/tricordrazine
+ var/treatment_tox = /datum/reagent/medicine/charcoal
+ var/treatment_tox_toxlover = /datum/reagent/toxin
+ var/treatment_virus_avoid = null
+ var/treatment_virus = /datum/reagent/medicine/spaceacillin
+ var/treat_virus = 1 //If on, the bot will attempt to treat viral infections, curing them if possible.
+ */
+ var/shut_up = 0 //self explanatory :)
+
+ //How panicked we are about being tipped over (why would you do this?)
+ var/tipped_status = NUTRIBOT_PANIC_NONE
+ //The name we got when we were tipped
+ var/tipper_name
+ //The last time we were tipped/righted and said a voice line, to avoid spam
+ var/last_tipping_action_voice = 0
+
+/*
+/mob/living/simple_animal/bot/nutribot/mysterious
+ name = "\improper Mysterious Nutribot"
+ desc = "International Nutribot of mystery."
+ skin = "bezerk"
+ treatment_brute = /datum/reagent/medicine/tricordrazine
+ treatment_fire = /datum/reagent/medicine/tricordrazine
+ treatment_tox = /datum/reagent/medicine/tricordrazine
+*/
+
+/*
+/mob/living/simple_animal/bot/nutribot/derelict
+ name = "\improper Old Nutribot"
+ desc = "Looks like it hasn't been modified since the late 2080s."
+ skin = "bezerk"
+ heal_threshold = 0
+ declare_crit = 0
+ treatment_oxy = /datum/reagent/toxin/pancuronium
+ treatment_brute_avoid = null
+ treatment_brute = /datum/reagent/toxin/pancuronium
+ treatment_fire_avoid = null
+ treatment_fire = /datum/reagent/toxin/sodium_thiopental
+ treatment_tox_avoid = null
+ treatment_tox = /datum/reagent/toxin/sodium_thiopental
+*/
+
+/mob/living/simple_animal/bot/nutribot/update_icon()
+ cut_overlays()
+ if(!on)
+ icon_state = "off"
+ return
+ if(IsStun())
+ icon_state = "medibota"
+ return
+ if(mode == BOT_HEALING)
+ icon_state = "medibots[stationary_mode]"
+ return
+ else if(stationary_mode) //Bot has yellow light to indicate stationary mode.
+ icon_state = "medibot2"
+ else
+ icon_state = "medibot1"
+
+/mob/living/simple_animal/bot/nutribot/Initialize(mapload, new_skin)
+ . = ..()
+ var/datum/job/doctor/J = new /datum/job/doctor
+ access_card.access += J.get_access()
+ prev_access = access_card.access
+ qdel(J)
+ skin = new_skin
+ update_icon()
+
+/mob/living/simple_animal/bot/nutribot/update_canmove()
+ . = ..()
+ update_icon()
+
+/mob/living/simple_animal/bot/nutribot/bot_reset()
+ ..()
+ patient = null
+ oldpatient = null
+ oldloc = null
+ last_found = world.time
+ declare_cooldown = 0
+ update_icon()
+
+/mob/living/simple_animal/bot/nutribot/proc/soft_reset() //Allows the Nutribot to still actively perform its medical duties without being completely halted as a hard reset does.
+ path = list()
+ patient = null
+ mode = BOT_IDLE
+ last_found = world.time
+ update_icon()
+
+/mob/living/simple_animal/bot/nutribot/set_custom_texts()
+
+ text_hack = "You corrupt [name]'s reagent processor circuits."
+ text_dehack = "You reset [name]'s reagent processor circuits."
+ text_dehack_fail = "[name] seems damaged and does not respond to reprogramming!"
+
+/mob/living/simple_animal/bot/nutribot/attack_paw(mob/user)
+ return attack_hand(user)
+
+/mob/living/simple_animal/bot/nutribot/get_controls(mob/user)
+ var/dat
+ dat += hack(user)
+ dat += showpai(user)
+ dat += "Medical Unit Controls v1.1
"
+ dat += "Status: [on ? "On" : "Off"]
"
+ dat += "Maintenance panel panel is [open ? "opened" : "closed"]
"
+ dat += "Beaker: "
+ if(reagent_glass)
+ dat += "Loaded \[[reagent_glass.reagents.total_volume]/[reagent_glass.reagents.maximum_volume]\]"
+ else
+ dat += "None Loaded"
+ dat += "
Behaviour controls are [locked ? "locked" : "unlocked"]
"
+ if(!locked || issilicon(user) || IsAdminGhost(user))
+ /*dat += "Healing Threshold: "
+ dat += "-- "
+ dat += "- "
+ dat += "[heal_threshold] "
+ dat += "+ "
+ dat += "++"
+ dat += "
"*/
+
+ dat += "Injection Level: "
+ dat += "- "
+ dat += "[injection_amount] "
+ dat += "+ "
+ dat += "
"
+
+ dat += "Reagent Source: "
+ dat += "[use_beaker ? "Loaded Beaker (When available)" : "Internal Synthesizer"]
"
+
+ dat += "Hunger Check: "
+ dat += "[(hunger_check==NUTRITION_LEVEL_HUNGRY) ? "Yes" : "No"]
"
+
+ dat += "Maximum Weight: "
+ dat += "[feed_threshold ? 140+(feed_threshold*FATNESS_TO_WEIGHT_RATIO) : "???"] "
+ dat += "Disable Maximum Weight: ["Disable"]
"
+ dat += "- "
+ dat += "+ "
+ dat += "
"
+
+ //dat += "Treat Viral Infections: [treat_virus ? "Yes" : "No"]
"
+ dat += "The speaker switch is [shut_up ? "off" : "on"]. Toggle
"
+ //dat += "Critical Patient Alerts: [declare_crit ? "Yes" : "No"]
"
+ dat += "Patrol Station: [auto_patrol ? "Yes" : "No"]
"
+ dat += "Stationary Mode: [stationary_mode ? "Yes" : "No"]
"
+
+ return dat
+
+/mob/living/simple_animal/bot/nutribot/Topic(href, href_list)
+ if(..())
+ return 1
+
+ /*if(href_list["adj_threshold"])
+ var/adjust_num = text2num(href_list["adj_threshold"])
+ heal_threshold += adjust_num
+ if(heal_threshold < 5)
+ heal_threshold = 5
+ if(heal_threshold > 75)
+ heal_threshold = 75
+ */
+
+ else if(href_list["adj_inject"])
+ var/adjust_num = text2num(href_list["adj_inject"])
+ injection_amount += adjust_num
+ if(injection_amount < 5)
+ injection_amount = 5
+ if(injection_amount > 15)
+ injection_amount = 15
+
+ else if(href_list["use_beaker"])
+ use_beaker = !use_beaker
+
+ else if(href_list["eject"] && (!isnull(reagent_glass)))
+ reagent_glass.forceMove(drop_location())
+ reagent_glass = null
+
+ else if(href_list["togglevoice"])
+ shut_up = !shut_up
+
+ /*else if(href_list["critalerts"])
+ declare_crit = !declare_crit*/
+
+ else if(href_list["stationary"])
+ stationary_mode = !stationary_mode
+ path = list()
+ update_icon()
+
+ //else if(href_list["virus"])
+ //treat_virus = !treat_virus
+
+ else if(href_list["hunger_check"])
+ hunger_check = ((hunger_check==NUTRITION_LEVEL_HUNGRY) ? NUTRITION_LEVEL_FULL : NUTRITION_LEVEL_HUNGRY)
+
+ else if(href_list["feed_threshold"])
+ var/adjust_num = text2num(href_list["feed_threshold"])
+ feed_threshold += adjust_num
+ if(adjust_num == 1)
+ feed_threshold = 0
+ if(feed_threshold < 0)
+ feed_threshold = 0
+
+ update_controls()
+ return
+
+/mob/living/simple_animal/bot/nutribot/attackby(obj/item/W as obj, mob/user as mob, params)
+ if(istype(W, /obj/item/reagent_containers/glass))
+ . = 1 //no afterattack
+ if(locked)
+ to_chat(user, "You cannot insert a beaker because the panel is locked!")
+ return
+ if(!isnull(reagent_glass))
+ to_chat(user, "There is already a beaker loaded!")
+ return
+ if(!user.transferItemToLoc(W, src))
+ return
+
+ reagent_glass = W
+ to_chat(user, "You insert [W].")
+ show_controls(user)
+
+ else
+ var/current_health = health
+ ..()
+ if(health < current_health) //if nutribot took some damage
+ step_to(src, (get_step_away(src,user)))
+
+/mob/living/simple_animal/bot/nutribot/emag_act(mob/user)
+ ..()
+ if(emagged == 2)
+ //declare_crit = 0
+ if(user)
+ to_chat(user, "You short out [src]'s reagent synthesis circuits.")
+ audible_message("[src] buzzes oddly!")
+ flick("nutribot_spark", src)
+ playsound(src, "sparks", 75, 1)
+ if(user)
+ oldpatient = user
+
+/mob/living/simple_animal/bot/nutribot/process_scan(mob/living/carbon/human/H)
+ if(H.stat == DEAD)
+ return
+
+ if((H == oldpatient) && (world.time < last_found + 200))
+ return
+
+ if(assess_patient(H))
+ last_found = world.time
+ if((last_newpatient_speak + 300) < world.time) //Don't spam these messages!
+ var/list/messagevoice = list("Hey, [H.name]! Hold on, I'm coming." = 'sound/voice/medbot/coming.ogg',"Wait [H.name]! I want to help!" = 'sound/voice/medbot/help.ogg',"[H.name], you appear to be injured!" = 'sound/voice/medbot/injured.ogg')
+ var/message = pick(messagevoice)
+ if(prob(2) && ISINRANGE_EX(H.getFireLoss(), 0, 20))
+ message = "Notices your minor burns*OwO what's this?"
+ messagevoice[message] = 'sound/voice/medbot/owo.ogg'
+ speak(message)
+ playsound(loc, messagevoice[message], 50, 0)
+ last_newpatient_speak = world.time
+ return H
+ else
+ return
+
+/mob/living/simple_animal/bot/nutribot/proc/tip_over(mob/user)
+ canmove = 0
+ //mobility_flags &= ~MOBILITY_MOVE
+ playsound(src, 'sound/machines/warning-buzzer.ogg', 50)
+ user.visible_message("[user] tips over [src]!", "You tip [src] over!")
+ mode = BOT_TIPPED
+ var/matrix/mat = transform
+ transform = mat.Turn(180)
+
+/mob/living/simple_animal/bot/nutribot/proc/set_right(mob/user)
+ canmove = 1
+ //mobility_flags &= MOBILITY_MOVE
+ var/list/messagevoice
+ if(user)
+ user.visible_message("[user] sets [src] right-side up!", "You set [src] right-side up!")
+ if(user.name == tipper_name)
+ messagevoice = list("I forgive you." = 'sound/voice/medbot/forgive.ogg')
+ else
+ messagevoice = list("Thank you!" = 'sound/voice/medbot/thank_you.ogg', "You are a good person." = 'sound/voice/medbot/youre_good.ogg')
+ else
+ visible_message("[src] manages to writhe wiggle enough to right itself.")
+ messagevoice = list("Fuck you." = 'sound/voice/medbot/fuck_you.ogg', "Your behavior has been reported, have a nice day." = 'sound/voice/medbot/reported.ogg')
+
+ tipper_name = null
+ if(world.time > last_tipping_action_voice + 15 SECONDS)
+ last_tipping_action_voice = world.time
+ var/message = pick(messagevoice)
+ speak(message)
+ playsound(src, messagevoice[message], 70)
+ tipped_status = NUTRIBOT_PANIC_NONE
+ mode = BOT_IDLE
+ transform = matrix()
+
+// if someone tipped us over, check whether we should ask for help or just right ourselves eventually
+/mob/living/simple_animal/bot/nutribot/proc/handle_panic()
+ tipped_status++
+ var/list/messagevoice
+ switch(tipped_status)
+ if(NUTRIBOT_PANIC_LOW)
+ messagevoice = list("I require assistance." = 'sound/voice/medbot/i_require_asst.ogg')
+ if(NUTRIBOT_PANIC_MED)
+ messagevoice = list("Please put me back." = 'sound/voice/medbot/please_put_me_back.ogg')
+ if(NUTRIBOT_PANIC_HIGH)
+ messagevoice = list("Please, I am scared!" = 'sound/voice/medbot/please_im_scared.ogg')
+ if(NUTRIBOT_PANIC_FUCK)
+ messagevoice = list("I don't like this, I need help!" = 'sound/voice/medbot/dont_like.ogg', "This hurts, my pain is real!" = 'sound/voice/medbot/pain_is_real.ogg')
+ if(NUTRIBOT_PANIC_ENDING)
+ messagevoice = list("Is this the end?" = 'sound/voice/medbot/is_this_the_end.ogg', "Nooo!" = 'sound/voice/medbot/nooo.ogg')
+ if(NUTRIBOT_PANIC_END)
+ speak("PSYCH ALERT: Crewmember [tipper_name] recorded displaying antisocial tendencies torturing bots in [get_area(src)]. Please schedule psych evaluation.", radio_channel)
+ set_right() // strong independent nutribot
+
+ if(prob(tipped_status))
+ do_jitter_animation(tipped_status * 0.1)
+
+ if(messagevoice)
+ var/message = pick(messagevoice)
+ speak(message)
+ playsound(src, messagevoice[message], 70)
+ else if(prob(tipped_status * 0.2))
+ playsound(src, 'sound/machines/warning-buzzer.ogg', 30, extrarange=-2)
+
+/mob/living/simple_animal/bot/nutribot/examine(mob/user)
+ . = ..()
+ if(tipped_status == NUTRIBOT_PANIC_NONE)
+ return
+
+ switch(tipped_status)
+ if(NUTRIBOT_PANIC_NONE to NUTRIBOT_PANIC_LOW)
+ . += "It appears to be tipped over, and is quietly waiting for someone to set it right."
+ if(NUTRIBOT_PANIC_LOW to NUTRIBOT_PANIC_MED)
+ . += "It is tipped over and requesting help."
+ if(NUTRIBOT_PANIC_MED to NUTRIBOT_PANIC_HIGH)
+ . += "They are tipped over and appear visibly distressed." // now we humanize the nutribot as a they, not an it
+ if(NUTRIBOT_PANIC_HIGH to NUTRIBOT_PANIC_FUCK)
+ . += "They are tipped over and visibly panicking!"
+ if(NUTRIBOT_PANIC_FUCK to INFINITY)
+ . += "They are freaking out from being tipped over!"
+
+/mob/living/simple_animal/bot/nutribot/handle_automated_action()
+ if(!..())
+ return
+
+ if(mode == BOT_TIPPED)
+ handle_panic()
+ return
+
+ if(mode == BOT_HEALING)
+ return
+
+ if(IsStun())
+ oldpatient = patient
+ patient = null
+ mode = BOT_IDLE
+ return
+
+ if(frustration > 8)
+ oldpatient = patient
+ soft_reset()
+
+ if(QDELETED(patient))
+ if(!shut_up && prob(1))
+ if(emagged && prob(30))
+ var/list/i_need_scissors = list('sound/voice/medbot/fuck_you.ogg', 'sound/voice/medbot/turn_off.ogg', 'sound/voice/medbot/im_different.ogg', 'sound/voice/medbot/close.ogg', 'sound/voice/medbot/shindemashou.ogg')
+ playsound(src, pick(i_need_scissors), 70)
+ else
+ var/list/messagevoice = list("Radar, put a mask on!" = 'sound/voice/medbot/radar.ogg',"There's always a catch, and I'm the best there is." = 'sound/voice/medbot/catch.ogg',"I knew it, I should've been a plastic surgeon." = 'sound/voice/medbot/surgeon.ogg',"What kind of medbay is this? Everyone's dropping like flies." = 'sound/voice/medbot/flies.ogg',"Delicious!" = 'sound/voice/medbot/delicious.ogg', "Why are we still here? Just to suffer?" = 'sound/voice/medbot/why.ogg')
+ var/message = pick(messagevoice)
+ speak(message)
+ playsound(src, messagevoice[message], 50)
+ var/scan_range = (stationary_mode ? 1 : DEFAULT_SCAN_RANGE) //If in stationary mode, scan range is limited to adjacent patients.
+ patient = scan(/mob/living/carbon/human, oldpatient, scan_range)
+ oldpatient = patient
+
+ if(patient && (get_dist(src,patient) <= 1)) //Patient is next to us, begin treatment!
+ if(mode != BOT_HEALING)
+ mode = BOT_HEALING
+ update_icon()
+ frustration = 0
+ medicate_patient(patient)
+ return
+
+ //Patient has moved away from us!
+ else if(patient && path.len && (get_dist(patient,path[path.len]) > 2))
+ path = list()
+ mode = BOT_IDLE
+ last_found = world.time
+
+ else if(stationary_mode && patient) //Since we cannot move in this mode, ignore the patient and wait for another.
+ soft_reset()
+ return
+
+ if(patient && path.len == 0 && (get_dist(src,patient) > 1))
+ path = get_path_to(src, get_turf(patient), /turf/proc/Distance_cardinal, 0, 30,id=access_card)
+ mode = BOT_MOVING
+ if(!path.len) //try to get closer if you can't reach the patient directly
+ path = get_path_to(src, get_turf(patient), /turf/proc/Distance_cardinal, 0, 30,1,id=access_card)
+ if(!path.len) //Do not chase a patient we cannot reach.
+ soft_reset()
+
+ if(path.len > 0 && patient)
+ if(!bot_move(path[path.len]))
+ oldpatient = patient
+ soft_reset()
+ return
+
+ if(path.len > 8 && patient)
+ frustration++
+
+ if(auto_patrol && !stationary_mode && !patient)
+ if(mode == BOT_IDLE || mode == BOT_START_PATROL)
+ start_patrol()
+
+ if(mode == BOT_PATROL)
+ bot_patrol()
+
+ return
+
+/mob/living/simple_animal/bot/nutribot/proc/assess_patient(mob/living/carbon/C)
+ //Time to see if they need medical help!
+ if(C.stat == DEAD || (HAS_TRAIT(C, TRAIT_FAKEDEATH)))
+ return FALSE //welp too late for them!
+
+ if(!(loc == C.loc) && !(isturf(C.loc) && isturf(loc)))
+ return FALSE
+
+ if(C.suiciding)
+ return FALSE //Kevorkian school of robotic medical assistants.
+
+ if(emagged == 2) //Everyone needs our medicine. (Our medicine is corn oil)
+ return TRUE
+
+ if(ishuman(C))
+ var/mob/living/carbon/human/H = C
+ if (H.wear_suit && H.head && istype(H.wear_suit, /obj/item/clothing) && istype(H.head, /obj/item/clothing))
+ var/obj/item/clothing/CS = H.wear_suit
+ var/obj/item/clothing/CH = H.head
+ if (CS.clothing_flags & CH.clothing_flags & THICKMATERIAL)
+ return FALSE // Skip over them if they have no exposed flesh.
+
+ //if(declare_crit && C.health <= 0) //Critical condition! Call for help!
+ //declare(C)
+
+ //If they're injured, we're using a beaker, and don't have one of our WONDERCHEMS.
+ if((reagent_glass) && (use_beaker) && (C.fatness <= feed_threshold))
+ for(var/A in reagent_glass.reagents.reagent_list)
+ var/datum/reagent/R = A
+ if(!C.reagents.has_reagent(R.type))
+ return TRUE
+
+ //They're injured enough for it!
+ if((C.nutrition <= hunger_check) && (C.fatness <= feed_threshold) && (!C.reagents.has_reagent(treatment_thin)))
+ return TRUE //If they're already medicated don't bother!
+
+ if((C.thirst <= THIRST_LEVEL_QUENCHED) && (!C.reagents.has_reagent(treatment_thirsty)))
+ return TRUE //If they're already medicated don't bother!
+
+ /*var/treatment_toxavoid = get_avoidchem_toxin(C)
+ if(((isnull(treatment_toxavoid) || !C.reagents.has_reagent(treatment_toxavoid))) && (C.getToxLoss() >= heal_threshold) && (!C.reagents.has_reagent(get_healchem_toxin(C))))
+ return TRUE
+
+ if(treat_virus && !C.reagents.has_reagent(treatment_virus_avoid) && !C.reagents.has_reagent(treatment_virus))
+ for(var/thing in C.diseases)
+ var/datum/disease/D = thing
+ //the Nutribot can't detect viruses that are undetectable to Health Analyzers or Pandemic machines.
+ if(!(D.visibility_flags & HIDDEN_SCANNER || D.visibility_flags & HIDDEN_PANDEMIC) \
+ && D.severity != DISEASE_SEVERITY_POSITIVE \
+ && (D.stage > 1 || (D.spread_flags & DISEASE_SPREAD_AIRBORNE))) // Nutribot can't detect a virus in its initial stage unless it spreads airborne.
+ return TRUE //STOP DISEASE FOREVER
+ */
+
+ return FALSE
+
+/mob/living/simple_animal/bot/nutribot/attack_hand(mob/living/carbon/human/H)
+ if(H.a_intent == INTENT_DISARM && mode != BOT_TIPPED)
+ H.visible_message("[H] begins tipping over [src].", "You begin tipping over [src]...")
+
+ if(world.time > last_tipping_action_voice + 15 SECONDS)
+ last_tipping_action_voice = world.time // message for tipping happens when we start interacting, message for righting comes after finishing
+ var/list/messagevoice = list("Hey, wait..." = 'sound/voice/medbot/hey_wait.ogg',"Please don't..." = 'sound/voice/medbot/please_dont.ogg',"I trusted you..." = 'sound/voice/medbot/i_trusted_you.ogg', "Nooo..." = 'sound/voice/medbot/nooo.ogg', "Oh fuck-" = 'sound/voice/medbot/oh_fuck.ogg')
+ var/message = pick(messagevoice)
+ speak(message)
+ playsound(src, messagevoice[message], 70, FALSE)
+
+ if(do_after(H, 3 SECONDS, target=src))
+ tip_over(H)
+
+ else if(H.a_intent == INTENT_HELP && mode == BOT_TIPPED)
+ H.visible_message("[H] begins righting [src].", "You begin righting [src]...")
+ if(do_after(H, 3 SECONDS, target=src))
+ set_right(H)
+ else
+ ..()
+
+/*
+/mob/living/simple_animal/bot/nutribot/proc/get_avoidchem_toxin(mob/M)
+ return HAS_TRAIT(M, TRAIT_TOXINLOVER)? null : treatment_tox_avoid
+*/
+
+/*
+/mob/living/simple_animal/bot/nutribot/proc/get_healchem_toxin(mob/M)
+ return HAS_TRAIT(M, TRAIT_TOXINLOVER)? treatment_tox_toxlover : treatment_tox
+*/
+
+/mob/living/simple_animal/bot/nutribot/UnarmedAttack(atom/A)
+ if(iscarbon(A))
+ var/mob/living/carbon/C = A
+ patient = C
+ mode = BOT_HEALING
+ update_icon()
+ medicate_patient(C)
+ update_icon()
+ else
+ ..()
+
+/mob/living/simple_animal/bot/nutribot/examinate(atom/A as mob|obj|turf in view())
+ ..()
+ if(!is_blind(src))
+ chemscan(src, A)
+
+/mob/living/simple_animal/bot/nutribot/proc/medicate_patient(mob/living/carbon/C)
+ if(!on)
+ return
+
+ if(!istype(C))
+ oldpatient = patient
+ soft_reset()
+ return
+
+ if(C.stat == DEAD || (HAS_TRAIT(C, TRAIT_FAKEDEATH)))
+ var/list/messagevoice = list("No! Stay with me!" = 'sound/voice/medbot/no.ogg',"Live, damnit! LIVE!" = 'sound/voice/medbot/live.ogg',"I...I've never lost a patient before. Not today, I mean." = 'sound/voice/medbot/lost.ogg')
+ var/message = pick(messagevoice)
+ speak(message)
+ playsound(loc, messagevoice[message], 50, 0)
+ oldpatient = patient
+ soft_reset()
+ return
+
+ var/reagent_id = null
+
+ if(emagged == 2) //Emagged! Time to poison everybody.
+ reagent_id = "emagged" //evil fucking check for a string as a reagent this shit is evil. its supposed to inject corn oil AND filzulphite but ill handle that later
+
+ else
+ /*if(treat_virus)
+ var/virus = 0
+ for(var/thing in C.diseases)
+ var/datum/disease/D = thing
+ //detectable virus
+ if((!(D.visibility_flags & HIDDEN_SCANNER)) || (!(D.visibility_flags & HIDDEN_PANDEMIC)))
+ if(D.severity != DISEASE_SEVERITY_POSITIVE) //virus is harmful
+ if((D.stage > 1) || (D.spread_flags & DISEASE_SPREAD_AIRBORNE))
+ virus = 1
+
+ if(!reagent_id && (virus))
+ if(!C.reagents.has_reagent(treatment_virus) && !C.reagents.has_reagent(treatment_virus_avoid))
+ reagent_id = treatment_virus*/
+
+ if(!reagent_id && ((feed_threshold == 0) || (C.fatness <= feed_threshold)) && (C.nutrition <= hunger_check))
+ if(!C.reagents.has_reagent(treatment_thin))
+ reagent_id = treatment_thin
+
+ if(!reagent_id && (C.thirst <= THIRST_LEVEL_THIRSTY))
+ if(!C.reagents.has_reagent(treatment_thirsty))
+ reagent_id = treatment_thirsty
+
+ /*if(!reagent_id && (C.getOxyLoss() >= (15 + heal_threshold)))
+ if(!C.reagents.has_reagent(treatment_oxy) && !C.reagents.has_reagent(treatment_oxy_avoid))
+ reagent_id = treatment_oxy
+
+ if(!reagent_id && (C.getFireLoss() >= heal_threshold))
+ if(!C.reagents.has_reagent(treatment_fire) && !C.reagents.has_reagent(treatment_fire_avoid))
+ reagent_id = treatment_fire
+
+ if(!reagent_id && (C.getToxLoss() >= heal_threshold))
+ var/toxin_heal_avoid = get_avoidchem_toxin(C)
+ var/toxin_healchem = get_healchem_toxin(C)
+ if(!C.reagents.has_reagent(toxin_healchem) && (isnull(toxin_heal_avoid) || !C.reagents.has_reagent(toxin_heal_avoid)))
+ reagent_id = toxin_healchem*/
+
+ //If the patient is injured but doesn't have our special reagent in them then we should give it to them first
+ if(reagent_id && use_beaker && reagent_glass && reagent_glass.reagents.total_volume)
+ for(var/A in reagent_glass.reagents.reagent_list)
+ var/datum/reagent/R = A
+ if(!C.reagents.has_reagent(R.type))
+ reagent_id = "internal_beaker"
+ break
+
+ if(!reagent_id) //If they don't need any of that they're probably cured!
+ if(C.maxHealth - C.health < heal_threshold)
+ to_chat(src, "[C] is healthy! Your programming prevents you from injecting anyone without at least [heal_threshold] damage of any one type ([heal_threshold + 15] for oxygen damage.)")
+ var/list/messagevoice = list("All patched up!" = 'sound/voice/medbot/patchedup.ogg',"An apple a day keeps me away." = 'sound/voice/medbot/apple.ogg',"Feel better soon!" = 'sound/voice/medbot/feelbetter.ogg')
+ var/message = pick(messagevoice)
+ speak(message)
+ playsound(loc, messagevoice[message], 50, 0)
+ bot_reset()
+ return
+ else
+ if(!emagged && check_overdose(patient,reagent_id,injection_amount))
+ soft_reset()
+ return
+ C.visible_message("[src] is trying to inject [patient]!", \
+ "[src] is trying to inject you!")
+
+ var/failed = FALSE
+ if(do_mob(src, patient, 30)) //Is C == patient? This is so confusing
+ if((get_dist(src, patient) <= 1) && (on) && assess_patient(patient))
+ if(reagent_id == "internal_beaker")
+ if(use_beaker && reagent_glass && reagent_glass.reagents.total_volume)
+ var/fraction = min(injection_amount/reagent_glass.reagents.total_volume, 1)
+ reagent_glass.reagents.reaction(patient, INJECT, fraction)
+ reagent_glass.reagents.trans_to(patient,injection_amount) //Inject from beaker instead.
+ else
+ patient.reagents.add_reagent(reagent_id,injection_amount)
+ C.visible_message("[src] injects [patient] with its syringe!", \
+ "[src] injects you with its syringe!")
+ else
+ failed = TRUE
+ else
+ failed = TRUE
+
+ if(failed)
+ visible_message("[src] retracts its syringe.")
+ update_icon()
+ soft_reset()
+ return
+
+ reagent_id = null
+ return
+
+/mob/living/simple_animal/bot/nutribot/proc/check_overdose(mob/living/carbon/patient,reagent_id,injection_amount)
+ var/datum/reagent/R = GLOB.chemical_reagents_list[reagent_id]
+ if(!R.overdose_threshold) //Some chems do not have an OD threshold
+ return FALSE
+ var/current_volume = patient.reagents.get_reagent_amount(reagent_id)
+ if(current_volume + injection_amount > R.overdose_threshold)
+ return TRUE
+ return FALSE
+
+/mob/living/simple_animal/bot/nutribot/explode()
+ on = FALSE
+ visible_message("[src] blows apart!")
+ var/atom/Tsec = drop_location()
+
+ drop_part(firstaid, Tsec)
+ new /obj/item/assembly/prox_sensor(Tsec)
+ drop_part(healthanalyzer, Tsec)
+
+ if(reagent_glass)
+ drop_part(reagent_glass, Tsec)
+
+ if(prob(50))
+ drop_part(robot_arm, Tsec)
+
+ if(emagged && prob(25))
+ playsound(loc, 'sound/voice/medbot/insult.ogg', 50, 0)
+
+ do_sparks(3, TRUE, src)
+ ..()
+
+/mob/living/simple_animal/bot/nutribot/proc/declare(crit_patient)
+ if(declare_cooldown > world.time)
+ return
+ var/area/location = get_area(src)
+ speak("Medical emergency! [crit_patient ? "[crit_patient]" : "A patient"] is in critical condition at [location]!",radio_channel)
+ declare_cooldown = world.time + 200
+
+/obj/machinery/bot_core/nutribot
+ req_one_access = list(ACCESS_MEDICAL, ACCESS_ROBOTICS)
+
+#undef NUTRIBOT_PANIC_NONE
+#undef NUTRIBOT_PANIC_LOW
+#undef NUTRIBOT_PANIC_MED
+#undef NUTRIBOT_PANIC_HIGH
+#undef NUTRIBOT_PANIC_FUCK
+#undef NUTRIBOT_PANIC_ENDING
+#undef NUTRIBOT_PANIC_END