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"