diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm index 9d90e7aa9b..a4d0b6427d 100644 --- a/code/__DEFINES/say.dm +++ b/code/__DEFINES/say.dm @@ -37,6 +37,7 @@ #define MODE_ALIEN "alientalk" #define MODE_HOLOPAD "holopad" +#define MODE_STATUSDISPLAY "statusdisplay" #define MODE_CHANGELING "changeling" #define MODE_KEY_CHANGELING "g" diff --git a/code/datums/saymode.dm b/code/datums/saymode.dm index 3b6fae5aee..bd0537bac7 100644 --- a/code/datums/saymode.dm +++ b/code/datums/saymode.dm @@ -120,6 +120,17 @@ return FALSE return TRUE +/datum/saymode/statusdisplay + key = "q" + mode = MODE_STATUSDISPLAY + +/datum/saymode/statusdisplay/handle_message(mob/living/user, message, datum/language/language) + if(isAI(user)) + var/mob/living/silicon/ai/AI = user + AI.statusdisplay_talk(message, language) + return FALSE + return TRUE + /datum/saymode/monkey key = "k" mode = MODE_MONKEY diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm index 2438a6b66b..5aaed2a808 100644 --- a/code/game/machinery/status_display.dm +++ b/code/game/machinery/status_display.dm @@ -29,10 +29,12 @@ var/obj/effect/overlay/status_display_text/message1_overlay var/obj/effect/overlay/status_display_text/message2_overlay + var/mutable_appearance/ai_vtuber_overlay var/current_picture = "" var/current_mode = SD_BLANK var/message1 = "" var/message2 = "" + var/mob/living/silicon/ai/master /obj/item/wallframe/status_display name = "status display frame" @@ -122,6 +124,10 @@ if(overlay && message == overlay.message) return null + // if an AI is controlling, we don't update the overlay + if(master) + return + if(overlay) qdel(overlay) @@ -147,6 +153,10 @@ remove_messages() return + if(master) + remove_messages() + return + switch(current_mode) if(SD_BLANK) remove_messages() @@ -493,6 +503,42 @@ user.emote(initial(emote.key)) break +// ai vtuber moment +/obj/machinery/status_display/AICtrlClick(mob/living/silicon/ai/user) + if(!isAI(user) || master || (user.current && !istype(user.current, /obj/machinery/status_display))) // don't let two AIs control the same one, don't let AI control two things at once + return + + if(!user.client.prefs.custom_holoform_icon) + to_chat(user, span_notice("You have no custom holoform set!")) + return + + // move AI to the location, set master, update overlays (removes messages) + user.current = src + user.eyeobj.setLoc(get_turf(src)) + icon_state = initial(icon_state) + user.controlled_display = src + master = user + update_overlays() + update_appearance() + + // we set the avatar here + var/icon/I = icon(user.client.prefs.custom_holoform_icon) + I.Crop(1,16,32,32) + ai_vtuber_overlay = mutable_appearance(I) + ai_vtuber_overlay.blend_mode = BLEND_ADD + ai_vtuber_overlay.pixel_y = 8 + update_overlays() + add_overlay(ai_vtuber_overlay) + + // tell the user how to speak + to_chat(user, span_notice("Use :q to relay messages through the status display.")) + +// modified version of how holopads 'hear' and relay to AIs +/obj/machinery/status_display/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list()) + . = ..() + if(master && !radio_freq && master.controlled_display == src) + master.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mods) + /obj/machinery/status_display/ai/process() if(stat & NOPOWER) update_appearance() diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 3e16c9afa1..deaf50973d 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -47,6 +47,7 @@ var/mob/living/simple_animal/bot/Bot var/tracking = FALSE //this is 1 if the AI is currently tracking somebody, but the track has not yet been completed. var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N + var/obj/machinery/status_display/controlled_display //MALFUNCTION var/datum/module_picker/malf_picker @@ -1043,3 +1044,14 @@ /mob/living/silicon/ai/zMove(dir, feedback = FALSE) . = eyeobj.zMove(dir, feedback) + +/mob/living/silicon/ai/proc/stop_controlling_display() + if(!controlled_display) + return + controlled_display.master = null + controlled_display.cut_overlay(controlled_display.ai_vtuber_overlay) + controlled_display.ai_vtuber_overlay = null + if(current == controlled_display) + current = null + controlled_display.update_appearance() + controlled_display = null diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index bc6ab7c34e..bce18462b2 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -92,6 +92,8 @@ ai.light_cameras() if(ai.master_multicam) ai.master_multicam.refresh_view() + if(ai.controlled_display) + ai.stop_controlling_display() //it uses setLoc not forceMove, talks to the sillycone and not the camera mob /mob/camera/aiEye/zMove(dir, feedback = FALSE) @@ -168,6 +170,9 @@ if(!user.tracking) user.cameraFollow = null + if(user.controlled_display) + user.stop_controlling_display() + // Return to the Core. /mob/living/silicon/ai/proc/view_core() if(istype(current,/obj/machinery/holopad)) diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm index 2b5d3d98f2..0b9a4fe9d4 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -34,7 +34,7 @@ //For holopads only. Usable by AI. /mob/living/silicon/ai/proc/holopad_talk(message, language) - + message_admins("HOLOPAD TALK") message = trim(message) @@ -56,6 +56,30 @@ to_chat(src, "No holopad connected.") +//For status displays only. Usable by AI. +/mob/living/silicon/ai/proc/statusdisplay_talk(message, language) + message_admins("STATUSDISPLAY TALK") + message = trim(message) + + if (!message) + return + + var/obj/machinery/status_display/T = controlled_display + if(T) + var/turf/padturf = get_turf(T) + var/padloc + if(padturf) + padloc = AREACOORD(padturf) + else + padloc = "(UNKNOWN)" + src.log_talk(message, LOG_SAY, tag="STATUS DISPLAY in [padloc]") + send_speech(message, 7, T, "robot", message_language = language) + to_chat(src, "Status Display message transmitted, [real_name] \"[message]\"") + else + to_chat(src, "No status display connected.") + + + // Make sure that the code compiles with AI_VOX undefined #ifdef AI_VOX #define VOX_DELAY 600