diff --git a/code/__DEFINES/language.dm b/code/__DEFINES/language.dm
index 53704330761..5b1082f606d 100644
--- a/code/__DEFINES/language.dm
+++ b/code/__DEFINES/language.dm
@@ -31,6 +31,7 @@
#define LANGUAGE_MALF "malf"
#define LANGUAGE_PIRATE "pirate"
#define LANGUAGE_MASTER "master"
+#define LANGUAGE_PAI "pai"
#define LANGUAGE_SOFTWARE "software"
#define LANGUAGE_STONER "stoner"
#define LANGUAGE_VOICECHANGE "voicechange"
diff --git a/code/__DEFINES/robots.dm b/code/__DEFINES/robots.dm
index c032018b830..7f7961e05be 100644
--- a/code/__DEFINES/robots.dm
+++ b/code/__DEFINES/robots.dm
@@ -234,3 +234,75 @@ DEFINE_BITFIELD(janitor_mode_flags, list(
#define NAVBEACON_PATROL_NEXT "next_patrol"
#define NAVBEACON_DELIVERY_MODE "delivery"
#define NAVBEACON_DELIVERY_DIRECTION "dir"
+
+// Defines for lines that bots can speak which also have corresponding voice lines
+
+#define ED209_VOICED_DOWN_WEAPONS "Please put down your weapon. You have 20 seconds to comply."
+
+#define HONKBOT_VOICED_HONK_HAPPY "Honk!"
+#define HONKBOT_VOICED_HONK_SAD "Honk..."
+
+#define BEEPSKY_VOICED_CRIMINAL_DETECTED "Criminal detected!"
+#define BEEPSKY_VOICED_FREEZE "Freeze, scumbag!"
+#define BEEPSKY_VOICED_JUSTICE "Prepare for justice!"
+#define BEEPSKY_VOICED_YOUR_MOVE "Your move, creep."
+#define BEEPSKY_VOICED_I_AM_THE_LAW "I am the law!"
+#define BEEPSKY_VOICED_SECURE_DAY "Have a secure day."
+
+#define FIREBOT_VOICED_FIRE_DETECTED "Fire detected!"
+#define FIREBOT_VOICED_STOP_DROP "Stop, drop and roll!"
+#define FIREBOT_VOICED_EXTINGUISHING "Extinguishing!"
+#define FIREBOT_VOICED_NO_FIRES "No fires detected."
+#define FIREBOT_VOICED_ONLY_YOU "Only you can prevent station fires."
+#define FIREBOT_VOICED_TEMPERATURE_NOMINAL "Temperature nominal."
+#define FIREBOT_VOICED_KEEP_COOL "Keep it cool."
+
+#define HYGIENEBOT_VOICED_UNHYGIENIC "Unhygienic client found. Please stand still so I can clean you."
+#define HYGIENEBOT_VOICED_ENJOY_DAY "Enjoy your clean and tidy day!"
+#define HYGIENEBOT_VOICED_THREAT_AIRLOCK "Either you stop running or I will fucking drag you out of an airlock."
+#define HYGIENEBOT_VOICED_FOUL_SMELL "Get back here you foul smelling fucker."
+#define HYGIENEBOT_VOICED_TROGLODYTE "I just want to fucking clean you you troglodyte."
+#define HYGIENEBOT_VOICED_GREEN_CLOUD "If you don't come back here I'll put a green cloud around you cunt."
+#define HYGIENEBOT_VOICED_ARSEHOLE "Just fucking let me clean you you arsehole!"
+#define HYGIENEBOT_VOICED_THREAT_ARTERIES "STOP RUNNING OR I WILL CUT YOUR ARTERIES!"
+#define HYGIENEBOT_VOICED_STOP_RUNNING "STOP. RUNNING."
+#define HYGIENEBOT_VOICED_FUCKING_FINALLY "Fucking finally."
+#define HYGIENEBOT_VOICED_THANK_GOD "Thank god, you finally stopped."
+#define HYGIENEBOT_VOICED_DEGENERATE "Well about fucking time you degenerate."
+
+#define MEDIBOT_VOICED_HOLD_ON "Hey! Hold on, I'm coming."
+#define MEDIBOT_VOICED_WANT_TO_HELP "Wait! I want to help!"
+#define MEDIBOT_VOICED_YOU_ARE_INJURED "You appear to be injured!"
+#define MEDIBOT_VOICED_ALL_PATCHED_UP "All patched up!"
+#define MEDIBOT_VOICED_APPLE_A_DAY "An apple a day keeps me away."
+#define MEDIBOT_VOICED_FEEL_BETTER "Feel better soon!"
+#define MEDIBOT_VOICED_STAY_WITH_ME "No! Stay with me!"
+#define MEDIBOT_VOICED_LIVE "Live, damnit! LIVE!"
+#define MEDIBOT_VOICED_NEVER_LOST "I...I've never lost a patient before. Not today, I mean."
+#define MEDIBOT_VOICED_DELICIOUS "Delicious!"
+#define MEDIBOT_VOICED_PLASTIC_SURGEON "I knew it, I should've been a plastic surgeon."
+#define MEDIBOT_VOICED_MASK_ON "Radar, put a mask on!"
+#define MEDIBOT_VOICED_ALWAYS_A_CATCH "There's always a catch, and I'm the best there is."
+#define MEDIBOT_VOICED_LIKE_FLIES "What kind of medbay is this? Everyone's dropping like flies."
+#define MEDIBOT_VOICED_SUFFER "Why are we still here? Just to suffer?"
+#define MEDIBOT_VOICED_FUCK_YOU "Fuck you."
+#define MEDIBOT_VOICED_NOT_A_GAME "Turn off your computer. This is not a game."
+#define MEDIBOT_VOICED_IM_DIFFERENT "I'm different!"
+#define MEDIBOT_VOICED_FOURTH_WALL "Close Dreamseeker.exe now. Or else."
+#define MEDIBOT_VOICED_SHINDEMASHOU "Shindemashou."
+#define MEDIBOT_VOICED_WAIT "Hey, wait..."
+#define MEDIBOT_VOICED_DONT "Please don't..."
+#define MEDIBOT_VOICED_TRUSTED_YOU "I trusted you..."
+#define MEDIBOT_VOICED_NO_SAD "Nooo..."
+#define MEDIBOT_VOICED_OH_FUCK "Oh fuck-"
+#define MEDIBOT_VOICED_FORGIVE "I forgive you."
+#define MEDIBOT_VOICED_THANKS "Thank you!"
+#define MEDIBOT_VOICED_GOOD_PERSON "You are a good person."
+#define MEDIBOT_VOICED_BEHAVIOUR_REPORTED "Your behavior has been reported, have a nice day."
+#define MEDIBOT_VOICED_ASSISTANCE "I require assistance."
+#define MEDIBOT_VOICED_PUT_BACK "Please put me back."
+#define MEDIBOT_VOICED_IM_SCARED "Please, I am scared!"
+#define MEDIBOT_VOICED_NEED_HELP "I don't like this, I need help!"
+#define MEDIBOT_VOICED_THIS_HURTS "This hurts, my pain is real!"
+#define MEDIBOT_VOICED_THE_END "Is this the end?"
+#define MEDIBOT_VOICED_NOOO "Nooo!"
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 0931efc044c..f04f32e3cdb 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -1544,7 +1544,7 @@
var/area/atom_area = get_area(src)
if(!atom_turf) // some machines spawn in nullspace
- return
+ return FALSE
if(!is_station_level(atom_turf.z) && !istype(atom_area, /area/shuttle/escape))
// Why snowflake check for escape shuttle? Well, a lot of shuttles spawn with machines
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index b7bad9a6603..97af2639b8c 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -112,6 +112,10 @@
COOLDOWN_DECLARE(offer_ghosts_cooldown)
/// Message to display upon possession
var/possessed_message = "You're a generic bot. How did one of these even get made?"
+ /// List of strings to sound effects corresponding to automated messages the bot can play
+ var/list/automated_announcements
+ /// Action we use to say voice lines out loud, also we just pass anything we try to say through here just in case it plays a voice line
+ var/datum/action/cooldown/bot_announcement/pa_system
/mob/living/simple_animal/bot/proc/get_mode()
if(client) //Player bots do not have modes, thus the override. Also an easy way for PDA users/AI to know when a bot is a player.
@@ -195,10 +199,14 @@
if(mapload && is_station_level(z) && bot_mode_flags & BOT_MODE_CAN_BE_SAPIENT && bot_mode_flags & BOT_MODE_ROUNDSTART_POSSESSION)
enable_possession(mapload = mapload)
+ pa_system = new(src, automated_announcements = automated_announcements)
+ pa_system.Grant(src)
+
/mob/living/simple_animal/bot/Destroy()
if(paicard)
ejectpai()
GLOB.bots_list -= src
+ QDEL_NULL(pa_system)
QDEL_NULL(personality_download)
QDEL_NULL(internal_radio)
QDEL_NULL(access_card)
@@ -526,13 +534,14 @@
if(was_on)
turn_on()
-/mob/living/simple_animal/bot/proc/speak(message,channel) //Pass a message to have the bot say() it. Pass a frequency to say it on the radio.
- if((!(bot_mode_flags & BOT_MODE_ON)) || (!message))
+/**
+ * Pass a message to have the bot say() it, passing through our announcement action to potentially also play a sound.
+ * Optionally pass a frequency to say it on the radio.
+ */
+/mob/living/simple_animal/bot/proc/speak(message, channel)
+ if(!message)
return
- if(channel && internal_radio.channels[channel])// Use radio if we have channel key
- internal_radio.talk_into(src, message, channel)
- else
- say(message)
+ pa_system.announce(message, channel)
/mob/living/simple_animal/bot/radio(message, list/message_mods = list(), list/spans, language)
. = ..()
@@ -1069,6 +1078,8 @@ Pass a positive integer as an argument to override a bot's default speed.
disable_possession()
if(paicard.pai.holoform)
paicard.pai.fold_in()
+ copy_languages(paicard.pai, source_override = LANGUAGE_PAI)
+ set_active_language(paicard.pai.get_selected_language())
user.visible_message(span_notice("[user] inserts [card] into [src]!"), span_notice("You insert [card] into [src]."))
paicard.pai.mind.transfer_to(src)
to_chat(src, span_notice("You sense your form change as you are uploaded into [src]."))
@@ -1104,6 +1115,8 @@ Pass a positive integer as an argument to override a bot's default speed.
paicard = null
name = initial(src.name)
faction = initial(faction)
+ remove_all_languages(source = LANGUAGE_PAI)
+ get_selected_language()
/// Ejects the pAI remotely.
/mob/living/simple_animal/bot/proc/ejectpairemote(mob/user)
diff --git a/code/modules/mob/living/simple_animal/bot/bot_announcement.dm b/code/modules/mob/living/simple_animal/bot/bot_announcement.dm
new file mode 100644
index 00000000000..32df0729c8c
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/bot/bot_announcement.dm
@@ -0,0 +1,55 @@
+/// Say something and play a corresponding sound effect
+/datum/action/cooldown/bot_announcement
+ name = "Make automated announcement"
+ desc = "Play a prerecorded message for the benefit of those around you."
+ background_icon_state = "bg_tech_blue"
+ overlay_icon_state = "bg_tech_blue_border"
+ button_icon = 'icons/mob/actions/actions_AI.dmi'
+ button_icon_state = "intercom"
+ cooldown_time = 10 SECONDS
+ melee_cooldown_time = 0 SECONDS
+ /// List of strings to sound effects corresponding to automated messages we can play
+ var/list/automated_announcements
+
+/datum/action/cooldown/bot_announcement/New(Target, original, list/automated_announcements)
+ src.automated_announcements = automated_announcements
+ return ..()
+
+/datum/action/cooldown/bot_announcement/IsAvailable(feedback)
+ . = ..()
+ if (!.)
+ return
+ if (!isbot(owner))
+ if (feedback)
+ owner.balloon_alert(owner, "no announcement system!")
+ return FALSE
+ if (!length(automated_announcements))
+ if (feedback)
+ owner.balloon_alert(owner, "no valid announcements!")
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/bot_announcement/Activate(trigger_flags, atom/target)
+ var/picked
+ if (length(automated_announcements) > 1)
+ picked = tgui_input_list(owner, message = "Choose announcement to make.", title = "Select announcement", items = automated_announcements)
+ else
+ picked = pick(automated_announcements)
+ if (isnull(picked))
+ return
+ announce(picked)
+ return ..()
+
+/// Speak the provided line on the provided radio channel
+/datum/action/cooldown/bot_announcement/proc/announce(line, channel)
+ var/mob/living/simple_animal/bot/bot_owner = owner
+ if (!(bot_owner.bot_mode_flags & BOT_MODE_ON))
+ return
+
+ if (channel && bot_owner.internal_radio.channels[channel])
+ bot_owner.internal_radio.talk_into(bot_owner, message = line, channel = channel)
+ else
+ bot_owner.say(line)
+
+ if (length(automated_announcements) && !isnull(automated_announcements[line]))
+ playsound(bot_owner, automated_announcements[line], vol = 50, vary = FALSE)
diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
index 7e2f6d04367..e1ca1c2b484 100644
--- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
@@ -12,6 +12,8 @@
bot_type = ADVANCED_SEC_BOT
hackables = "combat inhibitors"
+ automated_announcements = list(ED209_VOICED_DOWN_WEAPONS = 'sound/voice/ed209_20sec.ogg')
+
var/lastfired = 0
var/shot_delay = 15
var/shoot_sound = 'sound/weapons/laser.ogg'
@@ -54,6 +56,10 @@
shoot_at(all_targets)
..()
+/mob/living/simple_animal/bot/secbot/ed209/threat_react(threatlevel)
+ speak("Level [threatlevel] infraction alert!")
+ playsound(src, pick('sound/voice/ed209_20sec.ogg', 'sound/voice/edplaceholder.ogg'), 50, FALSE)
+
/mob/living/simple_animal/bot/secbot/ed209/proc/set_weapon() //used to update the projectile type and firing sound
shoot_sound = 'sound/weapons/laser.ogg'
if(bot_cover_flags & BOT_COVER_EMAGGED)
diff --git a/code/modules/mob/living/simple_animal/bot/firebot.dm b/code/modules/mob/living/simple_animal/bot/firebot.dm
index 99473de51f9..29006613e42 100644
--- a/code/modules/mob/living/simple_animal/bot/firebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/firebot.dm
@@ -22,6 +22,16 @@
path_image_color = "#FFA500"
possessed_message = "You are a firebot! Protect the station from fires to the best of your ability!"
+ automated_announcements = list(
+ FIREBOT_VOICED_FIRE_DETECTED = 'sound/voice/firebot/detected.ogg',
+ FIREBOT_VOICED_STOP_DROP = 'sound/voice/firebot/stopdropnroll.ogg',
+ FIREBOT_VOICED_EXTINGUISHING = 'sound/voice/firebot/extinguishing.ogg',
+ FIREBOT_VOICED_NO_FIRES = 'sound/voice/firebot/nofires.ogg',
+ FIREBOT_VOICED_ONLY_YOU = 'sound/voice/firebot/onlyyou.ogg',
+ FIREBOT_VOICED_TEMPERATURE_NOMINAL = 'sound/voice/firebot/tempnominal.ogg',
+ FIREBOT_VOICED_KEEP_COOL = 'sound/voice/firebot/keepitcool.ogg',
+ )
+
var/atom/target_fire
var/atom/old_target_fire
@@ -175,13 +185,13 @@
return
if(prob(1) && target_fire == null)
- var/list/messagevoice = list("No fires detected." = 'sound/voice/firebot/nofires.ogg',
- "Only you can prevent station fires." = 'sound/voice/firebot/onlyyou.ogg',
- "Temperature nominal." = 'sound/voice/firebot/tempnominal.ogg',
- "Keep it cool." = 'sound/voice/firebot/keepitcool.ogg')
- var/message = pick(messagevoice)
- speak(message)
- playsound(src, messagevoice[message], 50)
+ var/static/list/idle_line = list(
+ FIREBOT_VOICED_NO_FIRES,
+ FIREBOT_VOICED_ONLY_YOU,
+ FIREBOT_VOICED_TEMPERATURE_NOMINAL,
+ FIREBOT_VOICED_KEEP_COOL,
+ )
+ speak(pick(idle_line))
// Couldn't reach the target, reset and try again ignoring the old one
if(frustration > 8)
@@ -207,11 +217,9 @@
if(target_fire && (get_dist(src, target_fire) <= (bot_cover_flags & BOT_COVER_EMAGGED ? 1 : 2))) // Make the bot spray water from afar when not emagged
if((speech_cooldown + SPEECH_INTERVAL) < world.time)
if(ishuman(target_fire))
- speak("Stop, drop and roll!")
- playsound(src, 'sound/voice/firebot/stopdropnroll.ogg', 50, FALSE)
+ speak(FIREBOT_VOICED_STOP_DROP)
else
- speak("Extinguishing!")
- playsound(src, 'sound/voice/firebot/extinguishing.ogg', 50, FALSE)
+ speak(FIREBOT_VOICED_EXTINGUISHING)
speech_cooldown = world.time
flick("firebot1_use", src)
@@ -260,8 +268,7 @@
return null
if((detected_cooldown + DETECTED_VOICE_INTERVAL) < world.time)
- speak("Fire detected!")
- playsound(src, 'sound/voice/firebot/detected.ogg', 50, FALSE)
+ speak(FIREBOT_VOICED_FIRE_DETECTED)
detected_cooldown = world.time
return scan_target
diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm
index 5867e88f8fe..8c46d8bec9c 100644
--- a/code/modules/mob/living/simple_animal/bot/honkbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm
@@ -19,6 +19,11 @@
security_mode_flags = SECBOT_CHECK_WEAPONS | SECBOT_HANDCUFF_TARGET
possessed_message = "You are a honkbot! Make sure the crew are having a great time!"
+ automated_announcements = list(
+ HONKBOT_VOICED_HONK_HAPPY = 'sound/items/bikehorn.ogg',
+ HONKBOT_VOICED_HONK_SAD = 'sound/misc/sadtrombone.ogg',
+ )
+
///Keeping track of how much we honk to prevent spamming it
var/limiting_spam = FALSE
///Sound played when HONKing someone
@@ -44,11 +49,13 @@
/mob/living/simple_animal/bot/secbot/honkbot/knockOver(mob/living/carbon/tripped_target)
. = ..()
- INVOKE_ASYNC(src, TYPE_PROC_REF(/mob/living/simple_animal/bot, speak), "Honk!")
- playsound(loc, 'sound/misc/sadtrombone.ogg', 50, TRUE, -1)
+ INVOKE_ASYNC(src, TYPE_PROC_REF(/mob/living/simple_animal/bot, speak), HONKBOT_VOICED_HONK_SAD)
icon_state = "[initial(icon_state)]-c"
addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_appearance)), 0.2 SECONDS)
+/mob/living/simple_animal/bot/secbot/honkbot/threat_react(threatlevel)
+ speak(HONKBOT_VOICED_HONK_HAPPY)
+
/mob/living/simple_animal/bot/secbot/honkbot/bot_reset()
..()
limiting_spam = FALSE
diff --git a/code/modules/mob/living/simple_animal/bot/hygienebot.dm b/code/modules/mob/living/simple_animal/bot/hygienebot.dm
index 5151bf0e97d..0db59e2518c 100644
--- a/code/modules/mob/living/simple_animal/bot/hygienebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/hygienebot.dm
@@ -20,6 +20,21 @@
hackables = "cleaning service protocols"
path_image_color = "#993299"
+ automated_announcements = list(
+ HYGIENEBOT_VOICED_UNHYGIENIC = 'sound/voice/hygienebot/unhygienicclient.ogg',
+ HYGIENEBOT_VOICED_ENJOY_DAY = 'sound/voice/hygienebot/cleanandtidy.ogg',
+ HYGIENEBOT_VOICED_THREAT_AIRLOCK = 'sound/voice/hygienebot/dragyouout.ogg',
+ HYGIENEBOT_VOICED_FOUL_SMELL = 'sound/voice/hygienebot/foulsmelling.ogg',
+ HYGIENEBOT_VOICED_TROGLODYTE = 'sound/voice/hygienebot/troglodyte.ogg',
+ HYGIENEBOT_VOICED_GREEN_CLOUD = 'sound/voice/hygienebot/greencloud.ogg',
+ HYGIENEBOT_VOICED_ARSEHOLE = 'sound/voice/hygienebot/letmeclean.ogg',
+ HYGIENEBOT_VOICED_THREAT_ARTERIES = 'sound/voice/hygienebot/cutarteries.ogg',
+ HYGIENEBOT_VOICED_STOP_RUNNING = 'sound/voice/hygienebot/stoprunning.ogg',
+ HYGIENEBOT_VOICED_FUCKING_FINALLY = 'sound/voice/hygienebot/finally.ogg',
+ HYGIENEBOT_VOICED_THANK_GOD = 'sound/voice/hygienebot/thankgod.ogg',
+ HYGIENEBOT_VOICED_DEGENERATE = 'sound/voice/hygienebot/degenerate.ogg',
+ )
+
///The human target the bot is trying to wash.
var/mob/living/carbon/human/target
///The mob's current speed, which varies based on how long the bot chases it's target.
@@ -127,14 +142,12 @@
if(target.loc == loc && isturf(target.loc)) //LADIES AND GENTLEMAN WE GOTEM PREPARE TO DUMP
start_washing()
if(mad)
- var/static/list/messagevoice = list(
- "Fucking finally." = 'sound/voice/hygienebot/finally.ogg',
- "Thank god, you finally stopped." = 'sound/voice/hygienebot/thankgod.ogg',
- "Well about fucking time you degenerate." = 'sound/voice/hygienebot/degenerate.ogg',
+ var/static/list/relief = list(
+ HYGIENEBOT_VOICED_FUCKING_FINALLY,
+ HYGIENEBOT_VOICED_THANK_GOD,
+ HYGIENEBOT_VOICED_DEGENERATE,
)
- var/message = pick(messagevoice)
- speak(message)
- playsound(loc, messagevoice[message], 50)
+ speak(pick(relief))
playsound(loc, 'sound/effects/hygienebot_angry.ogg', 60, 1) //i think it should still make robot noises too
mad = FALSE
mode = BOT_SHOWERSTANCE
@@ -146,18 +159,16 @@
return
SSmove_manager.move_to(src, target, 0, currentspeed)
if(mad && prob(min(frustration * 2, 60)))
- var/static/list/messagevoice = list(
- "Either you stop running or I will fucking drag you out of an airlock." = 'sound/voice/hygienebot/dragyouout.ogg',
- "Get back here you foul smelling fucker." = 'sound/voice/hygienebot/foulsmelling.ogg',
- "I just want to fucking clean you you troglodyte." = 'sound/voice/hygienebot/troglodyte.ogg',
- "If you don't come back here I'll put a green cloud around you cunt." = 'sound/voice/hygienebot/greencloud.ogg',
- "Just fucking let me clean you you arsehole!" = 'sound/voice/hygienebot/letmeclean.ogg',
- "STOP RUNNING OR I WILL CUT YOUR ARTERIES!" = 'sound/voice/hygienebot/cutarteries.ogg',
- "STOP. RUNNING." = 'sound/voice/hygienebot/stoprunning.ogg',
+ var/static/list/threats = list(
+ HYGIENEBOT_VOICED_THREAT_AIRLOCK,
+ HYGIENEBOT_VOICED_FOUL_SMELL,
+ HYGIENEBOT_VOICED_TROGLODYTE,
+ HYGIENEBOT_VOICED_GREEN_CLOUD,
+ HYGIENEBOT_VOICED_ARSEHOLE,
+ HYGIENEBOT_VOICED_THREAT_ARTERIES,
+ HYGIENEBOT_VOICED_STOP_RUNNING,
)
- var/message = pick(messagevoice)
- speak(message)
- playsound(loc, messagevoice[message], 50)
+ speak(pick(threats))
playsound(loc, 'sound/effects/hygienebot_angry.ogg', 60, 1)
if((get_dist(src, target)) >= olddist)
frustration++
@@ -168,8 +179,7 @@
if(BOT_SHOWERSTANCE)
if(check_purity(target))
- speak("Enjoy your clean and tidy day!")
- playsound(loc, 'sound/voice/hygienebot/cleanandtidy.ogg', 50)
+ speak(HYGIENEBOT_VOICED_ENJOY_DAY)
playsound(loc, 'sound/effects/hygienebot_happy.ogg', 60, 1)
back_to_idle()
return
@@ -208,8 +218,7 @@
if(!check_purity(H)) //Theyre impure
target = H
oldtarget_name = H.name
- speak("Unhygienic client found. Please stand still so I can clean you.")
- playsound(loc, 'sound/voice/hygienebot/unhygienicclient.ogg', 50)
+ speak(HYGIENEBOT_VOICED_UNHYGIENIC)
playsound(loc, 'sound/effects/hygienebot_happy.ogg', 60, 1)
visible_message("[src] points at [H.name]!")
mode = BOT_HUNT
diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm
index 29240da7daf..9428ece78cb 100644
--- a/code/modules/mob/living/simple_animal/bot/medbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/medbot.dm
@@ -35,6 +35,45 @@
path_image_color = "#DDDDFF"
possessed_message = "You are a medbot! Ensure good health among the crew to the best of your ability!"
+ automated_announcements = list(
+ MEDIBOT_VOICED_HOLD_ON = 'sound/voice/medbot/coming.ogg',
+ MEDIBOT_VOICED_WANT_TO_HELP = 'sound/voice/medbot/help.ogg',
+ MEDIBOT_VOICED_YOU_ARE_INJURED = 'sound/voice/medbot/injured.ogg',
+ MEDIBOT_VOICED_ALL_PATCHED_UP = 'sound/voice/medbot/patchedup.ogg',
+ MEDIBOT_VOICED_APPLE_A_DAY = 'sound/voice/medbot/apple.ogg',
+ MEDIBOT_VOICED_FEEL_BETTER = 'sound/voice/medbot/feelbetter.ogg',
+ MEDIBOT_VOICED_STAY_WITH_ME = 'sound/voice/medbot/no.ogg',
+ MEDIBOT_VOICED_LIVE = 'sound/voice/medbot/live.ogg',
+ MEDIBOT_VOICED_NEVER_LOST = 'sound/voice/medbot/lost.ogg',
+ MEDIBOT_VOICED_DELICIOUS = 'sound/voice/medbot/delicious.ogg',
+ MEDIBOT_VOICED_PLASTIC_SURGEON = 'sound/voice/medbot/surgeon.ogg',
+ MEDIBOT_VOICED_MASK_ON = 'sound/voice/medbot/radar.ogg',
+ MEDIBOT_VOICED_ALWAYS_A_CATCH = 'sound/voice/medbot/catch.ogg',
+ MEDIBOT_VOICED_LIKE_FLIES = 'sound/voice/medbot/flies.ogg',
+ MEDIBOT_VOICED_SUFFER = 'sound/voice/medbot/why.ogg',
+ MEDIBOT_VOICED_FUCK_YOU = 'sound/voice/medbot/fuck_you.ogg',
+ MEDIBOT_VOICED_NOT_A_GAME = 'sound/voice/medbot/turn_off.ogg',
+ MEDIBOT_VOICED_IM_DIFFERENT = 'sound/voice/medbot/im_different.ogg',
+ MEDIBOT_VOICED_FOURTH_WALL = 'sound/voice/medbot/close.ogg',
+ MEDIBOT_VOICED_SHINDEMASHOU = 'sound/voice/medbot/shindemashou.ogg',
+ MEDIBOT_VOICED_WAIT = 'sound/voice/medbot/hey_wait.ogg',
+ MEDIBOT_VOICED_DONT = 'sound/voice/medbot/please_dont.ogg',
+ MEDIBOT_VOICED_TRUSTED_YOU = 'sound/voice/medbot/i_trusted_you.ogg',
+ MEDIBOT_VOICED_NO_SAD = 'sound/voice/medbot/nooo.ogg',
+ MEDIBOT_VOICED_OH_FUCK = 'sound/voice/medbot/oh_fuck.ogg',
+ MEDIBOT_VOICED_FORGIVE = 'sound/voice/medbot/forgive.ogg',
+ MEDIBOT_VOICED_THANKS = 'sound/voice/medbot/thank_you.ogg',
+ MEDIBOT_VOICED_GOOD_PERSON = 'sound/voice/medbot/youre_good.ogg',
+ MEDIBOT_VOICED_BEHAVIOUR_REPORTED = 'sound/voice/medbot/reported.ogg',
+ MEDIBOT_VOICED_ASSISTANCE = 'sound/voice/medbot/i_require_asst.ogg',
+ MEDIBOT_VOICED_PUT_BACK = 'sound/voice/medbot/please_put_me_back.ogg',
+ MEDIBOT_VOICED_IM_SCARED = 'sound/voice/medbot/please_im_scared.ogg',
+ MEDIBOT_VOICED_NEED_HELP = 'sound/voice/medbot/dont_like.ogg',
+ MEDIBOT_VOICED_THIS_HURTS = 'sound/voice/medbot/pain_is_real.ogg',
+ MEDIBOT_VOICED_THE_END = 'sound/voice/medbot/is_this_the_end.ogg',
+ MEDIBOT_VOICED_NOOO = 'sound/voice/medbot/nooo.ogg',
+ )
+
/// drop determining variable
var/healthanalyzer = /obj/item/healthanalyzer
/// drop determining variable
@@ -251,7 +290,11 @@
last_found = world.time
if(COOLDOWN_FINISHED(src, last_newpatient_speak))
COOLDOWN_START(src, last_newpatient_speak, MEDBOT_NEW_PATIENTSPEAK_DELAY)
- var/list/messagevoice = list("Hey, [H.name]! Hold on, I'm coming." = 'sound/voice/medbot/coming.ogg',"Wait [H.name]! I want to help!" = 'sound/voice/medbot/help.ogg',"[H.name], you appear to be injured!" = 'sound/voice/medbot/injured.ogg')
+ var/list/messagevoice = list(
+ "Hey, [H.name]! Hold on, I'm coming." = 'sound/voice/medbot/coming.ogg',
+ "Wait [H.name]! I want to help!" = 'sound/voice/medbot/help.ogg',
+ "[H.name], you appear to be injured!" = 'sound/voice/medbot/injured.ogg',
+ )
var/message = pick(messagevoice)
speak(message)
playsound(src, messagevoice[message], 50, FALSE)
@@ -267,16 +310,14 @@
return
COOLDOWN_START(src, last_tipping_action_voice, MEDBOT_FREAKOUT_DELAY) // message for tipping happens when we start interacting, message for righting comes after finishing
- var/static/list/messagevoice = list(
- "Hey, wait..." = 'sound/voice/medbot/hey_wait.ogg',
- "Please don't..." = 'sound/voice/medbot/please_dont.ogg',
- "I trusted you..." = 'sound/voice/medbot/i_trusted_you.ogg',
- "Nooo..." = 'sound/voice/medbot/nooo.ogg',
- "Oh fuck-" = 'sound/voice/medbot/oh_fuck.ogg',
- )
- var/message = pick(messagevoice)
- speak(message)
- playsound(src, messagevoice[message], 70, FALSE)
+ var/static/list/worried_line = list(
+ MEDIBOT_VOICED_WAIT,
+ MEDIBOT_VOICED_DONT,
+ MEDIBOT_VOICED_TRUSTED_YOU,
+ MEDIBOT_VOICED_NO_SAD,
+ MEDIBOT_VOICED_OH_FUCK,
+ )
+ speak(pick(worried_line))
/*
* Proc used in a callback for after this medibot is tipped by the tippable component.
@@ -297,18 +338,16 @@
var/list/messagevoice
if(user)
if(user.name == tipper_name)
- messagevoice = list("I forgive you." = 'sound/voice/medbot/forgive.ogg')
+ messagevoice = list(MEDIBOT_VOICED_FORGIVE)
else
- messagevoice = list("Thank you!" = 'sound/voice/medbot/thank_you.ogg', "You are a good person." = 'sound/voice/medbot/youre_good.ogg')
+ messagevoice = list(MEDIBOT_VOICED_THANKS, MEDIBOT_VOICED_GOOD_PERSON)
else
- messagevoice = list("Fuck you." = 'sound/voice/medbot/fuck_you.ogg', "Your behavior has been reported, have a nice day." = 'sound/voice/medbot/reported.ogg')
+ messagevoice = list(MEDIBOT_VOICED_FUCK_YOU, MEDIBOT_VOICED_BEHAVIOUR_REPORTED)
tipper_name = null
if(COOLDOWN_FINISHED(src, last_tipping_action_voice))
COOLDOWN_START(src, last_tipping_action_voice, MEDBOT_FREAKOUT_DELAY)
- var/message = pick(messagevoice)
- speak(message)
- playsound(src, messagevoice[message], 70)
+ speak(pick(messagevoice))
tipped_status = MEDBOT_PANIC_NONE
mode = BOT_IDLE
@@ -319,15 +358,15 @@
switch(tipped_status)
if(MEDBOT_PANIC_LOW)
- messagevoice = list("I require assistance." = 'sound/voice/medbot/i_require_asst.ogg')
+ messagevoice = list(MEDIBOT_VOICED_ASSISTANCE)
if(MEDBOT_PANIC_MED)
- messagevoice = list("Please put me back." = 'sound/voice/medbot/please_put_me_back.ogg')
+ messagevoice = list(MEDIBOT_VOICED_PUT_BACK)
if(MEDBOT_PANIC_HIGH)
- messagevoice = list("Please, I am scared!" = 'sound/voice/medbot/please_im_scared.ogg')
+ messagevoice = list(MEDIBOT_VOICED_IM_SCARED)
if(MEDBOT_PANIC_FUCK)
- messagevoice = list("I don't like this, I need help!" = 'sound/voice/medbot/dont_like.ogg', "This hurts, my pain is real!" = 'sound/voice/medbot/pain_is_real.ogg')
+ messagevoice = list(MEDIBOT_VOICED_NEED_HELP, MEDIBOT_VOICED_THIS_HURTS)
if(MEDBOT_PANIC_ENDING)
- messagevoice = list("Is this the end?" = 'sound/voice/medbot/is_this_the_end.ogg', "Nooo!" = 'sound/voice/medbot/nooo.ogg')
+ messagevoice = list(MEDIBOT_VOICED_THE_END, MEDIBOT_VOICED_NOOO)
if(MEDBOT_PANIC_END)
speak("PSYCH ALERT: Crewmember [tipper_name] recorded displaying antisocial tendencies torturing bots in [get_area(src)]. Please schedule psych evaluation.", radio_channel)
@@ -335,9 +374,7 @@
do_jitter_animation(tipped_status * 0.1)
if(messagevoice)
- var/message = pick(messagevoice)
- speak(message)
- playsound(src, messagevoice[message], 70)
+ speak(pick(messagevoice))
else if(prob(tipped_status * 0.2))
playsound(src, 'sound/machines/warning-buzzer.ogg', 30, extrarange=-2)
@@ -366,26 +403,24 @@
if(QDELETED(patient))
if(medical_mode_flags & MEDBOT_SPEAK_MODE && prob(1))
if(bot_cover_flags & BOT_COVER_EMAGGED && prob(30))
- var/list/i_need_scissors = list(
- 'sound/voice/medbot/fuck_you.ogg',
- 'sound/voice/medbot/turn_off.ogg',
- 'sound/voice/medbot/im_different.ogg',
- 'sound/voice/medbot/close.ogg',
- 'sound/voice/medbot/shindemashou.ogg',
+ var/static/list/i_need_scissors = list(
+ MEDIBOT_VOICED_FUCK_YOU,
+ MEDIBOT_VOICED_NOT_A_GAME,
+ MEDIBOT_VOICED_IM_DIFFERENT,
+ MEDIBOT_VOICED_FOURTH_WALL,
+ MEDIBOT_VOICED_SHINDEMASHOU,
)
- playsound(src, pick(i_need_scissors), 70)
+ speak(pick(i_need_scissors))
else
- var/static/list/messagevoice = list(
- "Delicious!" = 'sound/voice/medbot/delicious.ogg',
- "I knew it, I should've been a plastic surgeon." = 'sound/voice/medbot/surgeon.ogg',
- "Radar, put a mask on!" = 'sound/voice/medbot/radar.ogg',
- "There's always a catch, and I'm the best there is." = 'sound/voice/medbot/catch.ogg',
- "What kind of medbay is this? Everyone's dropping like flies." = 'sound/voice/medbot/flies.ogg',
- "Why are we still here? Just to suffer?" = 'sound/voice/medbot/why.ogg',
+ var/static/list/idle_lines = list(
+ MEDIBOT_VOICED_DELICIOUS,
+ MEDIBOT_VOICED_PLASTIC_SURGEON,
+ MEDIBOT_VOICED_MASK_ON,
+ MEDIBOT_VOICED_ALWAYS_A_CATCH,
+ MEDIBOT_VOICED_LIKE_FLIES,
+ MEDIBOT_VOICED_SUFFER,
)
- var/message = pick(messagevoice)
- speak(message)
- playsound(src, messagevoice[message], 50)
+ speak(pick(idle_lines))
var/scan_range = (medical_mode_flags & MEDBOT_STATIONARY_MODE ? 1 : DEFAULT_SCAN_RANGE) //If in stationary mode, scan range is limited to adjacent patients.
patient = scan(list(/mob/living/carbon/human), oldpatient, scan_range)
oldpatient = patient
@@ -515,10 +550,12 @@
return
if(C.stat == DEAD || (HAS_TRAIT(C, TRAIT_FAKEDEATH)))
- var/list/messagevoice = list("No! Stay with me!" = 'sound/voice/medbot/no.ogg',"Live, damnit! LIVE!" = 'sound/voice/medbot/live.ogg',"I...I've never lost a patient before. Not today, I mean." = 'sound/voice/medbot/lost.ogg')
- var/message = pick(messagevoice)
- speak(message)
- playsound(src, messagevoice[message], 50)
+ var/static/list/grief_lines = list(
+ MEDIBOT_VOICED_STAY_WITH_ME,
+ MEDIBOT_VOICED_LIVE,
+ MEDIBOT_VOICED_NEVER_LOST,
+ )
+ speak(pick(grief_lines))
oldpatient = patient
soft_reset()
return
@@ -552,10 +589,12 @@
if(C.maxHealth - C.get_organic_health() < heal_threshold)
to_chat(src, span_notice("[C] is healthy! Your programming prevents you from tending the wounds of anyone without at least [heal_threshold] damage of any one type ([heal_threshold + 5] for oxygen damage.)"))
- var/list/messagevoice = list("All patched up!" = 'sound/voice/medbot/patchedup.ogg',"An apple a day keeps me away." = 'sound/voice/medbot/apple.ogg',"Feel better soon!" = 'sound/voice/medbot/feelbetter.ogg')
- var/message = pick(messagevoice)
- speak(message)
- playsound(src, messagevoice[message], 50)
+ var/static/list/success_lines = list(
+ MEDIBOT_VOICED_ALL_PATCHED_UP,
+ MEDIBOT_VOICED_APPLE_A_DAY,
+ MEDIBOT_VOICED_FEEL_BETTER,
+ )
+ speak(pick(success_lines))
bot_reset()
tending = FALSE
else if(patient)
diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm
index d7c2f4a59d7..28defd68aa3 100644
--- a/code/modules/mob/living/simple_animal/bot/secbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/secbot.dm
@@ -21,6 +21,15 @@
path_image_color = "#FF0000"
possessed_message = "You are a securitron! Guard the station to the best of your ability!"
+ automated_announcements = list(
+ BEEPSKY_VOICED_CRIMINAL_DETECTED = 'sound/voice/beepsky/criminal.ogg',
+ BEEPSKY_VOICED_FREEZE = 'sound/voice/beepsky/freeze.ogg',
+ BEEPSKY_VOICED_JUSTICE = 'sound/voice/beepsky/justice.ogg',
+ BEEPSKY_VOICED_YOUR_MOVE = 'sound/voice/beepsky/creep.ogg',
+ BEEPSKY_VOICED_I_AM_THE_LAW = 'sound/voice/beepsky/iamthelaw.ogg',
+ BEEPSKY_VOICED_SECURE_DAY = 'sound/voice/beepsky/secureday.ogg',
+ )
+
///The type of baton this Secbot will use
var/baton_type = /obj/item/melee/baton/security
///The type of cuffs we use on criminals after making arrests
@@ -453,22 +462,17 @@
if(threatlevel >= 4)
target = nearby_carbons
oldtarget_name = nearby_carbons.name
- switch(bot_type)
- if(ADVANCED_SEC_BOT)
- speak("Level [threatlevel] infraction alert!")
- playsound(src, pick('sound/voice/ed209_20sec.ogg', 'sound/voice/edplaceholder.ogg'), 50, FALSE)
- if(HONK_BOT)
- speak("Honk!")
- playsound(src, pick('sound/items/bikehorn.ogg'), 50, FALSE)
- else
- speak("Level [threatlevel] infraction alert!")
- playsound(src, pick('sound/voice/beepsky/criminal.ogg', 'sound/voice/beepsky/justice.ogg', 'sound/voice/beepsky/freeze.ogg'), 50, FALSE)
-
+ threat_react(threatlevel)
visible_message("[src] points at [nearby_carbons.name]!")
mode = BOT_HUNT
INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
break
+/// React to detecting criminal scum by making some kind of noise
+/mob/living/simple_animal/bot/secbot/proc/threat_react(threatlevel)
+ speak("Level [threatlevel] infraction alert!")
+ playsound(src, pick('sound/voice/beepsky/criminal.ogg', 'sound/voice/beepsky/justice.ogg', 'sound/voice/beepsky/freeze.ogg'), 50, FALSE)
+
/mob/living/simple_animal/bot/secbot/proc/check_for_weapons(obj/item/slot_item)
if(slot_item && (slot_item.item_flags & NEEDS_PERMIT))
return TRUE
diff --git a/tgstation.dme b/tgstation.dme
index 4dcb77dd064..5a8876c1026 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -4392,6 +4392,7 @@
#include "code\modules\mob\living\simple_animal\shade.dm"
#include "code\modules\mob\living\simple_animal\simple_animal.dm"
#include "code\modules\mob\living\simple_animal\bot\bot.dm"
+#include "code\modules\mob\living\simple_animal\bot\bot_announcement.dm"
#include "code\modules\mob\living\simple_animal\bot\cleanbot.dm"
#include "code\modules\mob\living\simple_animal\bot\construction.dm"
#include "code\modules\mob\living\simple_animal\bot\ed209bot.dm"