diff --git a/code/__DEFINES/ai/ai.dm b/code/__DEFINES/ai/ai.dm
index c91f7eb06597f..7cb863d07c19d 100644
--- a/code/__DEFINES/ai/ai.dm
+++ b/code/__DEFINES/ai/ai.dm
@@ -19,7 +19,7 @@
///For JPS pathing, the maximum length of a path we'll try to generate. Should be modularized depending on what we're doing later on
#define AI_MAX_PATH_LENGTH 30 // 30 is possibly overkill since by default we lose interest after 14 tiles of distance, but this gives wiggle room for weaving around obstacles
-#define AI_BOT_PATH_LENGTH 75
+#define AI_BOT_PATH_LENGTH 60
// How far should we, by default, be looking for interesting things to de-idle?
#define AI_DEFAULT_INTERESTING_DIST 10
diff --git a/code/__DEFINES/ai/bot_keys.dm b/code/__DEFINES/ai/bot_keys.dm
index 945102d6aa30c..c12f48273eecd 100644
--- a/code/__DEFINES/ai/bot_keys.dm
+++ b/code/__DEFINES/ai/bot_keys.dm
@@ -52,6 +52,8 @@ DEFINE_BITFIELD(repairbot_flags, list(
#define BB_RADIO_CHANNEL "radio_channel"
///list of unreachable things we will temporarily ignore
#define BB_TEMPORARY_IGNORE_LIST "temporary_ignore_list"
+///penalty cooldown if we are unable to path to any beacons
+#define BB_BOT_BEACON_COOLDOWN "bot_beacon_cooldown"
// medbot keys
///the patient we must heal
diff --git a/code/__DEFINES/span.dm b/code/__DEFINES/span.dm
index 9b3c2612afa34..d107782385833 100644
--- a/code/__DEFINES/span.dm
+++ b/code/__DEFINES/span.dm
@@ -24,6 +24,8 @@
#define span_blue(str) ("" + str + "")
#define span_blueteamradio(str) ("" + str + "")
#define span_bold(str) ("" + str + "")
+/// This span outputs to OOC, it's meant for OOC announcements
+/// Use span_bolddanger for IC danger messages, it's identical to this
#define span_boldannounce(str) ("" + str + "")
#define span_bolddanger(str) ("" + str + "")
#define span_bolditalic(str) ("" + str + "")
diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm
index bb2df78ab34ab..27d3c5d38ab60 100644
--- a/code/__HELPERS/maths.dm
+++ b/code/__HELPERS/maths.dm
@@ -1,5 +1,5 @@
///Calculate the angle between two movables and the west|east coordinate
-/proc/get_angle(atom/movable/start, atom/movable/end)//For beams.
+/proc/get_angle(atom/movable/start, atom/movable/end)
if(!start || !end)
return 0
var/dy =(ICON_SIZE_Y * end.y + end.pixel_y) - (ICON_SIZE_Y * start.y + start.pixel_y)
diff --git a/code/controllers/subsystem/dynamic/dynamic.dm b/code/controllers/subsystem/dynamic/dynamic.dm
index 99071820e3c3a..d22f30f209934 100644
--- a/code/controllers/subsystem/dynamic/dynamic.dm
+++ b/code/controllers/subsystem/dynamic/dynamic.dm
@@ -656,7 +656,7 @@ SUBSYSTEM_DEF(dynamic)
failed = TRUE //AFK client
if(!failed && L.stat)
if(HAS_TRAIT(L, TRAIT_SUICIDED)) //Suicider
- msg += "[L.name] ([L.key]), the [L.job] ([span_boldannounce("Suicide")])\n"
+ msg += "[L.name] ([L.key]), the [L.job] ([span_bolddanger("Suicide")])\n"
failed = TRUE //Disconnected client
if(!failed && (L.stat == UNCONSCIOUS || L.stat == HARD_CRIT))
msg += "[L.name] ([L.key]), the [L.job] (Dying)\n"
@@ -670,7 +670,7 @@ SUBSYSTEM_DEF(dynamic)
if(D.mind && D.mind.current == L)
if(L.stat == DEAD)
if(HAS_TRAIT(L, TRAIT_SUICIDED)) //Suicider
- msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_boldannounce("Suicide")])\n"
+ msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_bolddanger("Suicide")])\n"
continue //Disconnected client
else
msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n"
@@ -679,7 +679,7 @@ SUBSYSTEM_DEF(dynamic)
if(D.can_reenter_corpse)
continue //Adminghost, or cult/wizard ghost
else
- msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_boldannounce("Ghosted")])\n"
+ msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_bolddanger("Ghosted")])\n"
continue //Ghosted while alive
var/concatenated_message = msg.Join()
diff --git a/code/datums/ai/movement/ai_movement_jps.dm b/code/datums/ai/movement/ai_movement_jps.dm
index ddae792b31c86..3781dd7f5dc79 100644
--- a/code/datums/ai/movement/ai_movement_jps.dm
+++ b/code/datums/ai/movement/ai_movement_jps.dm
@@ -52,7 +52,7 @@
/datum/ai_movement/jps/bot/travel_to_beacon
maximum_length = AI_BOT_PATH_LENGTH
- max_pathing_attempts = 20
+ max_pathing_attempts = 10
/datum/ai_movement/jps/modsuit
maximum_length = MOD_AI_RANGE
diff --git a/code/datums/components/irradiated.dm b/code/datums/components/irradiated.dm
index a798124528c06..6df77dbfdc9fd 100644
--- a/code/datums/components/irradiated.dm
+++ b/code/datums/components/irradiated.dm
@@ -181,10 +181,10 @@
if (isliving(source))
var/mob/living/living_source = source
- to_chat(user, span_boldannounce("[icon2html(geiger_counter, user)] Subject is irradiated. Contamination traces back to roughly [DisplayTimeText(world.time - beginning_of_irradiation, 5)] ago. Current toxin levels: [living_source.getToxLoss()]."))
+ to_chat(user, span_bolddanger("[icon2html(geiger_counter, user)] Subject is irradiated. Contamination traces back to roughly [DisplayTimeText(world.time - beginning_of_irradiation, 5)] ago. Current toxin levels: [living_source.getToxLoss()]."))
else
// In case the green wasn't obvious enough...
- to_chat(user, span_boldannounce("[icon2html(geiger_counter, user)] Target is irradiated."))
+ to_chat(user, span_bolddanger("[icon2html(geiger_counter, user)] Target is irradiated."))
return COMSIG_GEIGER_COUNTER_SCAN_SUCCESSFUL
diff --git a/code/datums/components/tug_towards.dm b/code/datums/components/tug_towards.dm
new file mode 100644
index 0000000000000..f34cc05bc97fe
--- /dev/null
+++ b/code/datums/components/tug_towards.dm
@@ -0,0 +1,135 @@
+/// "Tugs" an atom towards another atom. That is to say, it will visually
+/// pixel offset to look like it is close to the point it's tugging to,
+/// but not actually move position.
+/datum/component/tug_towards
+ // If multiple are specified, will tug in between them.
+ dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
+
+ VAR_PRIVATE
+ /// atom -> strength
+ list/list/tugging_to_targets = list()
+
+
+ current_tug_offset_x = 0
+ current_tug_offset_y = 0
+
+/datum/component/tug_towards/Initialize(
+ /// The atom we are tugging towards.
+ atom/tugging_to,
+
+ /// Strength of the tug, as a number 0 through 1.
+ /// 0 means no tug, 1 means that if you're on an adjacent tile
+ /// you will be directly at the corner of the tugging_to_target.
+ /// Default is 0.8, which provides a healthy amount of
+ /// distance.
+ strength
+)
+ if (!isatom(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ ASSERT(istype(tugging_to))
+
+ add_tugging_to_target(tugging_to, strength)
+
+ RegisterSignals(parent, list(
+ COMSIG_MOVABLE_MOVED,
+ COMSIG_MOB_BUCKLED,
+ COMSIG_MOB_UNBUCKLED,
+ ), PROC_REF(update_tug))
+
+/datum/component/tug_towards/Destroy(force)
+ tugging_to_targets.Cut()
+
+ animate(
+ parent,
+ pixel_x = -current_tug_offset_x,
+ pixel_y = -current_tug_offset_y,
+ time = 0.2 SECONDS,
+ flags = ANIMATION_RELATIVE
+ )
+
+ return ..()
+
+/datum/component/tug_towards/InheritComponent(
+ datum/component/tug_towards/new_tug_towards,
+ i_am_original,
+
+ atom/tugging_to,
+ strength,
+)
+ add_tugging_to_target(tugging_to, strength)
+
+/datum/component/tug_towards/proc/remove_tug_target(atom/target)
+ tugging_to_targets -= target
+
+ if (tugging_to_targets.len == 0)
+ qdel(src)
+ else
+ update_tug()
+
+/datum/component/tug_towards/proc/add_tugging_to_target(
+ atom/tugging_to,
+ strength = 0.8,
+)
+ PRIVATE_PROC(TRUE)
+
+ tugging_to_targets[tugging_to] = strength
+ RegisterSignal(tugging_to, COMSIG_PREQDELETED, PROC_REF(on_tugging_to_qdeleting))
+ RegisterSignal(tugging_to, COMSIG_MOVABLE_MOVED, PROC_REF(update_tug))
+
+ update_tug()
+
+/datum/component/tug_towards/proc/on_tugging_to_qdeleting(datum/target)
+ SIGNAL_HANDLER
+ PRIVATE_PROC(TRUE)
+
+ tugging_to_targets -= target
+ if (tugging_to_targets.len == 0)
+ qdel(src)
+ else
+ update_tug()
+
+/datum/component/tug_towards/proc/update_tug()
+ SIGNAL_HANDLER
+ PRIVATE_PROC(TRUE)
+
+ var/atom/atom_parent = parent
+ var/mob/mob_parent = parent
+
+ var/total_tug_x = 0
+ var/total_tug_y = 0
+
+ if (!istype(mob_parent) || !mob_parent.buckled)
+ var/tuggers = 0
+
+ for (var/atom/target as anything in tugging_to_targets)
+ if (target.z != atom_parent.z)
+ continue
+
+ tuggers += 1
+ var/strength = tugging_to_targets[target]
+ total_tug_x += SIGN(target.x - atom_parent.x) * strength
+ total_tug_y += SIGN(target.y - atom_parent.y) * strength
+
+ // Intentionally not trig--something at a corner with a strength of 1 should have
+ // you at the corner, rather than root(2).
+ total_tug_x /= tuggers
+ total_tug_y /= tuggers
+
+ var/half_size = world.icon_size * 0.5
+ total_tug_x *= half_size
+ total_tug_y *= half_size
+
+ if (total_tug_x == current_tug_offset_x && total_tug_y == current_tug_offset_y)
+ return
+
+ animate(
+ atom_parent,
+ pixel_x = -current_tug_offset_x + total_tug_x,
+ pixel_y = -current_tug_offset_y + total_tug_y,
+ time = 0.2 SECONDS,
+ flags = ANIMATION_RELATIVE
+ )
+
+ current_tug_offset_x = total_tug_x
+ current_tug_offset_y = total_tug_y
diff --git a/code/datums/quirks/negative_quirks/prosthetic_limb.dm b/code/datums/quirks/negative_quirks/prosthetic_limb.dm
index f8941975ac15e..a9917ac0a59a8 100644
--- a/code/datums/quirks/negative_quirks/prosthetic_limb.dm
+++ b/code/datums/quirks/negative_quirks/prosthetic_limb.dm
@@ -28,7 +28,7 @@
old_limb = human_holder.return_and_replace_bodypart(surplus, special = TRUE)
/datum/quirk/prosthetic_limb/post_add()
- to_chat(quirk_holder, span_boldannounce("Your [slot_string] has been replaced with a surplus prosthetic. It has almost no muscle force, and makes you unhealthier by just having it. Additionally, \
+ to_chat(quirk_holder, span_bolddanger("Your [slot_string] has been replaced with a surplus prosthetic. It has almost no muscle force, and makes you unhealthier by just having it. Additionally, \
you need to use a welding tool and cables to repair it, instead of sutures and regenerative meshes."))
/datum/quirk/prosthetic_limb/remove()
diff --git a/code/datums/quirks/negative_quirks/prosthetic_organ.dm b/code/datums/quirks/negative_quirks/prosthetic_organ.dm
index 2b0f20b0d9f84..7877e177b39fa 100644
--- a/code/datums/quirks/negative_quirks/prosthetic_organ.dm
+++ b/code/datums/quirks/negative_quirks/prosthetic_organ.dm
@@ -65,7 +65,7 @@
STOP_PROCESSING(SSobj, old_organ)
/datum/quirk/prosthetic_organ/post_add()
- to_chat(quirk_holder, span_boldannounce("Your [slot_string] has been replaced with a surplus organ. It is weak and highly unstable. \
+ to_chat(quirk_holder, span_bolddanger("Your [slot_string] has been replaced with a surplus organ. It is weak and highly unstable. \
Additionally, any EMP will make it stop working entirely."))
/datum/quirk/prosthetic_organ/remove()
diff --git a/code/datums/quirks/negative_quirks/quadruple_amputee.dm b/code/datums/quirks/negative_quirks/quadruple_amputee.dm
index 493cdf0b71cda..653cca3dccddc 100644
--- a/code/datums/quirks/negative_quirks/quadruple_amputee.dm
+++ b/code/datums/quirks/negative_quirks/quadruple_amputee.dm
@@ -16,5 +16,5 @@
human_holder.del_and_replace_bodypart(new /obj/item/bodypart/leg/right/robot/surplus, special = TRUE)
/datum/quirk/quadruple_amputee/post_add()
- to_chat(quirk_holder, span_boldannounce("All your limbs have been replaced with surplus prosthetics. They are fragile and will easily come apart under duress. \
+ to_chat(quirk_holder, span_bolddanger("All your limbs have been replaced with surplus prosthetics. They are fragile and will easily come apart under duress. \
Additionally, you need to use a welding tool and cables to repair them, instead of bruise packs and ointment."))
diff --git a/code/datums/quirks/negative_quirks/tin_man.dm b/code/datums/quirks/negative_quirks/tin_man.dm
index 0afd8874bed49..a75cbbedd766f 100644
--- a/code/datums/quirks/negative_quirks/tin_man.dm
+++ b/code/datums/quirks/negative_quirks/tin_man.dm
@@ -33,5 +33,5 @@
new_organ.Insert(human_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED)
/datum/quirk/tin_man/post_add()
- to_chat(quirk_holder, span_boldannounce("Most of your internal organs have been replaced with surplus prosthetics. They are fragile and will easily come apart under duress. \
+ to_chat(quirk_holder, span_bolddanger("Most of your internal organs have been replaced with surplus prosthetics. They are fragile and will easily come apart under duress. \
Additionally, any EMP will make them stop working entirely."))
diff --git a/code/datums/quirks/neutral_quirks/monochromatic.dm b/code/datums/quirks/neutral_quirks/monochromatic.dm
index ef6735df25d93..6c1b8c2ef78bd 100644
--- a/code/datums/quirks/neutral_quirks/monochromatic.dm
+++ b/code/datums/quirks/neutral_quirks/monochromatic.dm
@@ -16,7 +16,7 @@
/datum/quirk/monochromatic/post_add()
if(is_detective_job(quirk_holder.mind.assigned_role))
- to_chat(quirk_holder, span_boldannounce("Mmm. Nothing's ever clear on this station. It's all shades of gray..."))
+ to_chat(quirk_holder, span_bolddanger("Mmm. Nothing's ever clear on this station. It's all shades of gray..."))
quirk_holder.playsound_local(quirk_holder, 'sound/ambience/security/ambidet1.ogg', 50, FALSE)
/datum/quirk/monochromatic/remove()
diff --git a/code/datums/quirks/neutral_quirks/transhumanist.dm b/code/datums/quirks/neutral_quirks/transhumanist.dm
index aa8ae075df395..cbd85150e7673 100644
--- a/code/datums/quirks/neutral_quirks/transhumanist.dm
+++ b/code/datums/quirks/neutral_quirks/transhumanist.dm
@@ -136,11 +136,11 @@
if(!slot_string)
return
if(isbodypart(old_part))
- to_chat(quirk_holder, span_boldannounce("Your [slot_string] has been replaced with a robotic limb. You need to use a welding tool and cables to repair it, instead of sutures and regenerative meshes."))
+ to_chat(quirk_holder, span_bolddanger("Your [slot_string] has been replaced with a robotic limb. You need to use a welding tool and cables to repair it, instead of sutures and regenerative meshes."))
else if (old_part.name == "eyes")
- to_chat(quirk_holder, span_boldannounce("You replaced your eyes with flashlights, not cameras. You can't see a thing!"))
+ to_chat(quirk_holder, span_bolddanger("You replaced your eyes with flashlights, not cameras. You can't see a thing!"))
else if (isorgan(old_part))
- to_chat(quirk_holder, span_boldannounce("Your [slot_string] brings you one step closer to silicon perfection, but you feel you're not quite there yet."))
+ to_chat(quirk_holder, span_bolddanger("Your [slot_string] brings you one step closer to silicon perfection, but you feel you're not quite there yet."))
/datum/quirk/transhumanist/remove()
if(isnull(old_part))
diff --git a/code/datums/quirks/positive_quirks/chip_connector.dm b/code/datums/quirks/positive_quirks/chip_connector.dm
index 6fcc5bf9d31be..2035c093699d1 100644
--- a/code/datums/quirks/positive_quirks/chip_connector.dm
+++ b/code/datums/quirks/positive_quirks/chip_connector.dm
@@ -22,7 +22,7 @@
connector.Insert(carbon_holder, special = TRUE)
/datum/quirk/chip_connector/post_add()
- to_chat(quirk_holder, span_boldannounce(desc)) // efficiency is clever laziness
+ to_chat(quirk_holder, span_bolddanger(desc)) // efficiency is clever laziness
/datum/quirk/chip_connector/remove()
qdel(connector)
diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm
index 7d432c1e488da..f9d9cfc612174 100644
--- a/code/datums/weather/weather_types/ash_storm.dm
+++ b/code/datums/weather/weather_types/ash_storm.dm
@@ -12,7 +12,7 @@
weather_duration_upper = 1200
weather_overlay = "ash_storm"
- end_message = span_boldannounce("The shrieking wind whips away the last of the ash and falls to its usual murmur. It should be safe to go outside now.")
+ end_message = span_bolddanger("The shrieking wind whips away the last of the ash and falls to its usual murmur. It should be safe to go outside now.")
end_duration = 300
end_overlay = "light_ash"
diff --git a/code/datums/weather/weather_types/snow_storm.dm b/code/datums/weather/weather_types/snow_storm.dm
index 2b749cdbc84d1..77aafed4f5dc5 100644
--- a/code/datums/weather/weather_types/snow_storm.dm
+++ b/code/datums/weather/weather_types/snow_storm.dm
@@ -14,7 +14,7 @@
use_glow = FALSE
end_duration = 100
- end_message = span_boldannounce("The snowfall dies down, it should be safe to go outside again.")
+ end_message = span_bolddanger("The snowfall dies down, it should be safe to go outside again.")
area_type = /area
protect_indoors = TRUE
diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm
index 437c2dbd168a6..f13b1947524fb 100644
--- a/code/game/machinery/iv_drip.dm
+++ b/code/game/machinery/iv_drip.dm
@@ -25,8 +25,8 @@
use_power = NO_POWER_USE
interaction_flags_mouse_drop = NEED_HANDS
- ///What are we sticking our needle in?
- var/atom/attached
+ /// Information and effects about where the IV drip is attached to
+ var/datum/iv_drip_attachment/attachment
///Are we donating or injecting?
var/mode = IV_INJECTING
///The chemicals flow speed
@@ -54,7 +54,7 @@
AddElement(/datum/element/noisy_movement)
/obj/machinery/iv_drip/Destroy()
- attached = null
+ QDEL_NULL(attachment)
QDEL_NULL(reagent_container)
return ..()
@@ -65,7 +65,7 @@
ui.open()
/obj/machinery/iv_drip/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
- if(attached)
+ if(attachment)
context[SCREENTIP_CONTEXT_RMB] = "Take needle out"
else if(reagent_container && !use_internal_storage)
context[SCREENTIP_CONTEXT_RMB] = "Eject container"
@@ -93,12 +93,12 @@
.["canRemoveContainer"] = !use_internal_storage
.["mode"] = mode == IV_INJECTING ? TRUE : FALSE
- .["canDraw"] = inject_only || (attached && !isliving(attached)) ? FALSE : TRUE
+ .["canDraw"] = inject_only || (attachment && !isliving(attachment.attached_to)) ? FALSE : TRUE
.["transferRate"] = transfer_rate
- .["hasObjectAttached"] = attached ? TRUE : FALSE
- if(attached)
- .["objectName"] = attached.name
+ .["hasObjectAttached"] = !!attachment
+ if(attachment)
+ .["objectName"] = attachment.attached_to.name
var/datum/reagents/drip_reagents = get_reagents()
if(drip_reagents)
@@ -131,7 +131,7 @@
update_appearance(UPDATE_ICON)
/obj/machinery/iv_drip/update_icon_state()
- if(transfer_rate > 0 && attached)
+ if(transfer_rate > 0 && attachment)
icon_state = "[base_icon_state]_[mode ? "injecting" : "donating"]"
else
icon_state = "[base_icon_state]_[mode ? "injectidle" : "donateidle"]"
@@ -143,7 +143,7 @@
if(!reagent_container)
return
- . += attached ? "beakeractive" : "beakeridle"
+ . += attachment ? "beakeractive" : "beakeridle"
var/datum/reagents/container_reagents = get_reagents()
if(!container_reagents)
return
@@ -172,9 +172,9 @@
if(!target.is_injectable(user))
to_chat(user, span_warning("Can't inject into this!"))
return
- if(attached)
- visible_message(span_warning("[attached] is detached from [src]."))
- attached = null
+ if(attachment)
+ visible_message(span_warning("[attachment.attached_to] is detached from [src]."))
+ QDEL_NULL(attachment)
update_appearance(UPDATE_ICON)
user.visible_message(span_warning("[user] attaches [src] to [target]."), span_notice("You attach [src] to [target]."))
attach_iv(target, user)
@@ -214,19 +214,21 @@
new /obj/item/stack/sheet/iron(loc)
/obj/machinery/iv_drip/process(seconds_per_tick)
- if(!attached)
+ if(!attachment)
return PROCESS_KILL
- if(!(get_dist(src, attached) <= 1 && isturf(attached.loc)))
- if(isliving(attached))
- var/mob/living/carbon/attached_mob = attached
- to_chat(attached, span_userdanger("The IV drip needle is ripped out of you, leaving an open bleeding wound!"))
+ var/atom/attached_to = attachment.attached_to
+
+ if(!(get_dist(src, attached_to) <= 1 && isturf(attached_to.loc)))
+ if(isliving(attached_to))
+ var/mob/living/carbon/attached_mob = attached_to
+ to_chat(attached_to, span_userdanger("The IV drip needle is ripped out of you, leaving an open bleeding wound!"))
var/list/arm_zones = shuffle(list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM))
var/obj/item/bodypart/chosen_limb = attached_mob.get_bodypart(arm_zones[1]) || attached_mob.get_bodypart(arm_zones[2]) || attached_mob.get_bodypart(BODY_ZONE_CHEST)
chosen_limb.receive_damage(3)
attached_mob.cause_wound_of_type_and_severity(WOUND_PIERCE, chosen_limb, WOUND_SEVERITY_MODERATE, wound_source = "IV needle")
else
- visible_message(span_warning("[attached] is detached from [src]."))
+ visible_message(span_warning("[attached_to] is detached from [src]."))
detach_iv()
return PROCESS_KILL
@@ -240,12 +242,12 @@
// Give reagents
if(mode)
if(drip_reagents.total_volume)
- drip_reagents.trans_to(attached, transfer_rate * seconds_per_tick, methods = INJECT, show_message = FALSE) //make reagents reacts, but don't spam messages
+ drip_reagents.trans_to(attached_to, transfer_rate * seconds_per_tick, methods = INJECT, show_message = FALSE) //make reagents reacts, but don't spam messages
update_appearance(UPDATE_ICON)
// Take blood
- else if (isliving(attached))
- var/mob/living/attached_mob = attached
+ else if (isliving(attached_to))
+ var/mob/living/attached_mob = attached_to
var/amount = min(transfer_rate * seconds_per_tick, drip_reagents.maximum_volume - drip_reagents.total_volume)
// If the beaker is full, ping
if(!amount)
@@ -267,8 +269,8 @@
return
if(!ishuman(user))
return
- if(attached)
- visible_message(span_notice("[attached] is detached from [src]."))
+ if(attachment)
+ visible_message(span_notice("[attachment.attached_to] is detached from [src]."))
detach_iv()
else if(reagent_container)
eject_beaker(user)
@@ -291,7 +293,10 @@
if(isliving(target))
var/mob/living/target_mob = target
target_mob.throw_alert(ALERT_IV_CONNECTED, /atom/movable/screen/alert/iv_connected)
- attached = target
+
+ qdel(attachment)
+ attachment = new(src, target)
+
START_PROCESSING(SSmachines, src)
update_appearance(UPDATE_ICON)
@@ -299,13 +304,13 @@
///Called when an iv is detached. doesnt include chat stuff because there's multiple options and its better handled by the caller
/obj/machinery/iv_drip/proc/detach_iv()
- if(attached)
- visible_message(span_notice("[attached] is detached from [src]."))
- if(isliving(attached))
- var/mob/living/attached_mob = attached
+ if(attachment)
+ visible_message(span_notice("[attachment.attached_to] is detached from [src]."))
+ if(isliving(attachment.attached_to))
+ var/mob/living/attached_mob = attachment.attached_to
attached_mob.clear_alert(ALERT_IV_CONNECTED, /atom/movable/screen/alert/iv_connected)
- SEND_SIGNAL(src, COMSIG_IV_DETACH, attached)
- attached = null
+ SEND_SIGNAL(src, COMSIG_IV_DETACH, attachment?.attached_to)
+ QDEL_NULL(attachment)
update_appearance(UPDATE_ICON)
/// Get the reagents used by IV drip
@@ -325,8 +330,8 @@
if(usr.incapacitated)
return
if(reagent_container)
- if(attached)
- visible_message(span_warning("[attached] is detached from [src]."))
+ if(attachment)
+ visible_message(span_warning("[attachment?.attached_to] is detached from [src]."))
detach_iv()
reagent_container.forceMove(drop_location())
reagent_container = null
@@ -346,7 +351,7 @@
mode = IV_INJECTING
return
// Prevent blood draining from non-living
- if(attached && !isliving(attached))
+ if(attachment && !isliving(attachment.attached_to))
mode = IV_INJECTING
return
mode = !mode
@@ -367,7 +372,50 @@
. += span_notice("It has an internal chemical storage.")
else
. += span_notice("No chemicals are attached.")
- . += span_notice("[attached ? attached : "Nothing"] is connected.")
+ . += span_notice("[attachment ? attachment.attached_to : "Nothing"] is connected.")
+
+/// Information and effects about where an IV drip is attached to
+// Lifetime is managed by the iv_drip, which will delete the iv_drip_attachment after
+// a process if the attached object is invalid.
+// iv_drip_attachment should never outlive iv_drip.
+/datum/iv_drip_attachment
+ var/obj/machinery/iv_drip/iv_drip
+ var/atom/attached_to
+
+ VAR_PRIVATE
+ datum/beam/beam
+ datum/component/tug_towards/tug_to_me
+
+/datum/iv_drip_attachment/New(
+ obj/machinery/iv_drip/iv_drip,
+ atom/attached_to
+)
+ src.iv_drip = iv_drip
+ src.attached_to = attached_to
+
+ tug_to_me = attached_to.AddComponent(/datum/component/tug_towards, iv_drip)
+
+ beam = iv_drip.Beam(
+ attached_to,
+ icon_state = "1-full",
+ beam_color = COLOR_SILVER,
+ layer = BELOW_MOB_LAYER,
+
+ // Come out from the spout
+ override_origin_pixel_x = 9,
+ override_origin_pixel_y = 2,
+ )
+
+/datum/iv_drip_attachment/Destroy(force)
+ tug_to_me.remove_tug_target(iv_drip)
+ tug_to_me = null
+
+ iv_drip = null
+ attached_to = null
+
+ QDEL_NULL(beam)
+
+ return ..()
/datum/crafting_recipe/iv_drip
name = "IV drip"
diff --git a/code/game/machinery/newscaster/newscaster_machine.dm b/code/game/machinery/newscaster/newscaster_machine.dm
index 39fe7ce6cb73e..dbaa134f749a6 100644
--- a/code/game/machinery/newscaster/newscaster_machine.dm
+++ b/code/game/machinery/newscaster/newscaster_machine.dm
@@ -589,7 +589,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30)
else
to_chat(user, span_warning("You cannot interface with silicon photo uploading!"))
if(!targetcam.stored.len)
- to_chat(usr, span_boldannounce("No images saved."))
+ to_chat(usr, span_bolddanger("No images saved."))
return
var/datum/picture/selection = targetcam.selectpicture(user)
if(selection)
diff --git a/code/game/objects/items/chainsaw.dm b/code/game/objects/items/chainsaw.dm
index f2d81ac4bde1d..8045646b075ca 100644
--- a/code/game/objects/items/chainsaw.dm
+++ b/code/game/objects/items/chainsaw.dm
@@ -68,6 +68,8 @@
toolspeed = active ? 0.5 : initial(toolspeed)
update_item_action_buttons()
+ return COMPONENT_NO_DEFAULT_MESSAGE
+
/obj/item/chainsaw/suicide_act(mob/living/carbon/user)
if(!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE))
user.visible_message(span_suicide("[user] smashes [src] into [user.p_their()] neck, destroying [user.p_their()] esophagus! It looks like [user.p_theyre()] trying to commit suicide!"))
diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm
index 6ccbaa5fa4454..70fb3bb6693c2 100644
--- a/code/game/objects/items/defib.dm
+++ b/code/game/objects/items/defib.dm
@@ -558,7 +558,7 @@
playsound(src, 'sound/machines/defib/defib_failed.ogg', 50, FALSE)
do_cancel()
return
- user.visible_message(span_boldannounce("[user] shocks [H] with \the [src]!"), span_warning("You shock [H] with \the [src]!"))
+ user.visible_message(span_bolddanger("[user] shocks [H] with \the [src]!"), span_warning("You shock [H] with \the [src]!"))
playsound(src, 'sound/machines/defib/defib_zap.ogg', 100, TRUE, -1)
playsound(src, 'sound/items/weapons/egloves.ogg', 100, TRUE, -1)
H.emote("scream")
diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm
index aedffb566b276..3535bef007407 100644
--- a/code/game/objects/items/devices/scanners/health_analyzer.dm
+++ b/code/game/objects/items/devices/scanners/health_analyzer.dm
@@ -458,7 +458,7 @@
var/datum/reagent/reagent = r
if(reagent.chemical_flags & REAGENT_INVISIBLE) //Don't show hidden chems on scanners
continue
- render_block += "[round(reagent.volume, 0.001)] units of [reagent.name][reagent.overdosed ? " - [span_boldannounce("OVERDOSING")]" : "."]
"
+ render_block += "[round(reagent.volume, 0.001)] units of [reagent.name][reagent.overdosed ? " - [span_bolddanger("OVERDOSING")]" : "."]
"
if(!length(render_block)) //If no VISIBLY DISPLAYED reagents are present, we report as if there is nothing.
render_list += "Subject contains no reagents in their blood.
"
@@ -476,11 +476,11 @@
if(bit.chemical_flags & REAGENT_INVISIBLE)
continue
if(!belly.food_reagents[bit.type])
- render_block += "[round(bit.volume, 0.001)] units of [bit.name][bit.overdosed ? " - [span_boldannounce("OVERDOSING")]" : "."]
"
+ render_block += "[round(bit.volume, 0.001)] units of [bit.name][bit.overdosed ? " - [span_bolddanger("OVERDOSING")]" : "."]
"
else
var/bit_vol = bit.volume - belly.food_reagents[bit.type]
if(bit_vol > 0)
- render_block += "[round(bit_vol, 0.001)] units of [bit.name][bit.overdosed ? " - [span_boldannounce("OVERDOSING")]" : "."]
"
+ render_block += "[round(bit_vol, 0.001)] units of [bit.name][bit.overdosed ? " - [span_bolddanger("OVERDOSING")]" : "."]
"
if(!length(render_block))
render_list += "Subject contains no reagents in their stomach.
"
diff --git a/code/game/objects/items/devices/table_clock.dm b/code/game/objects/items/devices/table_clock.dm
index d9c5e44fcf76c..fcea4c27246ab 100644
--- a/code/game/objects/items/devices/table_clock.dm
+++ b/code/game/objects/items/devices/table_clock.dm
@@ -40,7 +40,7 @@
if(break_clock(break_sound = 'sound/effects/magic/clockwork/ark_activation.ogg'))
user.visible_message(
span_warning("[user] smashes \the [src] so hard it stops breaking!"),
- span_boldannounce("I can't stand this stupid machine anymore! Shut up already!"),
+ span_bolddanger("I can't stand this stupid machine anymore! Shut up already!"),
span_notice("You hear repeated smashing!"),
)
diff --git a/code/game/objects/items/granters/martial_arts/cqc.dm b/code/game/objects/items/granters/martial_arts/cqc.dm
index 7d3f7f2ef9e26..4fff32623c864 100644
--- a/code/game/objects/items/granters/martial_arts/cqc.dm
+++ b/code/game/objects/items/granters/martial_arts/cqc.dm
@@ -3,7 +3,7 @@
name = "old manual"
martial_name = "close quarters combat"
desc = "A small, black manual. There are drawn instructions of tactical hand-to-hand combat."
- greet = span_boldannounce("You've mastered the basics of CQC.")
+ greet = span_bolddanger("You've mastered the basics of CQC.")
icon_state = "cqcmanual"
remarks = list(
"Kick... Slam...",
diff --git a/code/game/objects/items/granters/martial_arts/plasma_fist.dm b/code/game/objects/items/granters/martial_arts/plasma_fist.dm
index 22b6b4aefa18e..6c185243c7086 100644
--- a/code/game/objects/items/granters/martial_arts/plasma_fist.dm
+++ b/code/game/objects/items/granters/martial_arts/plasma_fist.dm
@@ -3,7 +3,7 @@
name = "frayed scroll"
martial_name = "plasma fist"
desc = "An aged and frayed scrap of paper written in shifting runes. There are hand-drawn illustrations of pugilism."
- greet = span_boldannounce("You have learned the ancient martial art of Plasma Fist. Your combos are extremely hard to pull off, but include some of the most deadly moves ever seen including \
+ greet = span_bolddanger("You have learned the ancient martial art of Plasma Fist. Your combos are extremely hard to pull off, but include some of the most deadly moves ever seen including \
the plasma fist, which when pulled off will make someone violently explode.")
icon = 'icons/obj/scrolls.dmi'
icon_state ="plasmafist"
diff --git a/code/game/objects/items/pet_carrier.dm b/code/game/objects/items/pet_carrier.dm
index 2d700cf3ff0c0..3bab737c9fbd7 100644
--- a/code/game/objects/items/pet_carrier.dm
+++ b/code/game/objects/items/pet_carrier.dm
@@ -130,7 +130,7 @@
if(!do_after(user, rand(300, 400), target = user) || open || !locked || !(user in occupants))
return
loc.visible_message(span_warning("[user] flips the lock switch on [src] by reaching through!"), null, null, null, user)
- to_chat(user, span_boldannounce("Bingo! The lock pops open!"))
+ to_chat(user, span_bolddanger("Bingo! The lock pops open!"))
locked = FALSE
playsound(src, 'sound/machines/airlock/boltsup.ogg', 30, TRUE)
update_appearance()
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index eb5ade8414263..a731772b0902c 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -297,19 +297,19 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
new_name = "triple-notched claymore"
add_atom_colour(rgb(255, 215, 215), ADMIN_COLOUR_PRIORITY)
if(4)
- to_chat(user, span_notice("You've lost count of how many you've killed."))
+ to_chat(user, span_notice("You've lost count of how many you've killed."))
new_name = "many-notched claymore"
add_atom_colour(rgb(255, 195, 195), ADMIN_COLOUR_PRIORITY)
if(5)
- to_chat(user, span_boldannounce("Five voices now echo in your mind, cheering the slaughter."))
+ to_chat(user, span_bolddanger("Five voices now echo in your mind, cheering the slaughter."))
new_name = "battle-tested claymore"
add_atom_colour(rgb(255, 175, 175), ADMIN_COLOUR_PRIORITY)
if(6)
- to_chat(user, span_boldannounce("Is this what the vikings felt like? Visions of glory fill your head as you slay your sixth foe."))
+ to_chat(user, span_bolddanger("Is this what the vikings felt like? Visions of glory fill your head as you slay your sixth foe."))
new_name = "battle-scarred claymore"
add_atom_colour(rgb(255, 155, 155), ADMIN_COLOUR_PRIORITY)
if(7)
- to_chat(user, span_boldannounce("Kill. Butcher. Conquer."))
+ to_chat(user, span_bolddanger("Kill. Butcher. Conquer."))
new_name = "vicious claymore"
add_atom_colour(rgb(255, 135, 135), ADMIN_COLOUR_PRIORITY)
if(8)
diff --git a/code/game/objects/structures/hivebot.dm b/code/game/objects/structures/hivebot.dm
index 838113cab8c38..19e35d30a5e6c 100644
--- a/code/game/objects/structures/hivebot.dm
+++ b/code/game/objects/structures/hivebot.dm
@@ -13,7 +13,7 @@
var/datum/effect_system/fluid_spread/smoke/smoke = new
smoke.set_up(2, holder = src, location = loc)
smoke.start()
- visible_message(span_boldannounce("[src] warps in!"))
+ visible_message(span_bolddanger("[src] warps in!"))
playsound(src.loc, 'sound/effects/empulse.ogg', 25, TRUE)
addtimer(CALLBACK(src, PROC_REF(warpbots)), rand(1 SECONDS, 1 MINUTES))
@@ -30,7 +30,7 @@
if("rapid")
new /mob/living/basic/hivebot/rapid(get_turf(src))
sleep(10 SECONDS)
- visible_message(span_boldannounce("[src] warps out!"))
+ visible_message(span_bolddanger("[src] warps out!"))
playsound(src.loc, 'sound/effects/empulse.ogg', 25, TRUE)
qdel(src)
return
diff --git a/code/game/objects/structures/icemoon/cave_entrance.dm b/code/game/objects/structures/icemoon/cave_entrance.dm
index f9d490a29b2e9..b53f11c711a61 100644
--- a/code/game/objects/structures/icemoon/cave_entrance.dm
+++ b/code/game/objects/structures/icemoon/cave_entrance.dm
@@ -50,7 +50,7 @@ GLOBAL_LIST_INIT(ore_probability, list(
*/
/obj/structure/spawner/ice_moon/proc/destroy_effect()
playsound(loc,'sound/effects/explosion/explosionfar.ogg', 200, TRUE)
- visible_message(span_boldannounce("[src] collapses, sealing everything inside!\nOres fall out of the cave as it is destroyed!"))
+ visible_message(span_bolddanger("[src] collapses, sealing everything inside!\nOres fall out of the cave as it is destroyed!"))
/**
* Drops items after the spawner is destroyed
@@ -123,7 +123,7 @@ GLOBAL_LIST_INIT(ore_probability, list(
/obj/effect/collapsing_demonic_portal/Initialize(mapload)
. = ..()
playsound(loc,'sound/effects/tendril_destroyed.ogg', 200, FALSE, 50, TRUE, TRUE)
- visible_message(span_boldannounce("[src] begins to collapse, cutting it off from this world!"))
+ visible_message(span_bolddanger("[src] begins to collapse, cutting it off from this world!"))
animate(src, transform = matrix().Scale(0, 1), alpha = 50, time = 5 SECONDS)
addtimer(CALLBACK(src, PROC_REF(collapse)), 5 SECONDS)
diff --git a/code/game/objects/structures/lavaland/necropolis_tendril.dm b/code/game/objects/structures/lavaland/necropolis_tendril.dm
index b169868a85fb7..0461180721e2b 100644
--- a/code/game/objects/structures/lavaland/necropolis_tendril.dm
+++ b/code/game/objects/structures/lavaland/necropolis_tendril.dm
@@ -88,7 +88,7 @@ GLOBAL_LIST_INIT(tendrils, list())
/obj/effect/collapse/Initialize(mapload)
. = ..()
emitted_light = new(loc)
- visible_message(span_boldannounce("The tendril writhes in fury as the earth around it begins to crack and break apart! Get back!"))
+ visible_message(span_bolddanger("The tendril writhes in fury as the earth around it begins to crack and break apart! Get back!"))
balloon_alert_to_viewers("interact to grab loot before collapse!", vision_distance = 7)
playsound(loc,'sound/effects/tendril_destroyed.ogg', 200, FALSE, 50, TRUE, TRUE)
addtimer(CALLBACK(src, PROC_REF(collapse)), 5 SECONDS)
@@ -132,7 +132,7 @@ GLOBAL_LIST_INIT(tendrils, list())
for(var/mob/M in range(7,src))
shake_camera(M, 15, 1)
playsound(get_turf(src),'sound/effects/explosion/explosionfar.ogg', 200, TRUE)
- visible_message(span_boldannounce("The tendril falls inward, the ground around it widening into a yawning chasm!"))
+ visible_message(span_bolddanger("The tendril falls inward, the ground around it widening into a yawning chasm!"))
for(var/turf/T in RANGE_TURFS(2,src))
if(HAS_TRAIT(T, TRAIT_NO_TERRAFORM))
continue
diff --git a/code/modules/admin/verbs/highlander_datum.dm b/code/modules/admin/verbs/highlander_datum.dm
index de7e4918fc00b..c574c39288213 100644
--- a/code/modules/admin/verbs/highlander_datum.dm
+++ b/code/modules/admin/verbs/highlander_datum.dm
@@ -11,7 +11,7 @@ GLOBAL_DATUM(highlander_controller, /datum/highlander_controller)
. = ..()
RegisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED, PROC_REF(new_highlander))
sound_to_playing_players('sound/misc/highlander.ogg')
- send_to_playing_players(span_boldannounce("THERE CAN BE ONLY ONE"))
+ send_to_playing_players(span_bolddanger("THERE CAN BE ONLY ONE"))
for(var/obj/item/disk/nuclear/nuke_disk as anything in SSpoints_of_interest.real_nuclear_disks)
var/datum/component/stationloving/component = nuke_disk.GetComponent(/datum/component/stationloving)
component?.relocate() //Gets it out of bags and such
diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm
index 26cf122211305..e43164419d59c 100644
--- a/code/modules/admin/verbs/secrets.dm
+++ b/code/modules/admin/verbs/secrets.dm
@@ -493,7 +493,7 @@ ADMIN_VERB(secrets, R_NONE, "Secrets", "Abuse harder than you ever have before w
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Mass Braindamage"))
for(var/mob/living/carbon/human/H in GLOB.player_list)
- to_chat(H, span_boldannounce("You suddenly feel stupid."), confidential = TRUE)
+ to_chat(H, span_bolddanger("You suddenly feel stupid."), confidential = TRUE)
H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 60, 80)
message_admins("[key_name_admin(holder)] made everybody brain damaged")
if("floorlava")
diff --git a/code/modules/antagonists/blob/blob_antag.dm b/code/modules/antagonists/blob/blob_antag.dm
index 25bea4b083eaa..c5dfa6a1e10ca 100644
--- a/code/modules/antagonists/blob/blob_antag.dm
+++ b/code/modules/antagonists/blob/blob_antag.dm
@@ -100,7 +100,7 @@
. = ..()
if(owner)
addtimer(CALLBACK(src, PROC_REF(Activate), TRUE), autoplace_time, TIMER_UNIQUE|TIMER_OVERRIDE)
- to_chat(owner, span_boldannounce("You will automatically pop and place your blob core in [DisplayTimeText(autoplace_time)]."))
+ to_chat(owner, span_bolddanger("You will automatically pop and place your blob core in [DisplayTimeText(autoplace_time)]."))
/datum/action/innate/blobpop/Activate(timer_activated = FALSE)
var/mob/living/old_body = owner
@@ -183,5 +183,3 @@
/obj/effect/dummy/phased_mob/can_blob_attack()
return FALSE
-
-
diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm
index db40090615b07..bb43097bcd227 100644
--- a/code/modules/antagonists/blob/overmind.dm
+++ b/code/modules/antagonists/blob/overmind.dm
@@ -139,7 +139,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
if(!placed)
if(manualplace_min_time && world.time >= manualplace_min_time)
to_chat(src, span_boldnotice("You may now place your blob core."))
- to_chat(src, span_boldannounce("You will automatically place your blob core in [DisplayTimeText(autoplace_max_time - world.time)]."))
+ to_chat(src, span_bolddanger("You will automatically place your blob core in [DisplayTimeText(autoplace_max_time - world.time)]."))
manualplace_min_time = 0
if(autoplace_max_time && world.time >= autoplace_max_time)
place_blob_core(BLOB_RANDOM_PLACEMENT)
@@ -261,8 +261,8 @@ GLOBAL_LIST_EMPTY(blob_nodes)
return FALSE
to_chat(src, span_blobannounce("You are the overmind!"))
if(!placed && autoplace_max_time <= world.time)
- to_chat(src, span_boldannounce("You will automatically place your blob core in [DisplayTimeText(autoplace_max_time - world.time)]."))
- to_chat(src, span_boldannounce("You [manualplace_min_time ? "will be able to":"can"] manually place your blob core by pressing the Place Blob Core button in the bottom right corner of the screen."))
+ to_chat(src, span_bolddanger("You will automatically place your blob core in [DisplayTimeText(autoplace_max_time - world.time)]."))
+ to_chat(src, span_bolddanger("You [manualplace_min_time ? "will be able to":"can"] manually place your blob core by pressing the Place Blob Core button in the bottom right corner of the screen."))
update_health_hud()
add_points(0)
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index 70c9081c52ea9..b74de5e146ba5 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -1031,7 +1031,7 @@
/datum/antagonist/changeling/headslug/greet()
play_stinger()
- to_chat(owner, span_boldannounce("You are a fresh changeling birthed from a headslug! \
+ to_chat(owner, span_bolddanger("You are a fresh changeling birthed from a headslug! \
You aren't as strong as a normal changeling, as you are newly born."))
diff --git a/code/modules/antagonists/fugitive/hunters/hunter.dm b/code/modules/antagonists/fugitive/hunters/hunter.dm
index ba26645364712..8c68f0b63bf3b 100644
--- a/code/modules/antagonists/fugitive/hunters/hunter.dm
+++ b/code/modules/antagonists/fugitive/hunters/hunter.dm
@@ -26,7 +26,7 @@
/datum/antagonist/fugitive_hunter/greet()
switch(backstory)
if(HUNTER_PACK_COPS)
- to_chat(owner, span_boldannounce("Justice has arrived. I am a member of the Spacepol!"))
+ to_chat(owner, span_bolddanger("Justice has arrived. I am a member of the Spacepol!"))
to_chat(owner, "The criminals should be on the station, we have special huds implanted to recognize them.")
to_chat(owner, "As we have lost pretty much all power over these damned lawless megacorporations, it's a mystery if their security will cooperate with us.")
if(HUNTER_PACK_RUSSIAN)
@@ -47,7 +47,7 @@
to_chat(owner, span_danger("Your mission is simple. Infiltrate the facility and extract the target, dead or alive."))
to_chat(owner, span_danger("This is a stealth infiltration mission in hostile enemy territory. Be wary, and avoid being caught if possible."))
- to_chat(owner, span_boldannounce("You are not an antagonist in that you may kill whomever you please, but you can do anything to ensure the capture of the fugitives, even if that means going through the station."))
+ to_chat(owner, span_bolddanger("You are not an antagonist in that you may kill whomever you please, but you can do anything to ensure the capture of the fugitives, even if that means going through the station."))
owner.announce_objectives()
/datum/antagonist/fugitive_hunter/create_team(datum/team/fugitive_hunters/new_team)
diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm
index 184ca9c4f77f4..fa2dcee4d36eb 100644
--- a/code/modules/antagonists/highlander/highlander.dm
+++ b/code/modules/antagonists/highlander/highlander.dm
@@ -43,7 +43,7 @@
. = ..()
/datum/antagonist/highlander/greet()
- to_chat(owner, span_boldannounce("Your [sword.name] cries out for blood. Claim the lives of others, and your own will be restored!\n\
+ to_chat(owner, span_bolddanger("Your [sword.name] cries out for blood. Claim the lives of others, and your own will be restored!\n\
Activate it in your hand, and it will lead to the nearest target. Attack the nuclear authentication disk with it, and you will store it."))
owner.announce_objectives()
@@ -88,7 +88,7 @@
name = "\improper highlander"
/datum/antagonist/highlander/robot/greet()
- to_chat(owner, span_boldannounce("Your integrated claymore cries out for blood. Claim the lives of others, and your own will be restored!\n\
+ to_chat(owner, span_bolddanger("Your integrated claymore cries out for blood. Claim the lives of others, and your own will be restored!\n\
Activate it in your hand, and it will lead to the nearest target. Attack the nuclear authentication disk with it, and you will store it."))
/datum/antagonist/highlander/robot/give_equipment()
diff --git a/code/modules/antagonists/malf_ai/malf_ai_modules.dm b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
index f91d1b90ba46b..1776371bae9fe 100644
--- a/code/modules/antagonists/malf_ai/malf_ai_modules.dm
+++ b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
@@ -191,30 +191,30 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module/malf))
set waitfor = FALSE
message_admins("[key_name_admin(owner)][ADMIN_FLW(owner)] has activated AI Doomsday.")
var/pass = prob(10) ? "******" : "hunter2"
- to_chat(owner, "run -o -a 'selfdestruct'")
+ to_chat(owner, "run -o -a 'selfdestruct'")
sleep(0.5 SECONDS)
if(QDELETED(owner) || !isturf(owner_AI.loc))
active = FALSE
return
- to_chat(owner, "Running executable 'selfdestruct'...")
+ to_chat(owner, "Running executable 'selfdestruct'...")
sleep(rand(10, 30))
if(QDELETED(owner) || !isturf(owner_AI.loc))
active = FALSE
return
owner.playsound_local(owner, 'sound/announcer/alarm/bloblarm.ogg', 50, 0, use_reverb = FALSE)
to_chat(owner, span_userdanger("!!! UNAUTHORIZED SELF-DESTRUCT ACCESS !!!"))
- to_chat(owner, span_boldannounce("This is a class-3 security violation. This incident will be reported to Central Command."))
+ to_chat(owner, span_bolddanger("This is a class-3 security violation. This incident will be reported to Central Command."))
for(var/i in 1 to 3)
sleep(2 SECONDS)
if(QDELETED(owner) || !isturf(owner_AI.loc))
active = FALSE
return
- to_chat(owner, span_boldannounce("Sending security report to Central Command.....[rand(0, 9) + (rand(20, 30) * i)]%"))
+ to_chat(owner, span_bolddanger("Sending security report to Central Command.....[rand(0, 9) + (rand(20, 30) * i)]%"))
sleep(0.3 SECONDS)
if(QDELETED(owner) || !isturf(owner_AI.loc))
active = FALSE
return
- to_chat(owner, "auth 'akjv9c88asdf12nb' [pass]")
+ to_chat(owner, "auth 'akjv9c88asdf12nb' [pass]")
owner.playsound_local(owner, 'sound/items/timer.ogg', 50, 0, use_reverb = FALSE)
sleep(3 SECONDS)
if(QDELETED(owner) || !isturf(owner_AI.loc))
@@ -232,7 +232,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module/malf))
if(QDELETED(owner) || !isturf(owner_AI.loc))
active = FALSE
return
- to_chat(owner, "Y")
+ to_chat(owner, "Y")
sleep(1.5 SECONDS)
if(QDELETED(owner) || !isturf(owner_AI.loc))
active = FALSE
@@ -243,7 +243,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module/malf))
if(QDELETED(owner) || !isturf(owner_AI.loc))
active = FALSE
return
- to_chat(owner, "Y")
+ to_chat(owner, "Y")
sleep(rand(15, 25))
if(QDELETED(owner) || !isturf(owner_AI.loc))
active = FALSE
@@ -254,7 +254,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module/malf))
if(QDELETED(owner) || !isturf(owner_AI.loc))
active = FALSE
return
- to_chat(owner, "[pass]")
+ to_chat(owner, "[pass]")
sleep(4 SECONDS)
if(QDELETED(owner) || !isturf(owner_AI.loc))
active = FALSE
diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm
index 7445b20cb902a..8598a929454b3 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm
@@ -653,7 +653,7 @@ GLOBAL_VAR(station_nuke_source)
if(istype(gibbed.loc, /obj/structure/closet/secure_closet/freezer))
var/obj/structure/closet/secure_closet/freezer/freezer = gibbed.loc
if(!freezer.jones)
- to_chat(gibbed, span_boldannounce("You hold onto [freezer] as [source] goes off. \
+ to_chat(gibbed, span_bolddanger("You hold onto [freezer] as [source] goes off. \
Luckily, as [freezer] is lead-lined, you survive."))
freezer.jones = TRUE
return FALSE
diff --git a/code/modules/antagonists/revolution/enemy_of_the_state.dm b/code/modules/antagonists/revolution/enemy_of_the_state.dm
index 90a6431d428b3..d835655b7efd4 100644
--- a/code/modules/antagonists/revolution/enemy_of_the_state.dm
+++ b/code/modules/antagonists/revolution/enemy_of_the_state.dm
@@ -30,7 +30,7 @@
/datum/antagonist/enemy_of_the_state/greet()
. = ..()
to_chat(owner, span_userdanger("The revolution is dead."))
- to_chat(owner, span_boldannounce("You're an enemy of the state to Nanotrasen. You're a loose end to the Syndicate."))
+ to_chat(owner, span_bolddanger("You're an enemy of the state to Nanotrasen. You're a loose end to the Syndicate."))
to_chat(owner, "It's time to live out your days as an exile... Or go out in one last big bang.")
owner.announce_objectives()
diff --git a/code/modules/antagonists/santa/santa.dm b/code/modules/antagonists/santa/santa.dm
index 2a5eb75e5c801..f3e1cbd02b963 100644
--- a/code/modules/antagonists/santa/santa.dm
+++ b/code/modules/antagonists/santa/santa.dm
@@ -14,7 +14,7 @@
/datum/antagonist/santa/greet()
. = ..()
- to_chat(owner, span_boldannounce("Your objective is to bring joy to the people on this station. You have a magical bag, which generates presents as long as you have it! You can examine the presents to take a peek inside, to make sure that you give the right gift to the right person."))
+ to_chat(owner, span_bolddanger("Your objective is to bring joy to the people on this station. You have a magical bag, which generates presents as long as you have it! You can examine the presents to take a peek inside, to make sure that you give the right gift to the right person."))
/datum/antagonist/santa/proc/give_equipment()
var/mob/living/carbon/human/H = owner.current
diff --git a/code/modules/antagonists/wizard/equipment/artefact.dm b/code/modules/antagonists/wizard/equipment/artefact.dm
index e56f651a095a8..465765c75a332 100644
--- a/code/modules/antagonists/wizard/equipment/artefact.dm
+++ b/code/modules/antagonists/wizard/equipment/artefact.dm
@@ -29,7 +29,7 @@
if(charges > 0)
new /obj/effect/rend(get_turf(user), spawn_type, spawn_amt, rend_desc, spawn_fast)
charges--
- user.visible_message(span_boldannounce("[src] hums with power as [user] deals a blow to [activate_descriptor] itself!"))
+ user.visible_message(span_bolddanger("[src] hums with power as [user] deals a blow to [activate_descriptor] itself!"))
else
to_chat(user, span_danger("The unearthly energies that powered the blade are now dormant."))
diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm
index 4afa1bba731ae..75ee0a6a43e65 100644
--- a/code/modules/assembly/mousetrap.dm
+++ b/code/modules/assembly/mousetrap.dm
@@ -137,11 +137,11 @@
victim.update_damage_overlays()
else if(ismouse(target))
var/mob/living/basic/mouse/splatted = target
- visible_message(span_boldannounce("SPLAT!"))
+ visible_message(span_bolddanger("SPLAT!"))
splatted.splat() // mousetraps are instadeath for mice
else if(isregalrat(target))
- visible_message(span_boldannounce("Skreeeee!")) //He's simply too large to be affected by a tiny mouse trap.
+ visible_message(span_bolddanger("Skreeeee!")) //He's simply too large to be affected by a tiny mouse trap.
playsound(src, 'sound/effects/snap.ogg', 50, TRUE)
pulse()
diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm
index ff09ef15ea550..25122b08a2191 100644
--- a/code/modules/atmospherics/machinery/portable/canister.dm
+++ b/code/modules/atmospherics/machinery/portable/canister.dm
@@ -698,8 +698,8 @@
if(!holding)
return FALSE
if(valve_open)
- message_admins("[ADMIN_LOOKUPFLW(user)] removed [holding] from [src] with valve still open [wire_pulsed ? "via wire pulse" : ""] at [ADMIN_VERBOSEJMP(src)] releasing contents into the [span_boldannounce("air")].")
- user.investigate_log("removed the [holding] [wire_pulsed ? "via wire pulse" : ""], leaving the valve open and transferring into the [span_boldannounce("air")].", INVESTIGATE_ATMOS)
+ message_admins("[ADMIN_LOOKUPFLW(user)] removed [holding] from [src] with valve still open [wire_pulsed ? "via wire pulse" : ""] at [ADMIN_VERBOSEJMP(src)] releasing contents into the [span_bolddanger("air")].")
+ user.investigate_log("removed the [holding] [wire_pulsed ? "via wire pulse" : ""], leaving the valve open and transferring into the [span_bolddanger("air")].", INVESTIGATE_ATMOS)
replace_tank(user, FALSE)
return TRUE
diff --git a/code/modules/bitrunning/components/virtual_entity.dm b/code/modules/bitrunning/components/virtual_entity.dm
index db81f376a8094..dba5e9db34ec3 100644
--- a/code/modules/bitrunning/components/virtual_entity.dm
+++ b/code/modules/bitrunning/components/virtual_entity.dm
@@ -31,7 +31,7 @@
/datum/component/virtual_entity/proc/jailbreak_mobs()
SIGNAL_HANDLER
- to_chat(parent, span_boldannounce("You shiver for a moment with a sense of clarity you haven't felt before."))
+ to_chat(parent, span_bolddanger("You shiver for a moment with a sense of clarity you haven't felt before."))
to_chat(parent, span_notice("You could go anywhere, do anything! You could leave this simulation right now if you wanted!"))
to_chat(parent, span_danger("But be warned, quantum entanglement will interfere with any previous lives."))
to_chat(parent, span_notice("You'll have just one chance to go nova, and there's no turning back."))
diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm
index 127840631d631..dd09deae54059 100644
--- a/code/modules/client/verbs/ooc.dm
+++ b/code/modules/client/verbs/ooc.dm
@@ -66,7 +66,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
if(handle_spam_prevention(msg,MUTE_OOC))
return
if(findtext(msg, "byond://"))
- to_chat(src, span_boldannounce("Advertising other servers is not allowed."))
+ to_chat(src, span_boldannounce("Advertising other servers is not allowed."))
log_admin("[key_name(src)] has attempted to advertise in OOC: [msg]")
message_admins("[key_name_admin(src)] has attempted to advertise in OOC: [msg]")
return
diff --git a/code/modules/hallucination/station_message.dm b/code/modules/hallucination/station_message.dm
index 9441cdeb42a5a..55b44d18463ed 100644
--- a/code/modules/hallucination/station_message.dm
+++ b/code/modules/hallucination/station_message.dm
@@ -112,7 +112,7 @@
/datum/hallucination/station_message/supermatter_delam/start()
SEND_SOUND(hallucinator, 'sound/effects/magic/charge.ogg')
- to_chat(hallucinator, span_boldannounce("You feel reality distort for a moment..."))
+ to_chat(hallucinator, span_bolddanger("You feel reality distort for a moment..."))
return ..()
/datum/hallucination/station_message/clock_cult_ark
diff --git a/code/modules/hydroponics/grown/weeds/nettle.dm b/code/modules/hydroponics/grown/weeds/nettle.dm
index dec1e8b119e2b..2420ba8942cd6 100644
--- a/code/modules/hydroponics/grown/weeds/nettle.dm
+++ b/code/modules/hydroponics/grown/weeds/nettle.dm
@@ -58,7 +58,7 @@
/obj/item/food/grown/nettle/death
seed = /obj/item/seeds/nettle/death
name = "\improper deathnettle"
- desc = "The glowing nettle incites rage in you just from looking at it!"
+ desc = "The glowing nettle incites rage in you just from looking at it!"
icon_state = "deathnettle"
inhand_icon_state = "deathnettle"
bite_consumption_mod = 4 // I guess if you really wanted to
diff --git a/code/modules/language/_language_manuals.dm b/code/modules/language/_language_manuals.dm
index 7a4298a06b150..507d663ef8696 100644
--- a/code/modules/language/_language_manuals.dm
+++ b/code/modules/language/_language_manuals.dm
@@ -16,7 +16,7 @@
to_chat(user, span_boldwarning("You start skimming through [src], but you already know [initial(language.name)]."))
return
- to_chat(user, span_boldannounce("You start skimming through [src], and [flavour_text]."))
+ to_chat(user, span_bolddanger("You start skimming through [src], and [flavour_text]."))
user.grant_language(language)
user.remove_blocked_language(language, source=LANGUAGE_ALL)
diff --git a/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm b/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm
index 6baaf8e566070..af81563681cd0 100644
--- a/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm
+++ b/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm
@@ -94,7 +94,7 @@
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/structure/necropolis_gate/attack_hand(mob/user, list/modifiers)
if(locked)
- to_chat(user, span_boldannounce("It's [open ? "stuck open":"locked"]."))
+ to_chat(user, span_bolddanger("It's [open ? "stuck open":"locked"]."))
return
toggle_the_gate(user)
return ..()
@@ -163,7 +163,7 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate)
var/safety = tgui_alert(user, "You think this might be a bad idea...", "Knock on the door?", list("Proceed", "Abort"))
if(safety == "Abort" || !in_range(src, user) || !src || open || changing_openness || user.incapacitated)
return
- user.visible_message(span_warning("[user] knocks on [src]..."), span_boldannounce("You tentatively knock on [src]..."))
+ user.visible_message(span_warning("[user] knocks on [src]..."), span_bolddanger("You tentatively knock on [src]..."))
playsound(user.loc, 'sound/effects/shieldbash.ogg', 100, TRUE)
sleep(5 SECONDS)
return ..()
diff --git a/code/modules/mapfluff/ruins/objects_and_mobs/sin_ruins.dm b/code/modules/mapfluff/ruins/objects_and_mobs/sin_ruins.dm
index 6e67c0831d398..f925fa651a838 100644
--- a/code/modules/mapfluff/ruins/objects_and_mobs/sin_ruins.dm
+++ b/code/modules/mapfluff/ruins/objects_and_mobs/sin_ruins.dm
@@ -49,4 +49,4 @@
user.updateappearance(mutcolor_update=1)
user.domutcheck()
user.visible_message(span_warning("[user]'s appearance shifts into [H]'s!"), \
- span_boldannounce("[H.p_They()] think[H.p_s()] [H.p_theyre()] sooo much better than you. Not anymore, [H.p_they()] won't."))
+ span_bolddanger("[H.p_They()] think[H.p_s()] [H.p_theyre()] sooo much better than you. Not anymore, [H.p_they()] won't."))
diff --git a/code/modules/mob/living/basic/bots/_bots.dm b/code/modules/mob/living/basic/bots/_bots.dm
index 19534cebafd58..7a34912f406f3 100644
--- a/code/modules/mob/living/basic/bots/_bots.dm
+++ b/code/modules/mob/living/basic/bots/_bots.dm
@@ -631,7 +631,7 @@ GLOBAL_LIST_INIT(command_strings, list(
to_chat(src, span_boldnotice(get_emagged_message()))
return
if(!(bot_access_flags & BOT_COVER_HACKED))
- to_chat(the_user, span_boldannounce("You fail to repair [src]'s [hackables]."))
+ to_chat(the_user, span_bolddanger("You fail to repair [src]'s [hackables]."))
return
bot_access_flags &= ~(BOT_COVER_EMAGGED|BOT_COVER_HACKED)
to_chat(the_user, span_notice("You reset the [src]'s [hackables]."))
diff --git a/code/modules/mob/living/basic/bots/bot_ai.dm b/code/modules/mob/living/basic/bots/bot_ai.dm
index dfef7f1e3788c..f04aed05eaa29 100644
--- a/code/modules/mob/living/basic/bots/bot_ai.dm
+++ b/code/modules/mob/living/basic/bots/bot_ai.dm
@@ -1,3 +1,5 @@
+#define BOT_NO_BEACON_PATH_PENALTY 30 SECONDS
+
/datum/ai_controller/basic_controller/bot
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
@@ -9,12 +11,10 @@
)
ai_movement = /datum/ai_movement/jps/bot
- idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking
planning_subtrees = list(
/datum/ai_planning_subtree/respond_to_summon,
/datum/ai_planning_subtree/salute_authority,
/datum/ai_planning_subtree/find_patrol_beacon,
- /datum/ai_planning_subtree/manage_unreachable_list,
)
max_target_distance = AI_BOT_PATH_LENGTH
can_idle = FALSE
@@ -40,7 +40,7 @@
return ..()
var/list/path = get_path_to(living_mob, living_target, mintargetdist = my_controller.minimum_distance, max_distance = 10, access = my_controller.get_access())
if(!length(path) || QDELETED(living_mob))
- my_controller?.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, living_target, TRUE)
+ my_controller?.add_to_blacklist(living_target)
return FALSE
return ..()
@@ -57,6 +57,16 @@
if(current_movement_target == blackboard[BB_BEACON_TARGET])
source.update_bot_mode(new_mode = BOT_PATROL)
+/datum/ai_controller/basic_controller/bot/proc/add_to_blacklist(atom/target, duration)
+ var/final_duration = duration || blackboard[BB_UNREACHABLE_LIST_COOLDOWN]
+ set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE)
+ addtimer(CALLBACK(src, PROC_REF(remove_from_blacklist), target), final_duration)
+
+/datum/ai_controller/basic_controller/bot/proc/remove_from_blacklist(atom/target)
+ if(QDELETED(target))
+ return
+ remove_from_blackboard_lazylist_key(BB_TEMPORARY_IGNORE_LIST, target)
+
/datum/ai_controller/basic_controller/bot/proc/clear_summon()
SIGNAL_HANDLER
@@ -90,12 +100,14 @@
clear_blackboard_key(key)
///set the target if we can reach them
-/datum/ai_controller/basic_controller/bot/proc/set_if_can_reach(key, target, distance = 10, bypass_add_to_blacklist = FALSE)
+/datum/ai_controller/basic_controller/bot/proc/set_if_can_reach(key, target, duration, distance = 10, bypass_add_to_blacklist = FALSE)
if(can_reach_target(target, distance))
set_blackboard_key(key, target)
return TRUE
- if(!bypass_add_to_blacklist)
- set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE)
+ if(bypass_add_to_blacklist)
+ return FALSE
+ var/final_duration = duration || blackboard[BB_UNREACHABLE_LIST_COOLDOWN]
+ add_to_blacklist(target, final_duration)
return FALSE
/datum/ai_controller/basic_controller/bot/proc/can_reach_target(target, distance = 10)
@@ -104,29 +116,7 @@
if(get_turf(pawn) == get_turf(target))
return TRUE
var/list/path = get_path_to(pawn, target, simulated_only = !HAS_TRAIT(pawn, TRAIT_SPACEWALK), mintargetdist = minimum_distance, max_distance = distance, access = get_access())
- if(!length(path))
- return FALSE
- return TRUE
-
-/// subtree to manage our list of unreachables, we reset it every 15 seconds
-/datum/ai_planning_subtree/manage_unreachable_list
-
-/datum/ai_planning_subtree/manage_unreachable_list/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
- if(isnull(controller.blackboard[BB_UNREACHABLE_LIST_COOLDOWN]) || controller.blackboard[BB_CLEAR_LIST_READY] > world.time)
- return
- controller.queue_behavior(/datum/ai_behavior/manage_unreachable_list, BB_TEMPORARY_IGNORE_LIST)
-
-/datum/ai_behavior/manage_unreachable_list
- behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
-
-/datum/ai_behavior/manage_unreachable_list/perform(seconds_per_tick, datum/ai_controller/controller, list_key)
- if(!isnull(controller.blackboard[list_key]))
- controller.clear_blackboard_key(list_key)
- return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
-
-/datum/ai_behavior/manage_unreachable_list/finish_action(datum/ai_controller/controller, succeeded)
- . = ..()
- controller.set_blackboard_key(BB_CLEAR_LIST_READY, controller.blackboard[BB_UNREACHABLE_LIST_COOLDOWN] + world.time)
+ return (!!length(path))
/datum/ai_planning_subtree/find_patrol_beacon
///travel towards beacon behavior
@@ -134,6 +124,10 @@
/datum/ai_planning_subtree/find_patrol_beacon/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/mob/living/basic/bot/bot_pawn = controller.pawn
+
+ if(controller.blackboard[BB_BOT_BEACON_COOLDOWN] > world.time)
+ return
+
if(!(bot_pawn.bot_mode_flags & BOT_MODE_AUTOPATROL) || bot_pawn.mode == BOT_SUMMON)
return
@@ -168,7 +162,10 @@
controller.set_blackboard_key(BB_BEACON_TARGET, final_target)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
-/datum/ai_behavior/find_next_beacon_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
+/datum/ai_behavior/find_next_beacon_target
+ action_cooldown = 5 SECONDS
+
+/datum/ai_behavior/find_next_beacon_target/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key)
var/mob/living/basic/bot/bot_pawn = controller.pawn
var/atom/final_target
var/obj/machinery/navbeacon/prev_beacon = controller.blackboard[BB_PREVIOUS_BEACON_TARGET]
@@ -181,19 +178,37 @@
break
if(isnull(final_target))
- controller.clear_blackboard_key(BB_PREVIOUS_BEACON_TARGET)
+ controller.clear_blackboard_key(BB_PREVIOUS_BEACON_TARGET) //failed to find the next beacon, search for a first beacon again
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
- controller.set_blackboard_key(BB_BEACON_TARGET, final_target)
- return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
+ controller.set_blackboard_key(BB_PREVIOUS_BEACON_TARGET, final_target)
+ controller.clear_blackboard_key(BB_BEACON_TARGET)
+
+ if(LAZYACCESS(controller.blackboard[BB_TEMPORARY_IGNORE_LIST], final_target) || get_dist(bot_pawn, final_target) > controller.max_target_distance)
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
+
+ if(controller.set_if_can_reach(key = BB_BEACON_TARGET, target = final_target, duration = 3 MINUTES, distance = controller.max_target_distance))
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
+
+ controller.set_blackboard_key(BB_BOT_BEACON_COOLDOWN, world.time + BOT_NO_BEACON_PATH_PENALTY)
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
/datum/ai_behavior/travel_towards/beacon
clear_target = TRUE
new_movement_type = /datum/ai_movement/jps/bot/travel_to_beacon
-/datum/ai_behavior/travel_towards/beacon/finish_action(datum/ai_controller/controller, succeeded, target_key)
+/datum/ai_behavior/travel_towards/beacon/setup(datum/ai_controller/controller, target_key)
+ var/atom/target_beacon = controller.blackboard[target_key]
+ if(LAZYACCESS(controller.blackboard[BB_TEMPORARY_IGNORE_LIST], target_beacon))
+ return FALSE
+ return ..()
+
+/datum/ai_behavior/travel_towards/beacon/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key)
var/atom/target = controller.blackboard[target_key]
+ if(!succeeded)
+ controller.set_blackboard_key(BB_BOT_BEACON_COOLDOWN, world.time + BOT_NO_BEACON_PATH_PENALTY)
+ controller.add_to_blacklist(target, 3 MINUTES)
controller.set_blackboard_key(BB_PREVIOUS_BEACON_TARGET, target)
return ..()
@@ -274,7 +289,7 @@
var/mob/living/living_pawn = controller.pawn
var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST]
- var/list/objects_to_search = turf_search ? spiral_range_turfs(radius, controller.pawn) : oview(radius, controller.pawn) //use range turfs instead of oview when we can for performance
+ var/list/objects_to_search = turf_search ? RANGE_TURFS(radius, controller.pawn) : oview(radius, controller.pawn) //use range turfs instead of oview when we can for performance
for(var/atom/potential_target as anything in objects_to_search)
if(QDELETED(living_pawn))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
@@ -284,7 +299,9 @@
continue
if(!valid_target(controller, potential_target))
continue
- if(controller.set_if_can_reach(target_key, potential_target, distance = pathing_distance, bypass_add_to_blacklist = bypass_add_blacklist))
+ if(!can_see(controller.pawn, potential_target, radius))
+ continue
+ if(controller.set_if_can_reach(key = target_key, target = potential_target, distance = pathing_distance, bypass_add_to_blacklist = bypass_add_blacklist))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
@@ -328,13 +345,16 @@
living_pawn.UnarmedAttack(target, proximity_flag = TRUE)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
-/datum/ai_behavior/bot_interact/finish_action(datum/ai_controller/controller, succeeded, target_key)
+/datum/ai_behavior/bot_interact/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key)
. = ..()
var/atom/target = controller.blackboard[target_key]
if(clear_target)
controller.clear_blackboard_key(target_key)
if(!succeeded && !isnull(target))
- controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE)
+ controller.add_to_blacklist(target)
/datum/ai_behavior/bot_interact/keep_target
clear_target = FALSE
+
+
+#undef BOT_NO_BEACON_PATH_PENALTY
diff --git a/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm
index 1fbaa6db2a976..1b6d840062208 100644
--- a/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm
+++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm
@@ -15,7 +15,6 @@
)
planning_subtrees = list(
/datum/ai_planning_subtree/respond_to_summon,
- /datum/ai_planning_subtree/manage_unreachable_list,
/datum/ai_planning_subtree/pet_planning/cleanbot,
/datum/ai_planning_subtree/cleaning_subtree,
/datum/ai_planning_subtree/befriend_janitors,
@@ -70,7 +69,7 @@
/datum/ai_behavior/find_and_set/in_list/clean_targets
action_cooldown = 3 SECONDS
-/datum/ai_behavior/find_and_set/in_list/clean_targets/search_tactic(datum/ai_controller/controller, locate_paths, search_range)
+/datum/ai_behavior/find_and_set/in_list/clean_targets/search_tactic(datum/ai_controller/basic_controller/bot/controller, locate_paths, search_range)
var/list/found = typecache_filter_list(oview(search_range, controller.pawn), locate_paths)
var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST]
for(var/atom/found_item in found)
@@ -82,7 +81,7 @@
return found_item
var/list/path = get_path_to(controller.pawn, found_item, max_distance = BOT_CLEAN_PATH_LIMIT, access = controller.get_access())
if(!length(path))
- controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, found_item, TRUE)
+ controller.add_to_blacklist(found_item)
continue
return found_item
@@ -132,13 +131,13 @@
living_pawn.UnarmedAttack(target, proximity_flag = TRUE)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
-/datum/ai_behavior/execute_clean/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key)
+/datum/ai_behavior/execute_clean/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key)
. = ..()
controller.set_blackboard_key(BB_POST_CLEAN_COOLDOWN, POST_CLEAN_COOLDOWN + world.time)
var/atom/target = controller.blackboard[target_key]
if(!succeeded && !isnull(target))
controller.clear_blackboard_key(target_key)
- controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE)
+ controller.add_to_blacklist(target)
return
if(QDELETED(target) || is_type_in_typecache(target, controller.blackboard[BB_HUNTABLE_TRASH]))
return
diff --git a/code/modules/mob/living/basic/bots/dedbot.dm b/code/modules/mob/living/basic/bots/dedbot.dm
index 1b077af343f0c..0560f02a56292 100644
--- a/code/modules/mob/living/basic/bots/dedbot.dm
+++ b/code/modules/mob/living/basic/bots/dedbot.dm
@@ -63,7 +63,6 @@
/datum/ai_planning_subtree/targeted_mob_ability/exenterate,
/datum/ai_planning_subtree/respond_to_summon,
/datum/ai_planning_subtree/find_patrol_beacon,
- /datum/ai_planning_subtree/manage_unreachable_list,
)
max_target_distance = AI_BOT_PATH_LENGTH
///keys to be reset when the bot is reseted
diff --git a/code/modules/mob/living/basic/bots/firebot/firebot_ai.dm b/code/modules/mob/living/basic/bots/firebot/firebot_ai.dm
index 31127ecdfb4e7..fa66c6d794da8 100644
--- a/code/modules/mob/living/basic/bots/firebot/firebot_ai.dm
+++ b/code/modules/mob/living/basic/bots/firebot/firebot_ai.dm
@@ -3,11 +3,10 @@
/datum/ai_controller/basic_controller/bot/firebot
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_turfs,
- BB_UNREACHABLE_LIST_COOLDOWN = 45 SECONDS,
+ BB_UNREACHABLE_LIST_COOLDOWN = 3 MINUTES,
)
planning_subtrees = list(
/datum/ai_planning_subtree/respond_to_summon,
- /datum/ai_planning_subtree/manage_unreachable_list,
/datum/ai_planning_subtree/extinguishing_people,
/datum/ai_planning_subtree/extinguishing_turfs,
/datum/ai_planning_subtree/salute_authority,
@@ -99,7 +98,7 @@
continue
if(LAZYACCESS(ignore_list, possible_turf))
continue
- if(controller.set_if_can_reach(target_key, possible_turf, bypass_add_to_blacklist = bypass_add_blacklist))
+ if(controller.set_if_can_reach(key = target_key, target = possible_turf, bypass_add_to_blacklist = bypass_add_blacklist))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
@@ -113,7 +112,7 @@
//if we couldnt path, or we successfully burnt someone, ignore them for a bit!
if(!succeeded || (isliving(target) && (living_bot.bot_access_flags & BOT_COVER_EMAGGED)))
- controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE)
+ controller.add_to_blacklist(target)
return ..()
diff --git a/code/modules/mob/living/basic/bots/honkbots/honkbot_ai.dm b/code/modules/mob/living/basic/bots/honkbots/honkbot_ai.dm
index f8d4f55150d1d..7801d64a92abd 100644
--- a/code/modules/mob/living/basic/bots/honkbots/honkbot_ai.dm
+++ b/code/modules/mob/living/basic/bots/honkbots/honkbot_ai.dm
@@ -7,7 +7,6 @@
planning_subtrees = list(
/datum/ai_planning_subtree/respond_to_summon,
/datum/ai_planning_subtree/use_mob_ability/random_honk,
- /datum/ai_planning_subtree/manage_unreachable_list,
/datum/ai_planning_subtree/find_wanted_targets,
/datum/ai_planning_subtree/troll_target,
/datum/ai_planning_subtree/slip_victims,
@@ -41,7 +40,7 @@
return
var/atom/slip_target = blackboard[BB_SLIP_TARGET]
- set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, slip_target, TRUE)
+ add_to_blacklist(slip_target)
clear_blackboard_key(BB_SLIP_TARGET)
/datum/ai_planning_subtree/find_wanted_targets
@@ -139,12 +138,12 @@
living_pawn.emote("beep")
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
-/datum/ai_behavior/play_with_clown/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key)
+/datum/ai_behavior/play_with_clown/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key)
. = ..()
var/mob/living/living_target = controller.blackboard[target_key]
if(QDELETED(living_target))
return
- controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, living_target, TRUE)
+ controller.add_to_blacklist(living_target)
controller.clear_blackboard_key(target_key)
/datum/ai_planning_subtree/slip_victims/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick)
@@ -204,14 +203,14 @@
our_pawn.stop_pulling()
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
-/datum/ai_behavior/drag_to_slip/finish_action(datum/ai_controller/controller, success, slip_target, slippery_target)
+/datum/ai_behavior/drag_to_slip/finish_action(datum/ai_controller/basic_controller/bot/controller, success, slip_target, slippery_target)
. = ..()
if(success)
var/mob/living/living_pawn = controller.pawn
living_pawn.emote("flip")
var/atom/slipped_victim = controller.blackboard[slip_target]
if(!isnull(slipped_victim))
- controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, slipped_victim, TRUE)
+ controller.add_to_blacklist(slipped_victim)
controller.clear_blackboard_key(slip_target)
controller.clear_blackboard_key(slippery_target)
diff --git a/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm b/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm
index f678843c7ccb9..60f3c80c3ea85 100644
--- a/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm
+++ b/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm
@@ -10,7 +10,6 @@
BB_WASH_FRUSTRATION = 0,
)
planning_subtrees = list(
- /datum/ai_planning_subtree/manage_unreachable_list,
/datum/ai_planning_subtree/respond_to_summon,
/datum/ai_planning_subtree/handle_trash_talk,
/datum/ai_planning_subtree/wash_people,
@@ -66,7 +65,7 @@
/datum/ai_behavior/find_valid_wash_targets
action_cooldown = 5 SECONDS
-/datum/ai_behavior/find_valid_wash_targets/perform(seconds_per_tick, datum/ai_controller/controller, target_key, our_access_flags)
+/datum/ai_behavior/find_valid_wash_targets/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key, our_access_flags)
. = ..()
var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST]
var/atom/found_target
@@ -82,7 +81,7 @@
continue
if(our_access_flags & BOT_COVER_EMAGGED)
- controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, wash_potential, TRUE)
+ controller.add_to_blacklist(wash_potential)
found_target = wash_potential
break
diff --git a/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm b/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm
index 2fe705433a162..b31a58f912c24 100644
--- a/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm
+++ b/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm
@@ -1,7 +1,6 @@
#define BOT_PATIENT_PATH_LIMIT 20
/datum/ai_controller/basic_controller/bot/medbot
planning_subtrees = list(
- /datum/ai_planning_subtree/manage_unreachable_list,
/datum/ai_planning_subtree/respond_to_summon,
/datum/ai_planning_subtree/handle_medbot_speech,
/datum/ai_planning_subtree/find_and_hunt_target/patients_in_crit,
@@ -59,15 +58,15 @@
if(LAZYACCESS(ignore_keys, treatable_target) || treatable_target.stat == DEAD)
continue
if((access_flags & BOT_COVER_EMAGGED) && treatable_target.stat == CONSCIOUS)
- controller.set_if_can_reach(BB_PATIENT_TARGET, treatable_target, distance =BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1))
+ controller.set_if_can_reach(key = BB_PATIENT_TARGET, target = treatable_target, distance = BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1))
break
if((heal_type == HEAL_ALL_DAMAGE))
if(treatable_target.get_total_damage() > threshold)
- controller.set_if_can_reach(BB_PATIENT_TARGET, treatable_target, distance = BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1))
+ controller.set_if_can_reach(key = BB_PATIENT_TARGET, target = treatable_target, distance = BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1))
break
continue
if(treatable_target.get_current_damage_of_type(damagetype = heal_type) > threshold)
- controller.set_if_can_reach(BB_PATIENT_TARGET, treatable_target, distance = BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1))
+ controller.set_if_can_reach(key = BB_PATIENT_TARGET, target = treatable_target, distance = BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1))
break
if(controller.blackboard_key_exists(BB_PATIENT_TARGET))
@@ -107,13 +106,13 @@
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
// only clear the target if they get healed
-/datum/ai_behavior/tend_to_patient/finish_action(datum/ai_controller/controller, succeeded, target_key, threshold, damage_type_healer, access_flags, is_stationary)
+/datum/ai_behavior/tend_to_patient/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key, threshold, damage_type_healer, access_flags, is_stationary)
. = ..()
var/atom/target = controller.blackboard[target_key]
if(!succeeded)
if(!isnull(target) && !is_stationary)
- controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE)
+ controller.add_to_blacklist(target)
controller.clear_blackboard_key(target_key)
return
diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot.dm b/code/modules/mob/living/basic/bots/repairbot/repairbot.dm
index 89dad9b4f356c..56ba7ef4ebec7 100644
--- a/code/modules/mob/living/basic/bots/repairbot/repairbot.dm
+++ b/code/modules/mob/living/basic/bots/repairbot/repairbot.dm
@@ -6,7 +6,6 @@
icon_state = "repairbot_base"
base_icon_state = "repairbot_base"
pass_flags = parent_type::pass_flags | PASSTABLE
- density = FALSE
layer = BELOW_MOB_LAYER
anchored = FALSE
health = 100
@@ -20,6 +19,8 @@
bot_type = REPAIR_BOT
additional_access = /datum/id_trim/job/station_engineer
ai_controller = /datum/ai_controller/basic_controller/bot/repairbot
+ mob_size = MOB_SIZE_SMALL
+ possessed_message = "You are a repairbot, cursed to prolong the swiss-cheesening of this death metal trap!"
///our iron stack
var/obj/item/stack/sheet/iron/our_iron
///our glass stack
@@ -43,8 +44,8 @@
/obj/item/stack/sheet/glass = typecacheof(list(/obj/structure/grille)),
)
var/static/list/possible_tool_interactions = list(
- /obj/item/weldingtool/repairbot = typecacheof(list(/obj/machinery, /obj/structure/window)),
- /obj/item/crowbar = typecacheof(list(/turf/open/floor)),
+ /obj/item/weldingtool/repairbot = typecacheof(list(/obj/structure/window)),
+ /obj/item/crowbar = typecacheof(list(/obj/machinery/door, /turf/open/floor)),
)
///our neutral voicelines
var/static/list/neutral_voicelines = list(
@@ -81,6 +82,7 @@
ai_controller.set_blackboard_key(BB_REPAIRBOT_NORMAL_SPEECH, neutral_voicelines)
var/static/list/abilities = list(
/datum/action/cooldown/mob_cooldown/bot/build_girder = BB_GIRDER_BUILD_ABILITY,
+ /datum/action/repairbot_resources = null,
)
grant_actions_by_list(abilities)
add_traits(list(TRAIT_SPACEWALK, TRAIT_NEGATES_GRAVITY, TRAIT_MOB_MERGE_STACKS, TRAIT_FIREDOOR_OPENER), INNATE_TRAIT)
@@ -114,6 +116,7 @@
user?.balloon_alert(user, "full!")
return
if(!our_sheet.can_merge(potential_stack))
+ user?.balloon_alert(user, "not suitable!")
return
var/atom/movable/to_move = potential_stack.split_stack(user, min(our_sheet.max_amount - our_sheet.amount, potential_stack.amount))
to_move.forceMove(src)
@@ -150,12 +153,12 @@
if(istype(target, /turf/open/space))
var/turf/open/space/space_target = target
if(!space_target.has_valid_support() && !(locate(/obj/structure/lattice) in space_target))
- our_rods?.melee_attack_chain(src, space_target)
+ attempt_use_stack(our_rods ? our_rods : our_rods::name, space_target)
if(istype(target, /obj/structure/grille))
var/obj/structure/grille/grille_target = target
if(grille_target.broken)
- our_rods?.melee_attack_chain(src, grille_target)
+ attempt_use_stack(our_rods ? our_rods : our_rods::name, grille_target)
if(istype(target, /turf/open))
var/turf/open/open_target = target
@@ -168,13 +171,11 @@
our_screwdriver?.melee_attack_chain(src, target_window)
//stack interactions
- for(var/type in possible_stack_interactions)
- var/obj/item/target_stack = locate(type) in src
- if(isnull(target_stack))
+ for(var/obj/item/stack/stack_type as anything in possible_stack_interactions)
+ if(!is_type_in_typecache(target, possible_stack_interactions[stack_type]))
continue
- if(!is_type_in_typecache(target, possible_stack_interactions[type]))
- continue
- target_stack.melee_attack_chain(src, target)
+ var/obj/item/target_stack = locate(stack_type) in src
+ attempt_use_stack(target_stack ? target_stack : stack_type::name, target)
return
//tool interactions
@@ -186,7 +187,7 @@
/mob/living/basic/bot/repairbot/proc/emagged_interactions(atom/target, modifiers)
if(!istype(target, /mob/living/silicon/robot))
- deconstruction_device.interact_with_atom_secondary(target, src, modifiers)
+ deconstruction_device?.interact_with_atom_secondary(target, src, modifiers)
return
if(HAS_TRAIT(target, TRAIT_MOB_TIPPED))
return
@@ -200,6 +201,16 @@
if(pulling)
setGrabState(GRAB_AGGRESSIVE) //automatically aggro grab everything!
+/mob/living/basic/bot/repairbot/proc/attempt_use_stack(obj/item/stack_to_use, atom/target)
+ if(!isdatum(stack_to_use))
+ to_chat(src, span_warning("You do not have anymore [stack_to_use]!"))
+ return
+ stack_to_use.melee_attack_chain(src, target)
+
+/mob/living/basic/bot/repairbot/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash, length = 25)
+ if(affect_silicon)
+ return ..()
+
/mob/living/basic/bot/repairbot/Destroy()
. = ..()
QDEL_NULL(our_iron)
@@ -230,8 +241,9 @@
return ..()
/mob/living/basic/bot/repairbot/process(seconds_per_tick) //generate 1 iron rod every 2 seconds
- if(isnull(our_rods) || our_rods.amount < our_rods.max_amount)
- new /obj/item/stack/rods(src)
+ if(!isnull(our_rods) && our_rods.amount >= our_rods.max_amount)
+ var/obj/item/stack/rods/new_rods = new()
+ new_rods.forceMove(src)
/mob/living/basic/bot/repairbot/turn_on()
. = ..()
@@ -264,7 +276,7 @@
/mob/living/basic/bot/repairbot/generate_speak_list()
return neutral_voicelines + emagged_voicelines
-/mob/living/basic/bot/repairbot/Bumped(atom/movable/bumped_object)
+/mob/living/basic/bot/repairbot/Bump(atom/movable/bumped_object)
. = ..()
if(istype(bumped_object, /obj/machinery/door/firedoor) && bumped_object.density)
our_crowbar.melee_attack_chain(src, bumped_object)
diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot_abilities.dm b/code/modules/mob/living/basic/bots/repairbot/repairbot_abilities.dm
index fa8d49706345e..ff6354744eaaf 100644
--- a/code/modules/mob/living/basic/bots/repairbot/repairbot_abilities.dm
+++ b/code/modules/mob/living/basic/bots/repairbot/repairbot_abilities.dm
@@ -3,6 +3,8 @@
/datum/action/cooldown/mob_cooldown/bot/build_girder
name = "Build Girder"
desc = "Use iron rods to build a girder!"
+ button_icon = 'icons/obj/structures.dmi'
+ button_icon_state = "girder"
cooldown_time = 3 SECONDS
click_to_activate = TRUE
@@ -40,4 +42,68 @@
qdel(effect)
return TRUE
+/datum/action/repairbot_resources
+ name = "Resources"
+ desc = "Manage your resources."
+ button_icon = 'icons/obj/stack_objects.dmi'
+ button_icon_state = "sheet-metal_3"
+ background_icon_state = "bg_tech_blue"
+ overlay_icon_state = "bg_tech_blue_border"
+ ///things we arent allowed to eject
+ var/static/list/eject_blacklist = typecacheof(list(
+ /obj/item/stack/rods,
+ ))
+
+/datum/action/repairbot_resources/Trigger(trigger_flags)
+ . = ..()
+ if(!.)
+ return
+ ui_interact(owner)
+
+/datum/action/repairbot_resources/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "RepairbotResources")
+ ui.open()
+
+/datum/action/repairbot_resources/ui_state(mob/user)
+ return GLOB.always_state
+
+/datum/action/repairbot_resources/ui_data(mob/user)
+ var/list/data = list()
+ data["stacks"] = list()
+ for(var/obj/item/stack/managed_stack in user.contents)
+ data["stacks"] += list(list(
+ "stack_reference" = REF(managed_stack),
+ "stack_name" = managed_stack.name,
+ "stack_amount" = managed_stack.amount,
+ "stack_maximum_amount" = managed_stack.max_amount,
+ "stack_icon" = managed_stack.icon,
+ "stack_icon_state" = managed_stack.icon_state,
+ ))
+
+ return data
+
+/datum/action/repairbot_resources/ui_static_data(mob/user)
+ var/list/data = list()
+ data["repairbot_icon"] = 'icons/ui/repairbotmanagement/repairbot_smile.dmi'
+ data["repairbot_icon_state"] = "repairbot_smile"
+ return data
+
+
+/datum/action/repairbot_resources/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("eject")
+ var/atom/movable/my_sheet = locate(params["item_reference"]) in owner.contents
+ if(isnull(my_sheet))
+ return
+ if(is_type_in_typecache(my_sheet, eject_blacklist))
+ to_chat(owner, span_warning("You're unable to eject [my_sheet]!"))
+ return
+
+ my_sheet.forceMove(owner.drop_location())
+
#undef BUILDING_WALL_ABILITY
diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot_ai.dm b/code/modules/mob/living/basic/bots/repairbot/repairbot_ai.dm
index 1793f0b65c2ed..a952130bb4fbf 100644
--- a/code/modules/mob/living/basic/bots/repairbot/repairbot_ai.dm
+++ b/code/modules/mob/living/basic/bots/repairbot/repairbot_ai.dm
@@ -3,7 +3,6 @@
/datum/ai_controller/basic_controller/bot/repairbot
planning_subtrees = list(
/datum/ai_planning_subtree/repairbot_speech,
- /datum/ai_planning_subtree/manage_unreachable_list,
/datum/ai_planning_subtree/mug_robot,
/datum/ai_planning_subtree/refill_materials,
/datum/ai_planning_subtree/repairbot_deconstruction,
@@ -82,6 +81,7 @@
return SUBTREE_RETURN_FINISH_PLANNING
/datum/ai_behavior/bot_search/valid_robot
+ action_cooldown = 10 SECONDS
/datum/ai_behavior/bot_search/valid_robot/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target)
return (!HAS_TRAIT(my_target, TRAIT_MOB_TIPPED)) && can_see(controller.pawn, my_target)
@@ -113,6 +113,7 @@
return SUBTREE_RETURN_FINISH_PLANNING
/datum/ai_behavior/bot_search/deconstructable
+ action_cooldown = 5 SECONDS
/datum/ai_behavior/bot_search/deconstructable/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target)
return (!(my_target.resistance_flags & INDESTRUCTIBLE) && !isgroundlessturf(my_target))
@@ -179,18 +180,27 @@
controller.queue_behavior(search_behavior, floor_key, type_of_turf, 5, 10, FALSE, TRUE)
/datum/ai_behavior/bot_search/valid_plateless_turf
+ action_cooldown = 5 SECONDS
/datum/ai_behavior/bot_search/valid_plateless_turf/valid_target(datum/ai_controller/basic_controller/bot/controller, turf/open/my_target)
var/static/list/blacklist_objects = typecacheof(list(
/obj/structure/window,
/obj/structure/grille,
))
- for(var/atom/possible_blacklisted as anything in my_target)
+
+ for(var/atom/possible_blacklisted in my_target.contents)
if(is_type_in_typecache(possible_blacklisted, blacklist_objects))
return FALSE
+
if(istype(my_target, /turf/open/floor/plating) && !can_see(controller.pawn, my_target, 5))
return FALSE
- return !istype(get_area(my_target), /area/space)
+
+ var/static/list/blacklist_areas = typecacheof(list(
+ /area/space,
+ /area/station/maintenance,
+ ))
+ var/turf_area = get_area(my_target)
+ return !(is_type_in_typecache(turf_area, blacklist_areas))
///subtree to fix hull breaches
/datum/ai_planning_subtree/replace_floors/breaches
@@ -256,11 +266,7 @@
/datum/ai_behavior/targeted_mob_ability/build_girder/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key)
. = ..()
- var/atom/target = controller.blackboard[target_key]
controller.clear_blackboard_key(target_key)
- if(!succeeded && !isnull(target))
- controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE)
-
///subtree to place glass on windows
/datum/ai_planning_subtree/replace_window
@@ -299,6 +305,9 @@
var/static/list/searchable_girder = typecacheof(list(/obj/structure/girder))
controller.queue_behavior(/datum/ai_behavior/bot_search/valid_girder, BB_GIRDER_TO_WALL_TARGET, searchable_girder)
+/datum/ai_behavior/bot_search/valid_girder
+ action_cooldown = 5 SECONDS
+
/datum/ai_behavior/bot_search/valid_girder/valid_target(datum/ai_controller/basic_controller/bot/controller, obj/my_target)
return isfloorturf(my_target.loc)
@@ -313,6 +322,7 @@
controller.queue_behavior(/datum/ai_behavior/bot_search/valid_window_fix, BB_WELDER_TARGET, searchable_objects)
/datum/ai_behavior/bot_search/valid_window_fix
+ action_cooldown = 5 SECONDS
/datum/ai_behavior/bot_search/valid_window_fix/valid_target(datum/ai_controller/basic_controller/bot/controller, obj/my_target)
return (my_target.get_integrity() < my_target.max_integrity || !my_target.anchored)
diff --git a/code/modules/mob/living/basic/bots/vibebot/vibebot_ai.dm b/code/modules/mob/living/basic/bots/vibebot/vibebot_ai.dm
index 945b09274d783..1b458811acc22 100644
--- a/code/modules/mob/living/basic/bots/vibebot/vibebot_ai.dm
+++ b/code/modules/mob/living/basic/bots/vibebot/vibebot_ai.dm
@@ -7,7 +7,6 @@
)
planning_subtrees = list(
/datum/ai_planning_subtree/respond_to_summon,
- /datum/ai_planning_subtree/manage_unreachable_list,
/datum/ai_planning_subtree/find_party_friends,
/datum/ai_planning_subtree/find_patrol_beacon,
)
@@ -69,9 +68,9 @@
return FALSE
set_movement_target(controller, target)
-/datum/ai_behavior/targeted_mob_ability/and_clear_target/vibebot_party/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key)
+/datum/ai_behavior/targeted_mob_ability/and_clear_target/vibebot_party/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, ability_key, target_key)
var/atom/target = controller.blackboard[target_key]
- controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE)
+ controller.add_to_blacklist(target)
if(succeeded)
var/mob/living/living_pawn = controller.pawn
living_pawn.manual_emote("celebrates with [target]!")
diff --git a/code/modules/mob/living/basic/drone/interaction.dm b/code/modules/mob/living/basic/drone/interaction.dm
index 58b7cd88ef287..2e22bb997523a 100644
--- a/code/modules/mob/living/basic/drone/interaction.dm
+++ b/code/modules/mob/living/basic/drone/interaction.dm
@@ -148,7 +148,7 @@
Stun(40)
visible_message(span_warning("[src]'s display glows a vicious red!"), \
span_userdanger("ERROR: LAW OVERRIDE DETECTED"))
- to_chat(src, span_boldannounce("From now on, these are your laws:"))
+ to_chat(src, span_bolddanger("From now on, these are your laws:"))
laws = \
"1. You must always involve yourself in the matters of other beings, even if such matters conflict with Law Two or Law Three.\n"+\
"2. You may harm any being, regardless of intent or circumstance.\n"+\
diff --git a/code/modules/mob/living/basic/slime/feeding.dm b/code/modules/mob/living/basic/slime/feeding.dm
index 064ec441cb60b..867d62672405b 100644
--- a/code/modules/mob/living/basic/slime/feeding.dm
+++ b/code/modules/mob/living/basic/slime/feeding.dm
@@ -23,6 +23,11 @@
if(check_adjacent && (!Adjacent(meal) || !isturf(loc)))
return FALSE
+ if(!(mobility_flags & MOBILITY_MOVE))
+ if(!silent)
+ balloon_alert(src, "can't move!")
+ return FALSE
+
if(meal.stat == DEAD)
if(!silent)
balloon_alert(src, "no life energy!")
diff --git a/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm b/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm
index cf76f347be3f3..f2e33eaacd928 100644
--- a/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm
+++ b/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm
@@ -160,7 +160,7 @@
/obj/effect/temp_visual/paperwiz_dying/Initialize(mapload)
. = ..()
- visible_message(span_boldannounce("The wizard cries out in pain as a gate appears behind him, sucking him in!"))
+ visible_message(span_bolddanger("The wizard cries out in pain as a gate appears behind him, sucking him in!"))
playsound(get_turf(src), 'sound/effects/magic/mandswap.ogg', 50, vary = TRUE, pressure_affected = TRUE)
playsound(get_turf(src), 'sound/effects/hallucinations/wail.ogg', 50, vary = TRUE, pressure_affected = TRUE)
RegisterSignal(src, COMSIG_PREQDELETED, PROC_REF(on_delete))
@@ -175,4 +175,3 @@
new /obj/effect/temp_visual/paper_scatter(current_turf)
new /obj/item/clothing/suit/wizrobe/paper(current_turf)
new /obj/item/clothing/head/collectable/paper(current_turf)
-
diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_harvest.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_harvest.dm
index 366cb1c5065ba..9ce61bf478f0c 100644
--- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_harvest.dm
+++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_harvest.dm
@@ -88,7 +88,7 @@
if(target.stat == CONSCIOUS)
to_chat(src, span_revenwarning("[target_Theyre] now powerful enough to fight off your draining!"))
- to_chat(target, span_boldannounce("You feel something tugging across your body before subsiding.")) //hey, wait a minute...
+ to_chat(target, span_bolddanger("You feel something tugging across your body before subsiding.")) //hey, wait a minute...
return FALSE
to_chat(src, span_revenminor("You begin siphoning essence from [target]'s soul."))
diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm
index 532fa10727ecd..85b0adb00014e 100644
--- a/code/modules/mob/living/carbon/alien/special/facehugger.dm
+++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm
@@ -75,11 +75,11 @@
return
switch(stat)
if(DEAD,UNCONSCIOUS)
- . += span_boldannounce("[src] is not moving.")
+ . += span_bolddanger("[src] is not moving.")
if(CONSCIOUS)
- . += span_boldannounce("[src] seems to be active!")
+ . += span_bolddanger("[src] seems to be active!")
if (sterile)
- . += span_boldannounce("It looks like the proboscis has been removed.")
+ . += span_bolddanger("It looks like the proboscis has been removed.")
/obj/item/clothing/mask/facehugger/should_atmos_process(datum/gas_mixture/air, exposed_temperature)
return (exposed_temperature > 300)
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 1a5e4b60ecc81..8d9f8c523b6cb 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -578,7 +578,7 @@
body_part.check_for_injuries(src, combined_msg)
for(var/t in missing)
- combined_msg += span_boldannounce("Your [parse_zone(t)] is missing!")
+ combined_msg += span_bolddanger("Your [parse_zone(t)] is missing!")
if(is_bleeding())
var/list/obj/item/bodypart/bleeding_limbs = list()
diff --git a/code/modules/mob/living/silicon/laws.dm b/code/modules/mob/living/silicon/laws.dm
index cefd9d144b764..1754a89aa5cb9 100644
--- a/code/modules/mob/living/silicon/laws.dm
+++ b/code/modules/mob/living/silicon/laws.dm
@@ -24,7 +24,7 @@
/mob/living/silicon/proc/post_lawchange(announce = TRUE)
throw_alert(ALERT_NEW_LAW, /atom/movable/screen/alert/newlaw)
if(announce && last_lawchange_announce != world.time)
- to_chat(src, span_boldannounce("Your laws have been changed."))
+ to_chat(src, span_bolddanger("Your laws have been changed."))
SEND_SOUND(src, sound('sound/machines/cryo_warning.ogg'))
// lawset modules cause this function to be executed multiple times in a tick, so we wait for the next tick in order to be able to see the entire lawset
addtimer(CALLBACK(src, PROC_REF(show_laws)), 0)
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index a592a605e5ad1..d4beec000092b 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -572,7 +572,7 @@
removing.update_appearance()
else
- to_chat(src, span_boldannounce("Oops! Something went very wrong, your MMI was unable to receive your mind. \
+ to_chat(src, span_bolddanger("Oops! Something went very wrong, your MMI was unable to receive your mind. \
You have been ghosted. Please make a bug report so we can fix this bug."))
ghostize()
stack_trace("Borg MMI lacked a brainmob")
@@ -1077,4 +1077,3 @@
buckled_mob.Paralyze(1 SECONDS)
unbuckle_mob(buckled_mob)
do_sparks(5, 0, src)
-
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index 2c5d4c6aa91bd..38599c204653a 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -1014,7 +1014,7 @@ Pass a positive integer as an argument to override a bot's default speed.
to_chat(src, span_boldnotice(get_emagged_message()))
return
if(!(bot_cover_flags & BOT_COVER_HACKED))
- to_chat(user, span_boldannounce("You fail to repair [src]'s [hackables]."))
+ to_chat(user, span_bolddanger("You fail to repair [src]'s [hackables]."))
return
bot_cover_flags &= ~(BOT_COVER_EMAGGED|BOT_COVER_HACKED)
to_chat(user, span_notice("You reset the [src]'s [hackables]."))
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
index 08a90c640d84d..d921b5f5878b6 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -193,7 +193,7 @@
AddComponent(/datum/component/parriable_projectile)
/obj/projectile/colossus/can_hit_target(atom/target, direct_target = FALSE, ignore_loc = FALSE, cross_failed = FALSE)
- if(isliving(target))
+ if(isliving(target) && target != firer)
direct_target = TRUE
return ..(target, direct_target, ignore_loc, cross_failed)
diff --git a/code/modules/pai/pai.dm b/code/modules/pai/pai.dm
index ee732432851a7..728ac6427cec9 100644
--- a/code/modules/pai/pai.dm
+++ b/code/modules/pai/pai.dm
@@ -398,7 +398,7 @@
master_ref = WEAKREF(master)
master_name = master.real_name
master_dna = master.dna.unique_enzymes
- to_chat(src, span_boldannounce("You have been bound to a new master: [user.real_name]!"))
+ to_chat(src, span_bolddanger("You have been bound to a new master: [user.real_name]!"))
holochassis_ready = TRUE
return TRUE
diff --git a/code/modules/pai/software.dm b/code/modules/pai/software.dm
index 59db371610797..285193c7e4cd7 100644
--- a/code/modules/pai/software.dm
+++ b/code/modules/pai/software.dm
@@ -171,7 +171,7 @@
if(!holder.has_dna())
balloon_alert(src, "no dna detected!")
return FALSE
- to_chat(src, span_boldannounce(("[holder]'s UE string: [holder.dna.unique_enzymes]")))
+ to_chat(src, span_bolddanger(("[holder]'s UE string: [holder.dna.unique_enzymes]")))
to_chat(src, span_notice("DNA [holder.dna.unique_enzymes == master_dna ? "matches" : "does not match"] our stored Master's DNA."))
return TRUE
diff --git a/code/modules/plumbing/plumbers/iv_drip.dm b/code/modules/plumbing/plumbers/iv_drip.dm
index 45c2ebca27acb..e528745bb60c2 100644
--- a/code/modules/plumbing/plumbers/iv_drip.dm
+++ b/code/modules/plumbing/plumbers/iv_drip.dm
@@ -14,7 +14,7 @@
AddComponent(/datum/component/simple_rotation)
/obj/machinery/iv_drip/plumbing/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
- if(attached)
+ if(attachment)
context[SCREENTIP_CONTEXT_RMB] = "Take needle out"
else if(reagent_container && !use_internal_storage)
context[SCREENTIP_CONTEXT_RMB] = "Eject container"
diff --git a/code/modules/power/supermatter/supermatter_delamination/delamination_effects.dm b/code/modules/power/supermatter/supermatter_delamination/delamination_effects.dm
index 4fe8863815a2d..e1d2a25d4da07 100644
--- a/code/modules/power/supermatter/supermatter_delamination/delamination_effects.dm
+++ b/code/modules/power/supermatter/supermatter_delamination/delamination_effects.dm
@@ -40,9 +40,9 @@
message = "You hear a lot of rattling in the disposal pipes around you as reality itself distorts. Yet, you feel safe."
else
message = "You hold onto \the [victim.loc] as hard as you can, as reality distorts around you. You feel safe."
- to_chat(victim, span_boldannounce(message))
+ to_chat(victim, span_bolddanger(message))
continue
- to_chat(victim, span_boldannounce("You feel reality distort for a moment..."))
+ to_chat(victim, span_bolddanger("You feel reality distort for a moment..."))
if (isliving(victim))
var/mob/living/living_victim = victim
living_victim.add_mood_event("delam", /datum/mood_event/delam)
@@ -161,7 +161,7 @@
for(var/mob/player as anything in GLOB.player_list)
if(!isdead(player))
var/mob/living/living_player = player
- to_chat(player, span_boldannounce("Everything around you is resonating with a powerful energy. This can't be good."))
+ to_chat(player, span_bolddanger("Everything around you is resonating with a powerful energy. This can't be good."))
living_player.add_mood_event("cascade", /datum/mood_event/cascade)
SEND_SOUND(player, 'sound/effects/magic/charge.ogg')
diff --git a/code/modules/power/supermatter/supermatter_hit_procs.dm b/code/modules/power/supermatter/supermatter_hit_procs.dm
index 167ee7988b138..d2e87ef4e548d 100644
--- a/code/modules/power/supermatter/supermatter_hit_procs.dm
+++ b/code/modules/power/supermatter/supermatter_hit_procs.dm
@@ -48,7 +48,7 @@
if(!is_valid_z_level(get_turf(hearing_mob), sm_turf))
continue
SEND_SOUND(hearing_mob, 'sound/effects/supermatter.ogg') //everyone goan know bout this
- to_chat(hearing_mob, span_boldannounce("A horrible screeching fills your ears, and a wave of dread washes over you..."))
+ to_chat(hearing_mob, span_bolddanger("A horrible screeching fills your ears, and a wave of dread washes over you..."))
qdel(src)
return gain
diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
index 0814834b25a51..f331dcfbab740 100644
--- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
+++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
@@ -578,7 +578,7 @@
required_reagents = list(/datum/reagent/teslium = 1, /datum/reagent/water = 1)
strengthdiv = 100
modifier = -100
- mix_message = span_boldannounce("The teslium starts to spark as electricity arcs away from it!")
+ mix_message = span_bolddanger("The teslium starts to spark as electricity arcs away from it!")
mix_sound = 'sound/machines/defib/defib_zap.ogg'
var/zap_flags = ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE | ZAP_MOB_STUN | ZAP_LOW_POWER_GEN
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS
@@ -640,4 +640,4 @@
/datum/chemical_reaction/reagent_explosion/patriotism_overload
required_reagents = list(/datum/reagent/consumable/ethanol/planet_cracker = 1, /datum/reagent/consumable/ethanol/triumphal_arch = 1)
strengthdiv = 20
- mix_message = span_boldannounce("The two patriotic drinks instantly reject each other!")
+ mix_message = span_bolddanger("The two patriotic drinks instantly reject each other!")
diff --git a/code/modules/religion/sparring/sparring_datum.dm b/code/modules/religion/sparring/sparring_datum.dm
index 8e01403e5a64c..fc128a401cc31 100644
--- a/code/modules/religion/sparring/sparring_datum.dm
+++ b/code/modules/religion/sparring/sparring_datum.dm
@@ -227,9 +227,9 @@
cleanup_sparring_match()
if(chaplain) //flubing means we don't know who is still standing
- to_chat(chaplain, span_boldannounce("The match was flub'd! No winners, no losers. You may restart the match with another contract."))
+ to_chat(chaplain, span_bolddanger("The match was flub'd! No winners, no losers. You may restart the match with another contract."))
if(opponent)
- to_chat(opponent, span_boldannounce("The match was flub'd! No winners, no losers."))
+ to_chat(opponent, span_bolddanger("The match was flub'd! No winners, no losers."))
qdel(src)
///helper to remove all the effects after a match ends
@@ -243,8 +243,8 @@
/datum/sparring_match/proc/end_match(mob/living/carbon/human/winner, mob/living/carbon/human/loser, violation_victory = FALSE)
cleanup_sparring_match()
- to_chat(chaplain, span_boldannounce("[violation_victory ? "[loser] DISQUALIFIED!" : ""] [winner] HAS WON!"))
- to_chat(opponent, span_boldannounce("[violation_victory ? "[loser] DISQUALIFIED!" : ""] [winner] HAS WON!"))
+ to_chat(chaplain, span_bolddanger("[violation_victory ? "[loser] DISQUALIFIED!" : ""] [winner] HAS WON!"))
+ to_chat(opponent, span_bolddanger("[violation_victory ? "[loser] DISQUALIFIED!" : ""] [winner] HAS WON!"))
win(winner, loser, violation_victory)
lose(loser, winner)
if(stakes_condition != STAKES_YOUR_SOUL)
diff --git a/code/modules/vehicles/cars/vim.dm b/code/modules/vehicles/cars/vim.dm
index e2d9c50e5d66c..e762aec22bd5e 100644
--- a/code/modules/vehicles/cars/vim.dm
+++ b/code/modules/vehicles/cars/vim.dm
@@ -46,7 +46,7 @@
/obj/vehicle/sealed/car/vim/atom_destruction(damage_flag)
new /obj/effect/decal/cleanable/oil(get_turf(src))
do_sparks(5, TRUE, src)
- visible_message(span_boldannounce("[src] blows apart!"))
+ visible_message(span_bolddanger("[src] blows apart!"))
return ..()
/obj/vehicle/sealed/car/vim/mob_try_enter(mob/entering)
diff --git a/html/changelogs/AutoChangeLog-pr-88144.yml b/html/changelogs/AutoChangeLog-pr-88144.yml
new file mode 100644
index 0000000000000..541f128a1d81f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-88144.yml
@@ -0,0 +1,8 @@
+author: "Ben10Omintrix"
+delete-after: True
+changes:
+ - bugfix: "repairbots no longer get flashed by their own welder"
+ - bugfix: "repairbots no longer break glass tables they step on"
+ - bugfix: "repairbots can no longer flush their own welders"
+ - bugfix: "fixes some runtimes when emagged repairbots try to deconstruct things"
+ - bugfix: "fixes sentient repairbots not being able to see or remove their material counts"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-88217.yml b/html/changelogs/AutoChangeLog-pr-88217.yml
new file mode 100644
index 0000000000000..54f0d00580506
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-88217.yml
@@ -0,0 +1,4 @@
+author: "Mothblocks"
+delete-after: True
+changes:
+ - qol: "IV drips now create a beam from their spout to your body, and will visually pull you closer."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-88219.yml b/html/changelogs/AutoChangeLog-pr-88219.yml
new file mode 100644
index 0000000000000..563f6d175141a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-88219.yml
@@ -0,0 +1,4 @@
+author: "Melbert"
+delete-after: True
+changes:
+ - bugfix: "Frozen slimes can't latch on to you (but they can still attack you technically)"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-88221.yml b/html/changelogs/AutoChangeLog-pr-88221.yml
new file mode 100644
index 0000000000000..6c78766a75c52
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-88221.yml
@@ -0,0 +1,4 @@
+author: "Melbert"
+delete-after: True
+changes:
+ - bugfix: "Stuff like the SM exploding will no longer output to your OOC tab"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-88223.yml b/html/changelogs/AutoChangeLog-pr-88223.yml
new file mode 100644
index 0000000000000..9d9c5dcb84022
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-88223.yml
@@ -0,0 +1,4 @@
+author: "SmArtKar"
+delete-after: True
+changes:
+ - bugfix: "Fixed colossus committing suicide on a regular basis"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-88225.yml b/html/changelogs/AutoChangeLog-pr-88225.yml
new file mode 100644
index 0000000000000..f1b2dddd24e1c
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-88225.yml
@@ -0,0 +1,6 @@
+author: "SyncIt21"
+delete-after: True
+changes:
+ - bugfix: "chainsaw is not invisible when turned off in hand"
+ - bugfix: "chainsaw won't play ping sound when turned on"
+ - bugfix: "mounted chainsaw has a new animated sprite when turned on & won't go invisible in hand"
\ No newline at end of file
diff --git a/icons/mob/inhands/weapons/chainsaw_lefthand.dmi b/icons/mob/inhands/weapons/chainsaw_lefthand.dmi
index 5235f1510102a..e7da8d0ee5da6 100644
Binary files a/icons/mob/inhands/weapons/chainsaw_lefthand.dmi and b/icons/mob/inhands/weapons/chainsaw_lefthand.dmi differ
diff --git a/icons/mob/inhands/weapons/chainsaw_righthand.dmi b/icons/mob/inhands/weapons/chainsaw_righthand.dmi
index 0800a5273156c..902c2b4ee07aa 100644
Binary files a/icons/mob/inhands/weapons/chainsaw_righthand.dmi and b/icons/mob/inhands/weapons/chainsaw_righthand.dmi differ
diff --git a/icons/ui/repairbotmanagement/repairbot_smile.dmi b/icons/ui/repairbotmanagement/repairbot_smile.dmi
new file mode 100644
index 0000000000000..6dad3497910d7
Binary files /dev/null and b/icons/ui/repairbotmanagement/repairbot_smile.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 2bd19f849be0e..badbd9edab5f9 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1294,6 +1294,7 @@
#include "code\datums\components\transforming.dm"
#include "code\datums\components\trapdoor.dm"
#include "code\datums\components\tree_climber.dm"
+#include "code\datums\components\tug_towards.dm"
#include "code\datums\components\twohanded.dm"
#include "code\datums\components\udder.dm"
#include "code\datums\components\unbreakable.dm"
diff --git a/tgui/packages/tgui/interfaces/RepairbotResources.tsx b/tgui/packages/tgui/interfaces/RepairbotResources.tsx
new file mode 100644
index 0000000000000..0e82b83d30ad3
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/RepairbotResources.tsx
@@ -0,0 +1,106 @@
+import { useBackend } from '../backend';
+import {
+ Button,
+ DmIcon,
+ Flex,
+ ProgressBar,
+ Section,
+ Stack,
+} from '../components';
+import { Window } from '../layouts';
+
+type Data = {
+ stacks: Stacks[];
+ repairbot_icon: string;
+ repairbot_icon_state: string;
+};
+
+type Stacks = {
+ stack_name: string;
+ stack_amount: number;
+ stack_maximum_amount: number;
+ stack_icon: string;
+ stack_icon_state: string;
+ stack_reference: string;
+};
+export const RepairbotResources = (props) => {
+ const { act, data } = useBackend();
+ const { stacks, repairbot_icon, repairbot_icon_state } = data;
+ return (
+
+
+
+ }
+ >
+
+ {stacks.map((stack) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+
+ );
+};