From abd366fcf30d0bc7e3521c10ad8e979232c98527 Mon Sep 17 00:00:00 2001
From: Kapu1178 <75460809+Kapu1178@users.noreply.github.com>
Date: Thu, 26 Dec 2024 01:02:32 -0500
Subject: [PATCH] Bundle o fixes and changes (#1165)
* make current medbot work
* forgor
* bugs
* fix pathfinding bug
* cleanup ignore_list handling
* make the timeout window smaller
* add BOT_PATHING state
* set_mode
---
code/__DEFINES/robots.dm | 47 ++
code/__HELPERS/paths/jps_path.dm | 17 +-
code/__HELPERS/paths/path.dm | 21 +-
code/game/data_huds.dm | 2 +-
.../items/devices/scanners/health_analyzer.dm | 5 +-
code/modules/clothing/suits/armor.dm | 1 +
code/modules/do_after/do_after.dm | 2 +-
code/modules/mob/living/damage_procs.dm | 19 +
.../living/simple_animal/bot/SuperBeepsky.dm | 4 +-
.../mob/living/simple_animal/bot/bot.dm | 155 ++++---
.../mob/living/simple_animal/bot/cleanbot.dm | 17 +-
.../mob/living/simple_animal/bot/ed209bot.dm | 2 +-
.../mob/living/simple_animal/bot/firebot.dm | 11 +-
.../mob/living/simple_animal/bot/floorbot.dm | 14 +-
.../mob/living/simple_animal/bot/honkbot.dm | 2 +-
.../living/simple_animal/bot/hygienebot.dm | 12 +-
.../mob/living/simple_animal/bot/medbot.dm | 410 ++++++++++--------
.../mob/living/simple_animal/bot/mulebot.dm | 36 +-
.../mob/living/simple_animal/bot/secbot.dm | 22 +-
code/modules/mob/mob.dm | 5 +-
.../chemistry/reagents/medicine_reagents.dm | 4 +-
21 files changed, 472 insertions(+), 336 deletions(-)
diff --git a/code/__DEFINES/robots.dm b/code/__DEFINES/robots.dm
index d8aabb1e17c5..d4fd44c293c0 100644
--- a/code/__DEFINES/robots.dm
+++ b/code/__DEFINES/robots.dm
@@ -152,6 +152,8 @@ DEFINE_BITFIELD(bot_cover_flags, list(
#define BOT_RESPONDING "Proceeding to AI waypoint"
/// Currently moving
#define BOT_MOVING "Moving"
+/// Currently making a path
+#define BOT_PATHING "Pathing"
// Unique modes //
/// Secbot - At target, preparing to arrest
@@ -225,3 +227,48 @@ DEFINE_BITFIELD(janitor_mode_flags, list(
"CLEANBOT_CLEAN_PESTS" = CLEANBOT_CLEAN_PESTS,
"CLEANBOT_CLEAN_DRAWINGS" = CLEANBOT_CLEAN_DRAWINGS,
))
+
+#define MEDIBOT_VOICED_HOLD_ON "Hey, %TARGET%! Hold on, I'm coming."
+#define MEDIBOT_VOICED_WANT_TO_HELP "Wait, %TARGET%! I want to help!"
+#define MEDIBOT_VOICED_YOU_ARE_INJURED "%TARGET%, you appear to be injured!"
+
+#define MEDIBOT_VOICED_ALL_PATCHED_UP "All patched up!"
+#define MEDIBOT_VOICED_APPLE_A_DAY "An apple a day keeps me away."
+#define MEDIBOT_VOICED_FEEL_BETTER "Feel better soon!"
+
+#define MEDIBOT_VOICED_STAY_WITH_ME "No! Stay with me!"
+#define MEDIBOT_VOICED_LIVE "Live, damnit! LIVE!"
+#define MEDIBOT_VOICED_NEVER_LOST "I...I've never lost a patient before. Not today, I mean."
+
+#define MEDIBOT_VOICED_DELICIOUS "Delicious!"
+#define MEDIBOT_VOICED_PLASTIC_SURGEON "I knew it, I should've been a plastic surgeon."
+#define MEDIBOT_VOICED_MASK_ON "Radar, put a mask on!"
+#define MEDIBOT_VOICED_ALWAYS_A_CATCH "There's always a catch, and I'm the best there is."
+#define MEDIBOT_VOICED_LIKE_FLIES "What kind of medbay is this? Everyone's dropping like flies."
+#define MEDIBOT_VOICED_SUFFER "Why are we still here? Just to suffer?"
+
+#define MEDIBOT_VOICED_FUCK_YOU "Fuck you."
+#define MEDIBOT_VOICED_NOT_A_GAME "Turn off your computer. This is not a game."
+#define MEDIBOT_VOICED_IM_DIFFERENT "I'm different!"
+#define MEDIBOT_VOICED_FOURTH_WALL "Close Dreamseeker.exe now. Or else."
+#define MEDIBOT_VOICED_SHINDEMASHOU "Shindemashou."
+
+#define MEDIBOT_VOICED_WAIT "Hey, wait..."
+#define MEDIBOT_VOICED_DONT "Please don't..."
+#define MEDIBOT_VOICED_TRUSTED_YOU "I trusted you..."
+#define MEDIBOT_VOICED_NO_SAD "Nooo..."
+#define MEDIBOT_VOICED_OH_FUCK "Oh fuck-"
+
+#define MEDIBOT_VOICED_FORGIVE "I forgive you."
+#define MEDIBOT_VOICED_THANKS "Thank you!"
+#define MEDIBOT_VOICED_GOOD_PERSON "You are a good person."
+#define MEDIBOT_VOICED_BEHAVIOUR_REPORTED "Your behavior has been reported, have a nice day."
+
+#define MEDIBOT_VOICED_ASSISTANCE "I require assistance."
+#define MEDIBOT_VOICED_PUT_BACK "Please put me back."
+#define MEDIBOT_VOICED_IM_SCARED "Please, I am scared!"
+#define MEDIBOT_VOICED_NEED_HELP "I don't like this, I need help!"
+#define MEDIBOT_VOICED_THIS_HURTS "This hurts, my pain is real!"
+#define MEDIBOT_VOICED_THE_END "Is this the end?"
+#define MEDIBOT_VOICED_NOOO "Nooo!"
+#define MEDIBOT_VOICED_CHICKEN "LOOK AT ME?! I am a chicken."
diff --git a/code/__HELPERS/paths/jps_path.dm b/code/__HELPERS/paths/jps_path.dm
index a01cd315910a..b8b531ca5a12 100644
--- a/code/__HELPERS/paths/jps_path.dm
+++ b/code/__HELPERS/paths/jps_path.dm
@@ -147,16 +147,17 @@
QDEL_NULL(open)
var/list/path = src.path || list()
- reverse_range(path)
+ if(length(path))
+ reverse_range(path)
- switch(diagonal_handling)
- if(DIAGONAL_REMOVE_CLUNKY)
- path = remove_clunky_diagonals(path, pass_info, simulated_only, avoid)
- if(DIAGONAL_REMOVE_ALL)
- path = remove_diagonals(path, pass_info, simulated_only, avoid)
+ switch(diagonal_handling)
+ if(DIAGONAL_REMOVE_CLUNKY)
+ path = remove_clunky_diagonals(path, pass_info, simulated_only, avoid)
+ if(DIAGONAL_REMOVE_ALL)
+ path = remove_diagonals(path, pass_info, simulated_only, avoid)
- if(length(path) > 0 && skip_first)
- path.Cut(1,2)
+ if(length(path) > 0 && skip_first)
+ path.Cut(1,2)
hand_back(path)
return ..()
diff --git a/code/__HELPERS/paths/path.dm b/code/__HELPERS/paths/path.dm
index cd4b644a304d..cbc617313a84 100644
--- a/code/__HELPERS/paths/path.dm
+++ b/code/__HELPERS/paths/path.dm
@@ -21,23 +21,24 @@
* * diagonal_handling: defines how we handle diagonal moves. see __DEFINES/path.dm
*/
/proc/jps_path_to(atom/movable/caller, atom/end, max_distance = 30, mintargetdist, list/access, simulated_only = TRUE, turf/exclude, skip_first=TRUE, diagonal_handling=DIAGONAL_REMOVE_CLUNKY)
- var/list/path = list()
+ var/datum/pathfind_packet/packet = new
// We're guarenteed that list will be the first list in pathfinding_finished's argset because of how callback handles the arguments list
- var/datum/callback/await = CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(pathfinding_finished), path)
+ var/datum/callback/await = CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(pathfinding_finished), packet)
if(!SSpathfinder.pathfind(caller, end, max_distance, mintargetdist, access, simulated_only, exclude, skip_first, diagonal_handling, await))
return list()
- UNTIL(length(path))
- if(length(path) == 1 && path[1] == null || (QDELETED(caller) || QDELETED(end))) // It's trash, just hand back null to make it easy
- return list()
- return path
+ UNTIL(packet.path)
+ return packet.path
/// Uses funny pass by reference bullshit to take the path created by pathfinding, and insert it into a return list
/// We'll be able to use this return list to tell a sleeping proc to continue execution
-/proc/pathfinding_finished(list/return_list, list/path)
- // We use += here to ensure the list is still pointing at the same thing
- return_list += path
+/proc/pathfinding_finished(datum/pathfind_packet/return_packet, list/path)
+ return_packet.path = path || list()
+/// Wrapper around the path list since we play with refs.
+/datum/pathfind_packet
+ /// The unwound path, set when it's finished.
+ var/list/path
/datum/pathfind
/// The turf we started at
@@ -97,7 +98,7 @@
* Call to return a value to whoever spawned this pathfinding work
* Will fail if it's already been called
*/
-/datum/pathfind/proc/hand_back(value)
+/datum/pathfind/proc/hand_back(list/value)
set waitfor = FALSE
on_finish?.Invoke(value)
on_finish = null
diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm
index 2264b9aae738..03d8a5c1d9d5 100644
--- a/code/game/data_huds.dm
+++ b/code/game/data_huds.dm
@@ -509,7 +509,7 @@ Diagnostic HUDs!
new_state = "hudpatrol"
if(BOT_PREP_ARREST, BOT_ARREST, BOT_HUNT) //STOP RIGHT THERE, CRIMINAL SCUM!
new_state = "hudalert"
- if(BOT_MOVING, BOT_DELIVER, BOT_GO_HOME, BOT_NAV) //Moving to target for normal bots, moving to deliver or go home for MULES.
+ if(BOT_MOVING, BOT_PATHING, BOT_DELIVER, BOT_GO_HOME, BOT_NAV) //Moving to target for normal bots, moving to deliver or go home for MULES.
new_state = "hudmove"
else
new_state = ""
diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm
index d88d6f90e927..4680e643c499 100644
--- a/code/game/objects/items/devices/scanners/health_analyzer.dm
+++ b/code/game/objects/items/devices/scanners/health_analyzer.dm
@@ -64,12 +64,11 @@
playsound(user, 'sound/items/healthanalyzer.ogg', 50, 1)
- user.visible_message(span_notice("[user] analyzes [M]'s vitals."), \
- span_notice("You analyze [M]'s vitals."))
+ user.visible_message(span_notice("[user] analyzes [M] with [src]."))
switch (scanmode)
if (SCANMODE_HEALTH)
- healthscan(user, M, advanced, mode)
+ healthscan(user, M, advanced, mode, TRUE)
if (SCANMODE_CHEM)
chemscan(user, M)
if (SCANMODE_SURGERY)
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index 0aa78f864bc0..3f2dcf714570 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -2,6 +2,7 @@
fallback_colors = list(list(14, 18))
fallback_icon_state = "armor"
allowed = null
+ clothing_flags = parent_type::clothing_flags | THICKMATERIAL
body_parts_covered = CHEST
cold_protection = CHEST|GROIN
min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT
diff --git a/code/modules/do_after/do_after.dm b/code/modules/do_after/do_after.dm
index e956ec5733d3..77cf768edad0 100644
--- a/code/modules/do_after/do_after.dm
+++ b/code/modules/do_after/do_after.dm
@@ -12,7 +12,7 @@
* * max_interact_count: The action will automatically fail if they are already performing this many or more actions with the given interaction_key.
* * display: An atom or image to display over the user's head. Only works with DO_PUBLIC flag.
*/
-/proc/do_after(atom/movable/user, atom/target, time, timed_action_flags = NONE, progress = TRUE, datum/callback/extra_checks, interaction_key, max_interact_count = 1, image/display)
+/proc/do_after(atom/movable/user, atom/target, time = 0, timed_action_flags = NONE, progress = TRUE, datum/callback/extra_checks, interaction_key, max_interact_count = 1, image/display)
if(!user)
return FALSE
diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm
index 571c2bd0ac23..fd73bf2f0cd3 100644
--- a/code/modules/mob/living/damage_procs.dm
+++ b/code/modules/mob/living/damage_procs.dm
@@ -92,6 +92,25 @@
final_mod *= new_mod
return final_mod
+/**
+ * Simply a wrapper for calling mob adjustXLoss() procs to heal a certain damage type,
+ * when you don't know what damage type you're healing exactly.
+ */
+/mob/living/proc/heal_damage_type(heal_amount = 0, damagetype = BRUTE)
+ heal_amount = abs(heal_amount) * -1
+
+ switch(damagetype)
+ if(BRUTE)
+ return adjustBruteLoss(heal_amount)
+ if(BURN)
+ return adjustFireLoss(heal_amount)
+ if(TOX)
+ return adjustToxLoss(heal_amount)
+ if(OXY)
+ return adjustOxyLoss(heal_amount)
+ if(STAMINA)
+ stamina.adjust(-heal_amount)
+
///like [apply_damage][/mob/living/proc/apply_damage] except it always uses the damage procs
/mob/living/proc/apply_damage_type(damage = 0, damagetype = BRUTE)
switch(damagetype)
diff --git a/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm b/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm
index e7ec27ce0a48..040530547375 100644
--- a/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm
+++ b/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm
@@ -66,7 +66,7 @@
SSmove_manager.stop_looping(src)
look_for_perp() // see if any criminals are in range
if(!mode && bot_mode_flags & BOT_MODE_AUTOPATROL) // still idle, and set to patrol
- mode = BOT_START_PATROL // switch to patrol mode
+ set_mode(BOT_START_PATROL)
if(BOT_HUNT) // hunting for perp
update_appearance()
playsound(src,'sound/effects/beepskyspinsabre.ogg',100,TRUE,-1)
@@ -123,7 +123,7 @@
visible_message(span_warning("[src] ignites his energy swords!"))
icon_state = "grievous-c"
visible_message("[src] points at [C.name]!")
- mode = BOT_HUNT
+ set_mode(BOT_HUNT)
INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
break
else
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index d5b26bbe718a..a71da7e358d6 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -111,7 +111,59 @@
var/path_image_icon_state = "path_indicator"
var/path_image_color = "#FFFFFF"
var/reset_access_timer_id
- var/ignorelistcleanuptimer = 1 // This ticks up every automated action, at 300 we clean the ignore list
+
+ COOLDOWN_DECLARE(ignore_list_cleanup_cd)
+
+/mob/living/simple_animal/bot/Initialize(mapload)
+ . = ..()
+ GLOB.bots_list += src
+
+ path_hud = new /datum/atom_hud/data/bot_path()
+ for(var/hud in path_hud.hud_icons) // You get to see your own path
+ set_hud_image_active(hud, exclusive_hud = path_hud)
+
+ // Give bots a fancy new ID card that can hold any access.
+ access_card = new /obj/item/card/id/advanced/simple_bot(src)
+ // This access is so bots can be immediately set to patrol and leave Robotics, instead of having to be let out first.
+ access_card.set_access(list(ACCESS_ROBOTICS))
+ internal_radio = new /obj/item/radio(src)
+ if(radio_key)
+ internal_radio.keyslot = new radio_key
+ internal_radio.subspace_transmission = TRUE
+ internal_radio.canhear_range = -1 // anything greater will have the bot broadcast the channel as if it were saying it out loud.
+ internal_radio.recalculateChannels()
+
+ //Adds bot to the diagnostic HUD system
+ prepare_huds()
+ for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
+ diag_hud.add_atom_to_hud(src)
+ diag_hud_set_bothealth()
+ diag_hud_set_botstat()
+ diag_hud_set_botmode()
+
+ //If a bot has its own HUD (for player bots), provide it.
+ if(!isnull(data_hud_type))
+ var/datum/atom_hud/datahud = GLOB.huds[data_hud_type]
+ datahud.show_to(src)
+ if(path_hud)
+ path_hud.add_atom_to_hud(src)
+ path_hud.show_to(src)
+
+
+/mob/living/simple_animal/bot/Destroy()
+ if(path_hud)
+ QDEL_NULL(path_hud)
+ path_hud = null
+ GLOB.bots_list -= src
+ if(paicard)
+ ejectpai()
+ QDEL_NULL(internal_radio)
+ QDEL_NULL(access_card)
+ ignore_list = null
+ return ..()
+
+/mob/living/simple_animal/bot/proc/set_mode(new_mode)
+ mode = new_mode
/mob/living/simple_animal/bot/proc/get_mode()
if(client) //Player bots do not have modes, thus the override. Also an easy way for PDA users/AI to know when a bot is a player.
@@ -163,53 +215,6 @@
return TRUE
return FALSE
-/mob/living/simple_animal/bot/Initialize(mapload)
- . = ..()
- GLOB.bots_list += src
-
- path_hud = new /datum/atom_hud/data/bot_path()
- for(var/hud in path_hud.hud_icons) // You get to see your own path
- set_hud_image_active(hud, exclusive_hud = path_hud)
-
- // Give bots a fancy new ID card that can hold any access.
- access_card = new /obj/item/card/id/advanced/simple_bot(src)
- // This access is so bots can be immediately set to patrol and leave Robotics, instead of having to be let out first.
- access_card.set_access(list(ACCESS_ROBOTICS))
- internal_radio = new /obj/item/radio(src)
- if(radio_key)
- internal_radio.keyslot = new radio_key
- internal_radio.subspace_transmission = TRUE
- internal_radio.canhear_range = -1 // anything greater will have the bot broadcast the channel as if it were saying it out loud.
- internal_radio.recalculateChannels()
-
- //Adds bot to the diagnostic HUD system
- prepare_huds()
- for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
- diag_hud.add_atom_to_hud(src)
- diag_hud_set_bothealth()
- diag_hud_set_botstat()
- diag_hud_set_botmode()
-
- //If a bot has its own HUD (for player bots), provide it.
- if(!isnull(data_hud_type))
- var/datum/atom_hud/datahud = GLOB.huds[data_hud_type]
- datahud.show_to(src)
- if(path_hud)
- path_hud.add_atom_to_hud(src)
- path_hud.show_to(src)
-
-
-/mob/living/simple_animal/bot/Destroy()
- if(path_hud)
- QDEL_NULL(path_hud)
- path_hud = null
- GLOB.bots_list -= src
- if(paicard)
- ejectpai()
- QDEL_NULL(internal_radio)
- QDEL_NULL(access_card)
- return ..()
-
/mob/living/simple_animal/bot/proc/check_access(mob/living/user, obj/item/card/id)
if(user.has_unlimited_silicon_privilege || isAdminGhostAI(user)) // Silicon and Admins always have access.
return TRUE
@@ -301,14 +306,9 @@
/mob/living/simple_animal/bot/handle_automated_action() //Master process which handles code common across most bots.
diag_hud_set_botmode()
- if (ignorelistcleanuptimer % 300 == 0) // Every 300 actions, clean up the ignore list from old junk
- for(var/ref in ignore_list)
- var/atom/referredatom = locate(ref)
- if (!referredatom || !istype(referredatom) || QDELETED(referredatom))
- ignore_list -= ref
- ignorelistcleanuptimer = 1
- else
- ignorelistcleanuptimer++
+ if (COOLDOWN_FINISHED(src, ignore_list_cleanup_cd))
+ clear_ignore_list()
+ COOLDOWN_START(src, ignore_list_cleanup_cd, 20 SECONDS)
if(!(bot_mode_flags & BOT_MODE_ON) || client)
return FALSE
@@ -327,6 +327,8 @@
if(BOT_SUMMON) //Called to a location
summon_step()
return FALSE
+ if(BOT_PATHING)
+ return FALSE
return TRUE //Successful completion. Used to prevent child process() continuing if this one is ended early.
@@ -554,12 +556,26 @@ GLOBAL_LIST_EMPTY(scan_typecaches)
return TRUE
return FALSE
-/mob/living/simple_animal/bot/proc/add_to_ignore(subject)
- if(ignore_list.len < 50) //This will help keep track of them, so the bot is always trying to reach a blocked spot.
- ignore_list += REF(subject)
- else //If the list is full, insert newest, delete oldest.
- ignore_list.Cut(1,2)
- ignore_list += REF(subject)
+/// Add an atom to our list of ignored objects.
+/mob/living/simple_animal/bot/proc/add_to_ignore(atom/subject)
+ if(isnull(subject))
+ return
+
+ if(ignore_list.len < 50)
+ remove_ignored_atom(ignore_list[1])
+
+ ignore_list += subject
+ RegisterSignal(subject, COMSIG_PARENT_QDELETING, PROC_REF(remove_ignored_atom))
+
+/mob/living/simple_animal/bot/proc/remove_ignored_atom(datum/source)
+ SIGNAL_HANDLER
+
+ UnregisterSignal(source, COMSIG_PARENT_QDELETING)
+ ignore_list -= source
+
+/mob/living/simple_animal/bot/proc/clear_ignore_list()
+ for(var/atom/A as anything in ignore_list)
+ remove_ignored_atom(A)
/*
Movement proc for stepping a bot through a path generated through A-star.
@@ -626,7 +642,7 @@ Pass a positive integer as an argument to override a bot's default speed.
if(message)
to_chat(calling_ai, span_notice("[icon2html(src, calling_ai)] [name] called to [end_area]. [path.len-1] meters to destination."))
pathset = TRUE
- mode = BOT_RESPONDING
+ set_mode(BOT_RESPONDING)
tries = 0
else
if(message)
@@ -655,7 +671,8 @@ Pass a positive integer as an argument to override a bot's default speed.
pathset = FALSE
access_card.set_access(prev_access)
tries = 0
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
+ frustration = 0
ignore_list = list()
diag_hud_set_botstat()
diag_hud_set_botmode()
@@ -685,7 +702,7 @@ Pass a positive integer as an argument to override a bot's default speed.
return
if(!(bot_mode_flags & BOT_MODE_AUTOPATROL)) //A bot not set to patrol should not be patrolling.
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
return
if(patrol_target) // has patrol target
@@ -701,7 +718,7 @@ Pass a positive integer as an argument to override a bot's default speed.
if(!path.len)
patrol_target = null
return
- mode = BOT_PATROL
+ set_mode(BOT_PATROL)
// perform a single patrol step
/mob/living/simple_animal/bot/proc/patrol_step()
@@ -726,7 +743,7 @@ Pass a positive integer as an argument to override a bot's default speed.
addtimer(CALLBACK(src, PROC_REF(patrol_step_not_moved)), 2)
else // no path, so calculate new one
- mode = BOT_START_PATROL
+ set_mode(BOT_START_PATROL)
/mob/living/simple_animal/bot/proc/patrol_step_not_moved()
calc_path()
@@ -744,7 +761,7 @@ Pass a positive integer as an argument to override a bot's default speed.
destination = next_destination
else
bot_mode_flags &= ~BOT_MODE_AUTOPATROL
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
speak("Disengaging patrol mode.")
/mob/living/simple_animal/bot/proc/get_next_patrol_target()
@@ -793,7 +810,7 @@ Pass a positive integer as an argument to override a bot's default speed.
summon_target = get_turf(user)
if(user_access.len != 0)
access_card.set_access(user_access + prev_access) //Adds the user's access, if any.
- mode = BOT_SUMMON
+ set_mode(BOT_SUMMON)
speak("Responding.", radio_channel)
if("ejectpai")
diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm
index ccde75f075e3..63de613dca3e 100644
--- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm
@@ -246,28 +246,29 @@
else if(target)
if(QDELETED(target) || !isturf(target.loc))
target = null
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
return
if(get_dist(src, target) <= 1)
UnarmedAttack(target, proximity_flag = TRUE) //Rather than check at every step of the way, let's check before we do an action, so we can rescan before the other bot.
if(QDELETED(target)) //We done here.
target = null
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
return
- if(target && path.len == 0 && (get_dist(src,target) > 1) && mode != BOT_MOVING)
- mode = BOT_MOVING
+ if(target && path.len == 0 && (get_dist(src,target) > 1))
+ set_mode(BOT_PATHING)
path = jps_path_to(src, target, max_distance=30, mintargetdist=1, access = access_card?.GetAccess())
+ set_mode(BOT_MOVING)
if(length(path) == 0)
add_to_ignore(target)
target = null
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
if(path.len > 0 && target)
if(!bot_move(path[path.len]))
target = null
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
return
/mob/living/simple_animal/bot/cleanbot/proc/get_targets()
@@ -325,14 +326,14 @@
return
. = ..()
if(ismopable(attack_target) || istype(attack_target, /obj/effect/decal/cleanable/blood))
- mode = BOT_CLEANING
+ set_mode(BOT_CLEANING)
update_icon_state()
var/turf/T = get_turf(attack_target)
if(do_after(src, T, 1 SECOND))
T.wash(CLEAN_SCRUB)
visible_message(span_notice("[src] cleans [T]."))
target = null
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
update_icon_state()
else if(isitem(attack_target) || istype(attack_target, /obj/effect/decal))
diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
index 5facdc1ac168..1d506bb7567c 100644
--- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
@@ -109,7 +109,7 @@
var/mob/to_arrest = pick(targets)
if(to_arrest)
target = to_arrest
- mode = BOT_HUNT
+ set_mode(BOT_HUNT)
/mob/living/simple_animal/bot/secbot/ed209/RangedAttack(atom/A)
if(!(bot_mode_flags & BOT_MODE_ON))
diff --git a/code/modules/mob/living/simple_animal/bot/firebot.dm b/code/modules/mob/living/simple_animal/bot/firebot.dm
index 9ccba3009cd2..24873fb26153 100644
--- a/code/modules/mob/living/simple_animal/bot/firebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/firebot.dm
@@ -99,7 +99,7 @@
/mob/living/simple_animal/bot/firebot/proc/soft_reset()
path = list()
target_fire = null
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
last_found = world.time
update_appearance()
@@ -168,7 +168,7 @@
if(IsStun() || IsParalyzed())
old_target_fire = target_fire
target_fire = null
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
return
if(prob(1) && target_fire == null)
@@ -219,16 +219,17 @@
// Target ran away
else if(target_fire && path.len && (get_dist(target_fire,path[path.len]) > 2))
path = list()
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
last_found = world.time
else if(target_fire && stationary_mode)
soft_reset()
return
- if(target_fire && (get_dist(src, target_fire) > 2) && mode != BOT_MOVING)
- mode = BOT_MOVING
+ if(target_fire && (get_dist(src, target_fire) > 2))
+ set_mode(BOT_PATHING)
path = jps_path_to(src, target_fire, max_distance=30, mintargetdist=1, access = access_card?.GetAccess())
+ set_mode(BOT_MOVING)
if(!path.len)
soft_reset()
diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm
index 35faef3b15e3..9808a5fe8ed8 100644
--- a/code/modules/mob/living/simple_animal/bot/floorbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm
@@ -236,7 +236,7 @@
else if(bot_cover_flags & BOT_COVER_EMAGGED && isfloorturf(target))
var/turf/open/floor/F = target
toggle_magnet()
- mode = BOT_REPAIRING
+ set_mode(BOT_REPAIRING)
if(isplatingturf(F))
F.TryScrapeToLattice()
else
@@ -255,16 +255,16 @@
if(!bot_move(target))
add_to_ignore(target)
target = null
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
return
else if( !bot_move(target) )
target = null
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
return
/mob/living/simple_animal/bot/floorbot/proc/go_idle()
toggle_magnet(FALSE)
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
target = null
/mob/living/simple_animal/bot/floorbot/proc/is_hull_breach(turf/t) //Ignore space tiles not considered part of a structure, also ignores shuttle docking areas.
@@ -324,7 +324,7 @@
if(isspaceturf(target_turf)) //If we are fixing an area not part of pure space, it is
toggle_magnet()
visible_message(span_notice("[targetdirection ? "[src] begins installing a bridge plating." : "[src] begins to repair the hole."] "))
- mode = BOT_REPAIRING
+ set_mode(BOT_REPAIRING)
if(do_after(src, target_turf, 50) && mode == BOT_REPAIRING)
if(autotile) //Build the floor and include a tile.
if(replacetiles && tilestack)
@@ -345,14 +345,14 @@
if(F.broken || F.burnt || isplatingturf(F))
toggle_magnet()
- mode = BOT_REPAIRING
+ set_mode(BOT_REPAIRING)
visible_message(span_notice("[src] begins [(F.broken || F.burnt) ? "repairing the floor" : "placing a floor tile"]."))
if(do_after(src, F, 50) && mode == BOT_REPAIRING)
success = TRUE
else if(replacetiles && tilestack && F.type != tilestack.turf_type)
toggle_magnet()
- mode = BOT_REPAIRING
+ set_mode(BOT_REPAIRING)
visible_message(span_notice("[src] begins replacing the floor tiles."))
if(do_after(src, target_turf, 50) && mode == BOT_REPAIRING && tilestack)
success = TRUE
diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm
index 4fe46c3e2adb..799653b0f2a3 100644
--- a/code/modules/mob/living/simple_animal/bot/honkbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm
@@ -93,7 +93,7 @@
)
target_lastloc = target.loc
- mode = BOT_PREP_ARREST
+ set_mode(BOT_PREP_ARREST)
/mob/living/simple_animal/bot/secbot/honkbot/retaliate(mob/living/carbon/human/attacking_human)
. = ..()
diff --git a/code/modules/mob/living/simple_animal/bot/hygienebot.dm b/code/modules/mob/living/simple_animal/bot/hygienebot.dm
index 9038c49ffc5c..7ccd256818a3 100644
--- a/code/modules/mob/living/simple_animal/bot/hygienebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/hygienebot.dm
@@ -80,7 +80,7 @@
/mob/living/simple_animal/bot/hygienebot/turn_off()
..()
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
/mob/living/simple_animal/bot/hygienebot/bot_reset()
..()
@@ -108,7 +108,7 @@
SSmove_manager.stop_looping(src)
look_for_lowhygiene() // see if any disgusting fucks are in range
if(!mode && bot_mode_flags & BOT_MODE_AUTOPATROL) // still idle, and set to patrol
- mode = BOT_START_PATROL // switch to patrol mode
+ set_mode(BOT_START_PATROL)
if(BOT_HUNT) // hunting for stinkman
if(bot_cover_flags & BOT_COVER_EMAGGED) //lol fuck em up
@@ -130,7 +130,7 @@
speak("Well about fucking time you degenerate.", "Fucking finally.", "Thank god, you finally stopped.")
playsound(loc, 'sound/effects/hygienebot_angry.ogg', 60, 1)
mad = FALSE
- mode = BOT_SHOWERSTANCE
+ set_mode(BOT_SHOWERSTANCE)
else
stop_washing()
var/olddist = get_dist(src, target)
@@ -168,7 +168,7 @@
bot_patrol()
/mob/living/simple_animal/bot/hygienebot/proc/back_to_idle()
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
SSmove_manager.stop_looping(src)
target = null
frustration = 0
@@ -178,7 +178,7 @@
/mob/living/simple_animal/bot/hygienebot/proc/back_to_hunt()
frustration = 0
- mode = BOT_HUNT
+ set_mode(BOT_HUNT)
stop_washing()
INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
@@ -192,7 +192,7 @@
speak("Unhygienic client found. Please stand still so I can clean you.")
playsound(loc, 'sound/effects/hygienebot_happy.ogg', 60, 1)
visible_message("[src] points at [H.name]!")
- mode = BOT_HUNT
+ set_mode(BOT_HUNT)
INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
break
else
diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm
index eec739383d35..2a404a4ad542 100644
--- a/code/modules/mob/living/simple_animal/bot/medbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/medbot.dm
@@ -34,6 +34,62 @@
hackables = "health processor circuits"
path_image_color = "#DDDDFF"
+ var/list/idle_phrases = list(
+ MEDIBOT_VOICED_MASK_ON = 'sound/voice/medbot/radar.ogg',
+ MEDIBOT_VOICED_ALWAYS_A_CATCH = 'sound/voice/medbot/catch.ogg',
+ MEDIBOT_VOICED_PLASTIC_SURGEON = 'sound/voice/medbot/surgeon.ogg',
+ MEDIBOT_VOICED_LIKE_FLIES = 'sound/voice/medbot/flies.ogg',
+ MEDIBOT_VOICED_DELICIOUS = 'sound/voice/medbot/delicious.ogg',
+ MEDIBOT_VOICED_SUFFER = 'sound/voice/medbot/why.ogg'
+ )
+
+ var/list/finish_healing_phrases = list(
+ MEDIBOT_VOICED_ALL_PATCHED_UP = 'sound/voice/medbot/patchedup.ogg',
+ MEDIBOT_VOICED_APPLE_A_DAY = 'sound/voice/medbot/apple.ogg',
+ MEDIBOT_VOICED_FEEL_BETTER = 'sound/voice/medbot/feelbetter.ogg',
+ )
+
+ var/list/located_patient_phrases = list(
+ MEDIBOT_VOICED_HOLD_ON = 'sound/voice/medbot/coming.ogg',
+ MEDIBOT_VOICED_WANT_TO_HELP = 'sound/voice/medbot/help.ogg',
+ MEDIBOT_VOICED_YOU_ARE_INJURED = 'sound/voice/medbot/injured.ogg'
+ )
+
+ var/list/patient_died_phrases = list(
+ MEDIBOT_VOICED_STAY_WITH_ME = 'sound/voice/medbot/no.ogg',
+ MEDIBOT_VOICED_LIVE = 'sound/voice/medbot/live.ogg',
+ MEDIBOT_VOICED_NEVER_LOST = 'sound/voice/medbot/lost.ogg'
+ )
+
+ var/list/pre_tip_phrases = list(
+ MEDIBOT_VOICED_WAIT = 'sound/voice/medbot/hey_wait.ogg',
+ MEDIBOT_VOICED_DONT = 'sound/voice/medbot/please_dont.ogg',
+ MEDIBOT_VOICED_TRUSTED_YOU = 'sound/voice/medbot/i_trusted_you.ogg',
+ MEDIBOT_VOICED_NO_SAD = 'sound/voice/medbot/nooo.ogg',
+ MEDIBOT_VOICED_OH_FUCK = 'sound/voice/medbot/oh_fuck.ogg',
+ )
+
+ var/list/untip_phrases = list(
+ MEDIBOT_VOICED_FORGIVE = 'sound/voice/medbot/forgive.ogg',
+ MEDIBOT_VOICED_THANKS = 'sound/voice/medbot/thank_you.ogg',
+ MEDIBOT_VOICED_GOOD_PERSON = 'sound/voice/medbot/youre_good.ogg',
+ MEDIBOT_VOICED_FUCK_YOU = 'sound/voice/medbot/fuck_you.ogg',
+ MEDIBOT_VOICED_BEHAVIOUR_REPORTED = 'sound/voice/medbot/reported.ogg'
+ )
+
+ var/list/panic_phrases = list(
+ MEDIBOT_VOICED_ASSISTANCE = 'sound/voice/medbot/i_require_asst.ogg',
+ MEDIBOT_VOICED_PUT_BACK = 'sound/voice/medbot/please_put_me_back.ogg',
+ MEDIBOT_VOICED_IM_SCARED = 'sound/voice/medbot/please_im_scared.ogg',
+ MEDIBOT_VOICED_NEED_HELP = 'sound/voice/medbot/dont_like.ogg',
+ MEDIBOT_VOICED_THIS_HURTS = 'sound/voice/medbot/pain_is_real.ogg',
+ MEDIBOT_VOICED_THE_END = 'sound/voice/medbot/is_this_the_end.ogg',
+ MEDIBOT_VOICED_NOOO = 'sound/voice/medbot/nooo.ogg'
+ )
+
+ /// Compiled list of all the phrase lists.
+ var/list/all_phrases
+
/// drop determining variable
var/healthanalyzer = /obj/item/healthanalyzer
/// drop determining variable
@@ -41,15 +97,32 @@
///based off medkit_X skins in aibots.dmi for your selection; X goes here IE medskin_tox means skin var should be "tox"
var/skin
var/mob/living/carbon/patient
- var/mob/living/carbon/oldpatient
+ /// Weakref to the last patient. The last patient is ignored for a bit when finding a new target.
+ var/datum/weakref/previous_patient
+ /// Timestamp of the last time we found a patient.
var/last_found = 0
- /// How much healing do we do at a time?
- var/heal_amount = 2.5
+ /// Bots must wait this long before considering the previous patient a valid target.
+ var/repeat_patient_cooldown = 20 SECONDS
+
/// Start healing when they have this much damage in a category
var/heal_threshold = 10
- /// What damage type does this bot support. Because the default is brute, if the medkit is brute-oriented there is a slight bonus to healing. set to "all" for it to heal any of the 4 base damage types
+ /// How long it takes to inject.
+ var/heal_time = 2 SECONDS
+ /// How many units of reagent to inject.
+ var/injection_amount = 15
+
+ /// What damage type does this bot support. Set to "all" for it to heal any of the 4 base damage types
var/damagetype_healer = BRUTE
+ /// Reagents to use for each healing type.
+ var/list/healing_reagents = list(
+ BRUTE = /datum/reagent/medicine/tricordrazine,
+ BURN = /datum/reagent/medicine/tricordrazine,
+ TOX = /datum/reagent/medicine/tricordrazine,
+ OXY = /datum/reagent/medicine/tricordrazine,
+ "emag" = /datum/reagent/toxin/chloralhydrate,
+ )
+
///Flags Medbots use to decide how they should be acting.
var/medical_mode_flags = MEDBOT_DECLARE_CRIT | MEDBOT_SPEAK_MODE
// Selections: MEDBOT_DECLARE_CRIT | MEDBOT_STATIONARY_MODE | MEDBOT_SPEAK_MODE
@@ -79,7 +152,8 @@
desc = "International Medibot of mystery."
skin = "bezerk"
damagetype_healer = "all"
- heal_amount = 10
+ injection_amount = 20
+ heal_time = 1 SECOND
/mob/living/simple_animal/bot/medbot/derelict
name = "\improper Old Medibot"
@@ -88,7 +162,8 @@
damagetype_healer = "all"
medical_mode_flags = MEDBOT_SPEAK_MODE
heal_threshold = 0
- heal_amount = 5
+ injection_amount = 5
+ heal_time = 5 SECONDS
/mob/living/simple_animal/bot/medbot/examine(mob/user)
. = ..()
@@ -136,6 +211,8 @@
skin = new_skin
update_appearance()
+ all_phrases = idle_phrases + located_patient_phrases + finish_healing_phrases + patient_died_phrases + pre_tip_phrases + untip_phrases + panic_phrases
+
AddComponent(/datum/component/tippable, \
tip_time = 3 SECONDS, \
untip_time = 3 SECONDS, \
@@ -147,15 +224,18 @@
/mob/living/simple_animal/bot/medbot/bot_reset()
..()
patient = null
- oldpatient = null
+ previous_patient = null
last_found = world.time
+ tending = FALSE
update_appearance()
/mob/living/simple_animal/bot/medbot/proc/soft_reset() //Allows the medibot to still actively perform its medical duties without being completely halted as a hard reset does.
path = list()
patient = null
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
last_found = world.time
+ tending = FALSE
+ frustration = 0
update_appearance()
/mob/living/simple_animal/bot/medbot/attack_paw(mob/user, list/modifiers)
@@ -212,23 +292,20 @@
z_flick("medibot_spark", src)
playsound(src, SFX_SPARKS, 75, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
if(user)
- oldpatient = user
+ previous_patient = WEAKREF(user)
/mob/living/simple_animal/bot/medbot/process_scan(mob/living/carbon/human/H)
if(H.stat == DEAD)
return null
- if((H == oldpatient) && (world.time < last_found + 200))
+ if(IS_WEAKREF_OF(H, previous_patient) && (world.time < last_found + repeat_patient_cooldown))
return null
- if(!assess_patient(H))
+ if(!is_viable_patient(H))
return null
last_found = world.time
if(COOLDOWN_FINISHED(src, last_newpatient_speak))
COOLDOWN_START(src, last_newpatient_speak, MEDBOT_NEW_PATIENTSPEAK_DELAY)
- 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)
- speak(message)
- playsound(src, messagevoice[message], 50, FALSE)
+ medbot_phrase(pick(located_patient_phrases), H)
return H
/*
@@ -241,16 +318,7 @@
return
COOLDOWN_START(src, last_tipping_action_voice, MEDBOT_FREAKOUT_DELAY) // message for tipping happens when we start interacting, message for righting comes after finishing
- var/static/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)
+ medbot_phrase(pick(pre_tip_phrases), user)
/*
* Proc used in a callback for after this medibot is tipped by the tippable component.
@@ -258,7 +326,7 @@
* user - the mob who tipped us over
*/
/mob/living/simple_animal/bot/medbot/proc/after_tip_over(mob/user)
- mode = BOT_TIPPED
+ set_mode(BOT_TIPPED)
tipper_name = user.name
playsound(src, 'sound/machines/warning-buzzer.ogg', 50)
@@ -268,50 +336,49 @@
* user - the mob who righted us. Can be null.
*/
/mob/living/simple_animal/bot/medbot/proc/after_righted(mob/user)
- var/list/messagevoice
+ var/phrase
if(user)
if(user.name == tipper_name)
- messagevoice = list("I forgive you." = 'sound/voice/medbot/forgive.ogg')
+ phrase = MEDIBOT_VOICED_FORGIVE
else
- messagevoice = list("Thank you!" = 'sound/voice/medbot/thank_you.ogg', "You are a good person." = 'sound/voice/medbot/youre_good.ogg')
+ phrase = pick(MEDIBOT_VOICED_THANKS, MEDIBOT_VOICED_GOOD_PERSON)
else
- messagevoice = list("Fuck you." = 'sound/voice/medbot/fuck_you.ogg', "Your behavior has been reported, have a nice day." = 'sound/voice/medbot/reported.ogg')
+ phrase = pick(MEDIBOT_VOICED_FUCK_YOU, MEDIBOT_VOICED_BEHAVIOUR_REPORTED)
+
tipper_name = null
if(COOLDOWN_FINISHED(src, last_tipping_action_voice))
COOLDOWN_START(src, last_tipping_action_voice, MEDBOT_FREAKOUT_DELAY)
- var/message = pick(messagevoice)
- speak(message)
- playsound(src, messagevoice[message], 70)
+ medbot_phrase(phrase, user)
+
tipped_status = MEDBOT_PANIC_NONE
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
/// if someone tipped us over, check whether we should ask for help or just right ourselves eventually
/mob/living/simple_animal/bot/medbot/proc/handle_panic()
tipped_status++
- var/list/messagevoice
+ var/phrase
switch(tipped_status)
if(MEDBOT_PANIC_LOW)
- messagevoice = list("I require assistance." = 'sound/voice/medbot/i_require_asst.ogg')
+ phrase = MEDIBOT_VOICED_ASSISTANCE
if(MEDBOT_PANIC_MED)
- messagevoice = list("Please put me back." = 'sound/voice/medbot/please_put_me_back.ogg')
+ phrase = MEDIBOT_VOICED_PUT_BACK
if(MEDBOT_PANIC_HIGH)
- messagevoice = list("Please, I am scared!" = 'sound/voice/medbot/please_im_scared.ogg')
+ phrase = MEDIBOT_VOICED_IM_SCARED
if(MEDBOT_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')
+ phrase = pick(MEDIBOT_VOICED_NEED_HELP, MEDIBOT_VOICED_THIS_HURTS)
if(MEDBOT_PANIC_ENDING)
- messagevoice = list("Is this the end?" = 'sound/voice/medbot/is_this_the_end.ogg', "Nooo!" = 'sound/voice/medbot/nooo.ogg')
+ phrase = pick(MEDIBOT_VOICED_NOOO, MEDIBOT_VOICED_THE_END)
if(MEDBOT_PANIC_END)
speak("PSYCH ALERT: Crewmember [tipper_name] recorded displaying antisocial tendencies torturing bots in [get_area(src)]. Please schedule psych evaluation.", radio_channel)
if(prob(tipped_status))
do_jitter_animation(tipped_status * 0.1)
- if(messagevoice)
- var/message = pick(messagevoice)
- speak(message)
- playsound(src, messagevoice[message], 70)
+ if(phrase)
+ medbot_phrase(phrase)
+
else if(prob(tipped_status * 0.2))
playsound(src, 'sound/machines/warning-buzzer.ogg', 30, extrarange=-2)
@@ -328,14 +395,15 @@
return
if(IsStun() || IsParalyzed())
- oldpatient = patient
+ previous_patient = WEAKREF(patient)
patient = null
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
return
- if(frustration > 8)
- oldpatient = patient
+ if(frustration > 5)
+ previous_patient = WEAKREF(patient)
soft_reset()
+ medbot_phrase(MEDIBOT_VOICED_FUCK_YOU)
if(QDELETED(patient))
if(medical_mode_flags & MEDBOT_SPEAK_MODE && prob(1))
@@ -349,48 +417,44 @@
)
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)
+ medbot_phrase(pick(idle_phrases))
+
var/scan_range = (medical_mode_flags & MEDBOT_STATIONARY_MODE ? 1 : DEFAULT_SCAN_RANGE) //If in stationary mode, scan range is limited to adjacent patients.
- patient = scan(list(/mob/living/carbon/human), oldpatient, scan_range)
- oldpatient = patient
-
- if(patient && (get_dist(src,patient) <= 1) && !tending) //Patient is next to us, begin treatment!
- if(mode != BOT_HEALING)
- mode = BOT_HEALING
- update_appearance()
- frustration = 0
- medicate_patient(patient)
- return
+ patient = scan(list(/mob/living/carbon/human), scan_range = scan_range)
+ //previous_patient = WEAKREF(patient)
+
+ if(patient)
+ if((get_dist(src,patient) <= 1)) //Patient is next to us, begin treatment!
+ if(mode != BOT_HEALING)
+ update_appearance()
+ frustration = 0
+ try_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(medical_mode_flags & MEDBOT_STATIONARY_MODE) //Since we cannot move in this mode, ignore the patient and wait for another.
+ soft_reset()
+ return
- else if(medical_mode_flags & MEDBOT_STATIONARY_MODE && patient) //Since we cannot move in this mode, ignore the patient and wait for another.
- soft_reset()
- return
+ //Patient has moved away from us!
+ else if(!path.len || (path.len && (get_dist(patient,path[path.len]) > 2)))
+ path = list()
+ set_mode(BOT_IDLE)
+ last_found = world.time
- if(patient && path.len == 0 && (get_dist(src,patient) > 1) && mode != BOT_MOVING)
- mode = BOT_MOVING
- path = jps_path_to(src, patient, max_distance=30, access = access_card?.GetAccess())
- if(!path.len) //try to get closer if you can't reach the patient directly
+ if(path.len == 0 && (get_dist(src,patient) > 1))
+ set_mode(BOT_PATHING)
path = jps_path_to(src, patient, max_distance=30, mintargetdist=1, access = access_card?.GetAccess())
+ set_mode(BOT_MOVING)
if(!path.len) //Do not chase a patient we cannot reach.
+ add_to_ignore(patient)
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(path.len > 0)
+ frustration++
+ if(!bot_move(path[path.len]))
+ previous_patient = WEAKREF(patient)
+ soft_reset()
+ return
if(bot_mode_flags & BOT_MODE_AUTOPATROL && !(medical_mode_flags & MEDBOT_STATIONARY_MODE) && !patient)
switch(mode)
@@ -399,66 +463,60 @@
if(BOT_PATROL)
bot_patrol()
-/mob/living/simple_animal/bot/medbot/proc/assess_patient(mob/living/carbon/C)
- . = FALSE
+/// Returns a reagent to inject, if we can treat the mob.
+/mob/living/simple_animal/bot/medbot/proc/is_viable_patient(mob/living/carbon/C, declare_crit = TRUE)
//Time to see if they need medical help!
- if(medical_mode_flags & MEDBOT_STATIONARY_MODE && !Adjacent(C)) //YOU come to ME, BRO
- return FALSE
+ if((medical_mode_flags & MEDBOT_STATIONARY_MODE) && !Adjacent(C)) //YOU come to ME, BRO
+ return
if(C.stat == DEAD || (HAS_TRAIT(C, TRAIT_FAKEDEATH)))
- return FALSE //welp too late for them!
+ return //welp too late for them!
if(!(loc == C.loc) && !(isturf(C.loc) && isturf(loc)))
- return FALSE
+ return
if(C.suiciding)
- return FALSE //Kevorkian school of robotic medical assistants.
+ return //Kevorkian school of robotic medical assistants.
if(bot_cover_flags & BOT_COVER_EMAGGED) //Everyone needs our medicine. (Our medicine is toxins)
- return TRUE
+ return
if(HAS_TRAIT(C, TRAIT_MEDIBOTCOMINGTHROUGH) && !HAS_TRAIT_FROM(C, TRAIT_MEDIBOTCOMINGTHROUGH, tag)) //the early medbot gets the worm (or in this case the patient)
- return FALSE
-
- 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.
+ return
- if(medical_mode_flags & MEDBOT_DECLARE_CRIT && C.health <= 0) //Critical condition! Call for help!
+ //Critical condition! Call for help!
+ if(declare_crit && (medical_mode_flags & MEDBOT_DECLARE_CRIT) && C.undergoing_cardiac_arrest())
declare(C)
+ if(!C.try_inject(src, zone_selected || BODY_ZONE_CHEST))
+ return
+
+ if(bot_cover_flags & BOT_COVER_EMAGGED)
+ return healing_reagents["emag"]
+
//They're injured enough for it!
- var/list/treat_me_for = list()
- if(C.getBruteLoss() > heal_threshold)
- treat_me_for += BRUTE
+ var/heals_all = damagetype_healer == "all"
- if(C.getOxyLoss() > (5 + heal_threshold))
- treat_me_for += OXY
+ if((heals_all || (damagetype_healer == BRUTE)) && C.getBruteLoss() > heal_threshold && !C.bloodstream.has_reagent(healing_reagents[BRUTE]))
+ return healing_reagents[BRUTE]
- if(C.getFireLoss() > heal_threshold)
- treat_me_for += BURN
+ if((heals_all || (damagetype_healer == BURN)) && C.getFireLoss() > heal_threshold && !C.bloodstream.has_reagent(healing_reagents[BURN]))
+ return healing_reagents[BURN]
- if(C.getToxLoss() > heal_threshold)
- treat_me_for += TOX
+ if((heals_all || (damagetype_healer == TOX)) && C.getToxLoss() > heal_threshold && !C.bloodstream.has_reagent(healing_reagents[TOX]))
+ return healing_reagents[TOX]
- if(damagetype_healer in treat_me_for)
- return TRUE
- if(damagetype_healer == "all" && treat_me_for.len)
- return TRUE
+ if((heals_all || (damagetype_healer == OXY)) && C.getOxyLoss() > (5 + heal_threshold) && !C.bloodstream.has_reagent(healing_reagents[OXY]))
+ return healing_reagents[OXY]
/mob/living/simple_animal/bot/medbot/UnarmedAttack(atom/A, proximity_flag, list/modifiers)
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
return
- if(iscarbon(A) && !tending)
+ if(iscarbon(A) && mode != BOT_HEALING)
var/mob/living/carbon/C = A
patient = C
- mode = BOT_HEALING
update_appearance()
- medicate_patient(C)
+ try_medicate_patient(C)
update_appearance()
return
..()
@@ -468,91 +526,70 @@
if(!is_blind())
chemscan(src, A)
-/mob/living/simple_animal/bot/medbot/proc/medicate_patient(mob/living/carbon/C)
+/// Attempt to inject a patient with the goods.
+/mob/living/simple_animal/bot/medbot/proc/try_medicate_patient(mob/living/carbon/C)
if(!(bot_mode_flags & BOT_MODE_ON))
return
if(!istype(C))
- oldpatient = patient
+ previous_patient = WEAKREF(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(src, messagevoice[message], 50)
- oldpatient = patient
+ medbot_phrase(pick(patient_died_phrases), C)
+ previous_patient = WEAKREF(patient)
soft_reset()
return
- tending = TRUE
- while(tending)
- var/treatment_method
- var/list/potential_methods = list()
-
- if(C.getBruteLoss() > heal_threshold)
- potential_methods += BRUTE
-
- if(C.getFireLoss() > heal_threshold)
- potential_methods += BURN
+ if(!is_viable_patient(C, declare_crit = FALSE))
+ previous_patient = WEAKREF(patient)
+ soft_reset()
+ return
- if(C.getOxyLoss() > (5 + heal_threshold))
- potential_methods += OXY
+ set_mode(BOT_HEALING)
- if(C.getToxLoss() > heal_threshold)
- potential_methods += TOX
+ visible_message(span_warning("[src] is trying to inject [C]."))
- for(var/i in potential_methods)
- if(i != damagetype_healer)
- continue
- treatment_method = i
+ var/datum/callback/medicate_check = CALLBACK(src, PROC_REF(medicate_callback), C)
+ while(TRUE)
+ if(!patient)
+ break
- if(damagetype_healer == "all" && potential_methods.len)
- treatment_method = pick(potential_methods)
+ var/reagent_type = is_viable_patient(C, declare_crit = FALSE)
- if(!treatment_method && !(bot_cover_flags & BOT_COVER_EMAGGED)) //If they don't need any of that they're probably cured!
+ if(!reagent_type) //If they don't need any of that they're probably cured!
if(C.maxHealth - C.get_organic_health() < heal_threshold)
to_chat(src, span_notice("[C] is healthy! Your programming prevents you from tending the wounds of anyone without at least [heal_threshold] damage of any one type ([heal_threshold + 5] 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(src, messagevoice[message], 50)
+ visible_message(span_notice("[src] retracts it's syringe arm."))
+ medbot_phrase(pick(finish_healing_phrases), C)
bot_reset()
- tending = FALSE
- else if(patient)
- C.visible_message(span_danger("[src] is trying to tend the wounds of [patient]!"), \
- span_userdanger("[src] is trying to tend your wounds!"))
-
- if(do_after(src, patient, 20)) //Slightly faster than default tend wounds, but does less HPS
- if((get_dist(src, patient) <= 1) && (bot_mode_flags & BOT_MODE_ON) && assess_patient(patient))
- var/healies = heal_amount
- var/obj/item/storage/medkit/medkit = medkit_type
- if(treatment_method == BRUTE && initial(medkit.damagetype_healed) == BRUTE) //specialized brute gets a bit of bonus, as a snack.
- healies *= 1.1
- if(bot_cover_flags & BOT_COVER_EMAGGED)
- patient.reagents.add_reagent(/datum/reagent/toxin/chloralhydrate, 5)
- patient.apply_damage((healies * 1), treatment_method, spread_damage = TRUE)
- log_combat(src, patient, "pretended to tend wounds on", "internal tools", "([uppertext(treatment_method)]) (EMAGGED)")
- else
- patient.apply_damage((healies * -1), treatment_method) //don't need to check treatment_method since we know by this point that they were actually damaged.
- log_combat(src, patient, "tended the wounds of", "internal tools", "([uppertext(treatment_method)])")
- C.visible_message(span_notice("[src] tends the wounds of [patient]!"), \
- "[span_green("[src] tends your wounds!")]")
- ADD_TRAIT(patient,TRAIT_MEDIBOTCOMINGTHROUGH,tag)
- addtimer(TRAIT_CALLBACK_REMOVE(patient, TRAIT_MEDIBOTCOMINGTHROUGH, tag), (30 SECONDS))
- else
- tending = FALSE
- else
- tending = FALSE
+ return
- update_appearance()
- if(!tending)
- visible_message("[src] places its tools back into itself.")
- soft_reset()
- else
- tending = FALSE
+ ADD_TRAIT(patient,TRAIT_MEDIBOTCOMINGTHROUGH, tag)
+ addtimer(TRAIT_CALLBACK_REMOVE(patient, TRAIT_MEDIBOTCOMINGTHROUGH, tag), (30 SECONDS), TIMER_UNIQUE|TIMER_OVERRIDE)
+
+ visible_message(span_warning("[src] is trying to inject [patient]."))
+
+ if(!do_after(src, patient, heal_time, DO_PUBLIC, extra_checks = medicate_check, display = image('icons/obj/syringe.dmi', "syringe_0"))) //Slightly faster than default tend wounds, but does less HPS
+ break
+
+ var/datum/reagent/R = SSreagents.chemical_reagents_list[reagent_type]
+ R.expose_mob(patient, injection_amount, methods = INJECT)
+ patient.bloodstream.add_reagent(R.type, injection_amount)
+
+ log_combat(src, patient, "injected", "internal tools", "([injection_amount]u [reagent_type][bot_cover_flags & BOT_COVER_EMAGGED ? " (EMAGGED)" : ""])")
+
+ visible_message(span_notice("[src] injects [patient]."))
+
+ visible_message(span_notice("[src] retracts it's syringe arm."))
+ soft_reset()
+
+/mob/living/simple_animal/bot/medbot/proc/medicate_callback(mob/living/carbon/target)
+ if((get_dist(src, patient) > 1) || !(bot_mode_flags & BOT_MODE_ON) || !is_viable_patient(patient, declare_crit = FALSE))
+ return FALSE
+ return TRUE
/mob/living/simple_animal/bot/medbot/explode()
var/atom/Tsec = drop_location()
@@ -569,8 +606,17 @@
if(!COOLDOWN_FINISHED(src, last_patient_message))
return
COOLDOWN_START(src, last_patient_message, MEDBOT_PATIENTSPEAK_DELAY)
- var/area/location = get_area(src)
- speak("Medical emergency! [crit_patient || "A patient"] is in critical condition at [location]!", radio_channel)
+
+ var/area/location = get_area(crit_patient)
+ speak("Medical emergency! [crit_patient] is in critical condition at [location]!", radio_channel)
+
+/mob/living/simple_animal/bot/medbot/proc/medbot_phrase(phrase, mob/target)
+ var/sound_path = all_phrases[phrase]
+ if(target)
+ phrase = replacetext(phrase, "%TARGET%", "[target]")
+
+ speak(phrase)
+ playsound(src, sound_path, 75, FALSE)
#undef MEDBOT_NEW_PATIENTSPEAK_DELAY
#undef MEDBOT_PATIENTSPEAK_DELAY
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index 5e3412601f47..dec7c07d6327 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -249,7 +249,7 @@
switch(mode)
if(BOT_IDLE, BOT_DELIVER, BOT_GO_HOME)
data["modeStatus"] = "good"
- if(BOT_BLOCKED, BOT_NAV, BOT_WAIT_FOR_NAV)
+ if(BOT_BLOCKED, BOT_NAV, BOT_WAIT_FOR_NAV, BOT_PATHING)
data["modeStatus"] = "average"
if(BOT_NO_ROUTE)
data["modeStatus"] = "bad"
@@ -409,7 +409,7 @@
return
load = AM
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
update_appearance()
///resolves the name to display for the loaded mob. primarily needed for the paranormal subtype since we don't want to show the name of ghosts riding it.
@@ -438,7 +438,7 @@
update_appearance()
return
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
var/atom/movable/cached_load = load //cache the load since unbuckling mobs clears the var.
@@ -529,55 +529,55 @@
blockcount = 0
path -= loc
if(destination == home_destination)
- mode = BOT_GO_HOME
+ set_mode(BOT_GO_HOME)
else
- mode = BOT_DELIVER
+ set_mode(BOT_DELIVER)
else // failed to move
blockcount++
- mode = BOT_BLOCKED
+ set_mode(BOT_BLOCKED)
if(blockcount == 3)
buzz(ANNOYED)
if(blockcount > 10) // attempt 10 times before recomputing
// find new path excluding blocked turf
buzz(SIGH)
- mode = BOT_WAIT_FOR_NAV
+ set_mode(BOT_WAIT_FOR_NAV)
blockcount = 0
addtimer(CALLBACK(src, PROC_REF(process_blocked), next), 2 SECONDS)
return
return
else
buzz(ANNOYED)
- mode = BOT_NAV
+ set_mode(BOT_NAV)
return
else
- mode = BOT_NAV
+ set_mode(BOT_NAV)
return
if(BOT_NAV) // calculate new path
- mode = BOT_WAIT_FOR_NAV
+ set_mode(BOT_WAIT_FOR_NAV)
INVOKE_ASYNC(src, PROC_REF(process_nav))
/mob/living/simple_animal/bot/mulebot/proc/process_blocked(turf/next)
calc_path(avoid=next)
if(length(path))
buzz(DELIGHT)
- mode = BOT_BLOCKED
+ set_mode(BOT_BLOCKED)
/mob/living/simple_animal/bot/mulebot/proc/process_nav()
calc_path()
if(length(path))
blockcount = 0
- mode = BOT_BLOCKED
+ set_mode(BOT_BLOCKED)
buzz(DELIGHT)
else
buzz(SIGH)
- mode = BOT_NO_ROUTE
+ set_mode(BOT_NO_ROUTE)
// calculates a path to the current destination
// given an optional turf to avoid
@@ -596,9 +596,9 @@
if(!(bot_mode_flags & BOT_MODE_ON))
return
if(destination == home_destination)
- mode = BOT_GO_HOME
+ set_mode(BOT_GO_HOME)
else
- mode = BOT_DELIVER
+ set_mode(BOT_DELIVER)
get_nav()
// starts bot moving to home
@@ -610,7 +610,7 @@
/mob/living/simple_animal/bot/mulebot/proc/do_start_home()
set_destination(home_destination)
- mode = BOT_BLOCKED
+ set_mode(BOT_BLOCKED)
// called when bot reaches current target
/mob/living/simple_animal/bot/mulebot/proc/at_target()
@@ -651,7 +651,7 @@
if(auto_return && home_destination && destination != home_destination)
// auto return set and not at home already
start_home()
- mode = BOT_BLOCKED
+ set_mode(BOT_BLOCKED)
else
bot_reset() // otherwise go idle
@@ -828,7 +828,7 @@
return
load = AM
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
update_appearance()
/mob/living/simple_animal/bot/mulebot/paranormal/update_overlays()
diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm
index e3ffc5661092..1b38041206fb 100644
--- a/code/modules/mob/living/simple_animal/bot/secbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/secbot.dm
@@ -125,7 +125,7 @@
/mob/living/simple_animal/bot/secbot/turn_off()
..()
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
/mob/living/simple_animal/bot/secbot/bot_reset()
..()
@@ -183,7 +183,7 @@
threatlevel += 6
if(threatlevel >= 4)
target = attacking_human
- mode = BOT_HUNT
+ set_mode(BOT_HUNT)
/mob/living/simple_animal/bot/secbot/proc/judgement_criteria()
var/final = FALSE
@@ -276,7 +276,7 @@
..()
/mob/living/simple_animal/bot/secbot/proc/start_handcuffing(mob/living/carbon/current_target)
- mode = BOT_ARREST
+ set_mode(BOT_ARREST)
playsound(src, 'sound/weapons/cablecuff.ogg', 30, TRUE, -2)
current_target.visible_message(span_danger("[src] is trying to put zipties on [current_target]!"),\
span_userdanger("[src] is trying to put zipties on you!"))
@@ -320,7 +320,7 @@
span_userdanger("[src] stuns you!"))
target_lastloc = target.loc
- mode = BOT_PREP_ARREST
+ set_mode(BOT_PREP_ARREST)
/mob/living/simple_animal/bot/secbot/handle_automated_action()
. = ..()
@@ -333,7 +333,7 @@
SSmove_manager.stop_looping(src)
look_for_perp() // see if any criminals are in range
if((mode == BOT_IDLE) && bot_mode_flags & BOT_MODE_AUTOPATROL) // didn't start hunting during look_for_perp, and set to patrol
- mode = BOT_START_PATROL // switch to patrol mode
+ set_mode(BOT_START_PATROL)
if(BOT_HUNT) // hunting for perp
// if can't reach perp for long enough, go idle
@@ -378,7 +378,7 @@
if(BOT_ARREST)
if(!target)
set_anchored(FALSE)
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
last_found = world.time
frustration = 0
return
@@ -391,7 +391,7 @@
back_to_hunt()
return
else //Try arresting again if the target escapes.
- mode = BOT_PREP_ARREST
+ set_mode(BOT_PREP_ARREST)
set_anchored(FALSE)
if(BOT_START_PATROL)
@@ -404,7 +404,7 @@
/mob/living/simple_animal/bot/secbot/proc/back_to_idle()
set_anchored(FALSE)
- mode = BOT_IDLE
+ set_mode(BOT_IDLE)
target = null
last_found = world.time
frustration = 0
@@ -413,7 +413,7 @@
/mob/living/simple_animal/bot/secbot/proc/back_to_hunt()
set_anchored(FALSE)
frustration = 0
- mode = BOT_HUNT
+ set_mode(BOT_HUNT)
INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
// look for a criminal in view of the bot
@@ -447,7 +447,7 @@
playsound(src, pick('sound/voice/beepsky/criminal.ogg', 'sound/voice/beepsky/justice.ogg', 'sound/voice/beepsky/freeze.ogg'), 50, FALSE)
visible_message("[src] points at [nearby_carbons.name]!")
- mode = BOT_HUNT
+ set_mode(BOT_HUNT)
INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
break
@@ -498,7 +498,7 @@
. = ..()
if(!isalien(target))
target = user
- mode = BOT_HUNT
+ set_mode(BOT_HUNT)
/mob/living/simple_animal/bot/secbot/proc/on_entered(datum/source, atom/movable/AM)
SIGNAL_HANDLER
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 3f89815a8f0d..e50ceb906d98 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -201,7 +201,10 @@
/// Update the pixel_y value of all huds attached to us.
/atom/proc/update_hud_images_height()
var/new_pixel_y = get_hud_pixel_y()
- for(var/hud in hud_possible)
+ for(var/hud in hud_list)
+ var/image/I = hud_list[hud]
+ if(!isimage(I) || I.override)
+ continue
set_hud_image_vars(hud, new_pixel_y = new_pixel_y, pixel_y_only = TRUE)
/**
diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
index 898cb4fca592..323c084505d5 100644
--- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
@@ -219,9 +219,9 @@
value = 6
/datum/reagent/medicine/tricordrazine/affect_blood(mob/living/carbon/C, removed)
- var/heal = 1 + ((clamp(round(current_cycle % 10), 0, 3))) * removed
+ var/heal = (1 + clamp(floor(current_cycle / 25), 0, 5)) * removed
C.heal_overall_damage(heal, heal, updating_health = FALSE)
- C.adjustToxLoss(-heal * removed, FALSE)
+ C.adjustToxLoss(-heal, FALSE)
return TRUE
/datum/reagent/medicine/tricordrazine/godblood