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/_onclick/ai.dm b/code/_onclick/ai.dm index 239ef319d1..3d34444ca1 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -19,7 +19,9 @@ A.move_camera_by_click() /mob/living/silicon/ai/ClickOn(var/atom/A, params) + message_admins("do we even get this far") if(!can_interact_with(A)) + message_admins("can't interact with [A]") return if(multicam_on) @@ -31,13 +33,16 @@ break if(check_click_intercept(params,A)) + message_admins("intercepted") return if(control_disabled || incapacitated()) + message_admins("disabled or incap'd") return var/turf/pixel_turf = get_turf_pixel(A) if(isnull(pixel_turf)) + message_admins("no turf pixel") return if(!can_see(A)) if(isturf(A)) //On unmodified clients clicking the static overlay clicks the turf underneath @@ -51,6 +56,7 @@ send2tgs_adminless_only("NOCHEAT", message) return + message_admins("click going through with [params]") var/list/modifiers = params2list(params) if(modifiers["shift"] && modifiers["ctrl"]) CtrlShiftClickOn(A) @@ -62,6 +68,7 @@ AltClickOn(A) return if(modifiers["ctrl"]) + message_admins("he clicked [A]") CtrlClickOn(A) return diff --git a/code/datums/saymode.dm b/code/datums/saymode.dm index 3b6fae5aee..3e1de50ab2 100644 --- a/code/datums/saymode.dm +++ b/code/datums/saymode.dm @@ -114,12 +114,25 @@ mode = MODE_HOLOPAD /datum/saymode/holopad/handle_message(mob/living/user, message, datum/language/language) + world << "OTHER GAMING TEST" if(isAI(user)) var/mob/living/silicon/ai/AI = user AI.holopad_talk(message, language) return FALSE return TRUE +/datum/saymode/statusdisplay + key = "q" + mode = MODE_STATUSDISPLAY + +/datum/saymode/statusdisplay/handle_message(mob/living/user, message, datum/language/language) + message_admins("uwu?") + 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..685bee18a2 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,41 @@ 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)) + update_appearance() + icon_state = initial(icon_state) + user.controlled_display = src + master = user + + // 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 = 10 + 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