From d96f38b0442fc33671d1a05d7b9f16621ee48968 Mon Sep 17 00:00:00 2001 From: Jacquerel Date: Fri, 14 Feb 2025 20:06:27 +0000 Subject: [PATCH] Admin verb to apply mob random speech behaviour (#89382) ## About The Pull Request Separate verb and PR from #89375 because this is something you'd apply on top of a mob with existing behaviour, or one you've used the other verb on. This will probably conflict with my other PR but that's my problem. This adds a shortcut for making mobs say/emote stuff randomly on a timer from a list of things you have specified. Doing this via VV is possible but sufficiently complicated that I don't think anyone would ever bother. As with the other PR you can optionally do this to mobs who already have a client if you want them to randomly burp every so often or something. I briefly flirted with the idea of replacing all `/datum/ai_planning_subtree/random_speech` subtypes with blackboard ones but... I think probably actually we save some memory _and_ sanity by not doing that. A bunch of mobs on totally different typepaths use the `/insect` subtype for instance, and I don't think it would be an improvement to paste the same four vars into all of their blackboards. ## Why It's Good For The Game This one is frankly more niche than the other PR probably but it is plausibly useful if you are setting up some kind of VV creature. --- code/__DEFINES/vv.dm | 1 + .../basic_subtrees/speech_subtree.dm | 3 + .../ai/basic_mobs/generic_controllers.dm | 7 ++ code/modules/admin/admin_verbs.dm | 100 ++++++++++++++++++ code/modules/mob/mob.dm | 5 +- 5 files changed, 115 insertions(+), 1 deletion(-) diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm index 04addf1786d..6c9d4e2b850 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -135,6 +135,7 @@ #define VV_HK_OFFER_GHOSTS "offer_ghosts" #define VV_HK_VIEW_PLANES "view_planes" #define VV_HK_GIVE_AI "give_ai" +#define VV_HK_GIVE_AI_SPEECH "give_ai_speech" // /mob/living #define VV_HK_GIVE_SPEECH_IMPEDIMENT "impede_speech" diff --git a/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm b/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm index 7d877731e2b..7dbce139b57 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm @@ -34,6 +34,9 @@ var/total_choices_length = audible_emotes_length + non_audible_emotes_length + speak_lines_length + if (total_choices_length == 0) + return + var/random_number_in_range = rand(1, total_choices_length) var/sound_to_play = length(sound) > 0 ? pick(sound) : null diff --git a/code/datums/ai/basic_mobs/generic_controllers.dm b/code/datums/ai/basic_mobs/generic_controllers.dm index d38cfc70763..8bf0026e1aa 100644 --- a/code/datums/ai/basic_mobs/generic_controllers.dm +++ b/code/datums/ai/basic_mobs/generic_controllers.dm @@ -122,3 +122,10 @@ planning_subtrees = list( /datum/ai_planning_subtree/pet_planning, ) + +/// Literally does nothing except random speedh +/datum/ai_controller/basic_controller/talk + idle_behavior = null + planning_subtrees = list( + /datum/ai_planning_subtree/random_speech/blackboard, + ) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 7ac5fa72372..ec4107e5d64 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -679,3 +679,103 @@ ADMIN_VERB(give_ai_controller, R_FUN, "Give AI Controller", ADMIN_VERB_NO_DESCRI var/chosen_type = controllers_by_name[chosen] var/datum/admin_ai_template/using_template = new chosen_type using_template.apply(my_guy, user) + +ADMIN_VERB(give_ai_speech, R_FUN, "Give Random AI Speech", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/living/my_guy) + if (isnull(my_guy.ai_controller)) + var/create_controller = tgui_alert(user, "Target has no AI controller, add one?", "Give AI?", list("Yes", "No")) == "Yes" + if (!create_controller) + return + var/run_with_mind = tgui_alert(user, "Run AI controller while the target has a client?", "Override Client?", list("Yes", "No")) + if (isnull(run_with_mind)) + return + if (QDELETED(my_guy)) + to_chat(user, span_warning("Target ceased to exist.")) + return + my_guy.ai_controller = new /datum/ai_controller/basic_controller/talk(my_guy) + if (run_with_mind == "Yes") + var/datum/ai_controller/guy_controller = my_guy.ai_controller + guy_controller.continue_processing_when_client = TRUE + guy_controller.reset_ai_status() + + var/speech_chance + var/list/spoken_lines + var/list/audible_emotes + var/list/visible_emotes + var/list/sounds + + speech_chance = tgui_input_number(user, "Enter chance per second to say something", "Speech Chance", default = 2, min_value = 0, max_value = 100, round_value = FALSE) + if (isnull(speech_chance)) + return + + var/add_another + var/next_line + + add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] spoken line?", "Spoken Lines", list("Yes", "No")) + while (add_another == "Yes") + next_line = tgui_input_text(user, "Enter [length(spoken_lines) ? "another" : "a"] thing spoken out loud.", "Spoken Lines") + if (isnull(next_line)) + return + LAZYADD(spoken_lines, next_line) + add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] spoken line?", "Spoken Lines", list("Yes", "No")) + if (isnull(add_another)) + return + + add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] emote which people can hear?", "Audible Emotes", list("Yes", "No")) + while (add_another == "Yes") + next_line = tgui_input_text(user, "Enter [length(spoken_lines) ? "another" : "an"] emote which people can hear.", "Audible Emotes") + if (isnull(next_line)) + return + LAZYADD(audible_emotes, next_line) + add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] emote which people can hear?", "Audible Emotes", list("Yes", "No")) + if (isnull(add_another)) + return + + add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] emote which people can see?", "Visible Emotes", list("Yes", "No")) + while (add_another == "Yes") + next_line = tgui_input_text(user, "Enter [length(spoken_lines) ? "another" : "an"] emote which people can see.", "Visible Emotes") + if (isnull(next_line)) + return + LAZYADD(visible_emotes, next_line) + add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] emote which people can see?", "Visible Emotes", list("Yes", "No")) + if (isnull(add_another)) + return + + if (!length(spoken_lines) && !length(audible_emotes) && !length(visible_emotes)) + return // Well you didn't tell it to say anything... + + if (length(spoken_lines) || length(audible_emotes)) + add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] sound to play when doing something audible?", "Sounds", list("Yes", "No")) + while (add_another == "Yes") + next_line = input("", "Select sound",) as null|sound + if (isnull(next_line)) + return + LAZYADD(sounds, next_line) + add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] sound to play when doing something audible?", "Sounds", list("Yes", "No")) + if (isnull(add_another)) + return + + if (QDELETED(my_guy)) + to_chat(user, span_warning("Target stopped existing.")) + return + + var/datum/ai_controller/our_controller = my_guy.ai_controller + if (length(spoken_lines)) + spoken_lines = string_list(spoken_lines) + if (length(audible_emotes)) + audible_emotes = string_list(audible_emotes) + if (length(visible_emotes)) + visible_emotes = string_list(visible_emotes) + + var/list/emotes = list( + BB_EMOTE_SAY = spoken_lines, + BB_EMOTE_HEAR = audible_emotes, + BB_EMOTE_SEE = visible_emotes, + BB_EMOTE_SOUND = sounds, + BB_SPEAK_CHANCE = speech_chance, + ) + our_controller.set_blackboard_key(BB_BASIC_MOB_SPEAK_LINES, emotes) + + var/behaviour_exists = !!(locate(/datum/ai_planning_subtree/random_speech/blackboard) in our_controller.planning_subtrees) + if (behaviour_exists) + return + our_controller.planning_subtrees = list(GLOB.ai_subtrees[/datum/ai_planning_subtree/random_speech/blackboard]) + our_controller.planning_subtrees diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index e6d552295c2..91ffef4c3fa 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1372,7 +1372,7 @@ VV_DROPDOWN_OPTION("", "---------") VV_DROPDOWN_OPTION(VV_HK_GIB, "Gib") VV_DROPDOWN_OPTION(VV_HK_GIVE_AI, "Give AI Controller") - VV_DROPDOWN_OPTION(VV_HK_REMOVE_SPELL, "Remove Spell") + VV_DROPDOWN_OPTION(VV_HK_GIVE_AI_SPEECH, "Give Random AI Speech") VV_DROPDOWN_OPTION(VV_HK_GIVE_SPELL, "Give Spell") VV_DROPDOWN_OPTION(VV_HK_REMOVE_SPELL, "Remove Spell") VV_DROPDOWN_OPTION(VV_HK_GIVE_MOB_ACTION, "Give Mob Ability") @@ -1417,6 +1417,9 @@ if(href_list[VV_HK_GIVE_AI]) return SSadmin_verbs.dynamic_invoke_verb(usr, /datum/admin_verb/give_ai_controller, src) + if(href_list[VV_HK_GIVE_AI_SPEECH]) + return SSadmin_verbs.dynamic_invoke_verb(usr, /datum/admin_verb/give_ai_speech, src) + if(href_list[VV_HK_GIVE_MOB_ACTION]) return SSadmin_verbs.dynamic_invoke_verb(usr, /datum/admin_verb/give_mob_action, src)