Files
Bubberstation/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm
Jacquerel d7264ce0f3 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.
2025-02-14 14:06:27 -06:00

243 lines
8.8 KiB
Plaintext

/datum/ai_planning_subtree/random_speech
//The chance of an emote occurring each second
var/speech_chance = 0
///Hearable emotes
var/list/emote_hear = list()
///Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps
var/list/emote_see = list()
///Possible lines of speech the AI can have
var/list/speak = list()
///The sound effects associated with this speech, if any
var/list/sound = list()
/datum/ai_planning_subtree/random_speech/New()
. = ..()
if(speak)
speak = string_list(speak)
if(sound)
sound = string_list(sound)
if(emote_hear)
emote_hear = string_list(emote_hear)
if(emote_see)
emote_see = string_list(emote_see)
/datum/ai_planning_subtree/random_speech/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
if(!SPT_PROB(speech_chance, seconds_per_tick))
return
speak(controller)
/// Actually perform an action
/datum/ai_planning_subtree/random_speech/proc/speak(datum/ai_controller/controller)
var/audible_emotes_length = emote_hear?.len
var/non_audible_emotes_length = emote_see?.len
var/speak_lines_length = speak?.len
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
if(random_number_in_range <= audible_emotes_length)
controller.queue_behavior(/datum/ai_behavior/perform_emote, pick(emote_hear), sound_to_play)
else if(random_number_in_range <= (audible_emotes_length + non_audible_emotes_length))
controller.queue_behavior(/datum/ai_behavior/perform_emote, pick(emote_see))
else
controller.queue_behavior(/datum/ai_behavior/perform_speech, pick(speak), sound_to_play)
/datum/ai_planning_subtree/random_speech/insect
speech_chance = 5
sound = list('sound/mobs/non-humanoids/insect/chitter.ogg')
emote_hear = list("chitters.")
/datum/ai_planning_subtree/random_speech/mothroach
speech_chance = 15
emote_hear = list("flutters.")
/datum/ai_planning_subtree/random_speech/mouse
speech_chance = 1
speak = list("Squeak!", "SQUEAK!", "Squeak?")
sound = list('sound/mobs/non-humanoids/mouse/mousesqueek.ogg')
emote_hear = list("squeaks.")
emote_see = list("runs in a circle.", "shakes.")
/datum/ai_planning_subtree/random_speech/frog
speech_chance = 3
emote_see = list("jumps in a circle.", "shakes.")
/datum/ai_planning_subtree/random_speech/lizard // all of these have to be three words long or i'm killing you. you're dead.
speech_chance = 3
emote_hear = list("stamps around some.", "hisses a bit.")
emote_see = list("blehs the tongue.", "tilts the head.", "does a spin.")
/datum/ai_planning_subtree/random_speech/sheep
speech_chance = 5
speak = list("baaa","baaaAAAAAH!","baaah")
sound = list('sound/mobs/non-humanoids/sheep/sheep1.ogg', 'sound/mobs/non-humanoids/sheep/sheep2.ogg', 'sound/mobs/non-humanoids/sheep/sheep3.ogg')
emote_hear = list("bleats.")
emote_see = list("shakes her head.", "stares into the distance.")
/datum/ai_planning_subtree/random_speech/rabbit
speech_chance = 10
speak = list("Mrrp.", "CHIRP!", "Mrrp?") // rabbits make some weird noises dude i don't know what to tell you
emote_hear = list("hops.")
emote_see = list("hops around.", "bounces up and down.")
/// For the easter subvariant of rabbits, these ones actually speak catchphrases.
/datum/ai_planning_subtree/random_speech/rabbit/easter
speak = list(
"Hop into Easter!",
"Come get your eggs!",
"Prizes for everyone!",
)
/// These ones have a space mask on, so their catchphrases are muffled.
/datum/ai_planning_subtree/random_speech/rabbit/easter/space
speak = list(
"Hmph mmph mmmph!",
"Mmphe mmphe mmphe!",
"Hmm mmm mmm!",
)
/datum/ai_planning_subtree/random_speech/chicken
speech_chance = 15 // really talkative ladies
speak = list("Cluck!", "BWAAAAARK BWAK BWAK BWAK!", "Bwaak bwak.")
sound = list('sound/mobs/non-humanoids/chicken/clucks.ogg', 'sound/mobs/non-humanoids/chicken/bagawk.ogg')
emote_hear = list("clucks.", "croons.")
emote_see = list("pecks at the ground.","flaps her wings viciously.")
/datum/ai_planning_subtree/random_speech/chick
speech_chance = 4
speak = list("Cherp.", "Cherp?", "Chirrup.", "Cheep!")
sound = list('sound/mobs/non-humanoids/chicken/chick_peep.ogg')
emote_hear = list("cheeps.")
emote_see = list("pecks at the ground.","flaps her tiny wings.")
/datum/ai_planning_subtree/random_speech/cow
speech_chance = 1
speak = list("moo?","moo","MOOOOOO")
sound = list('sound/mobs/non-humanoids/cow/cow.ogg')
emote_hear = list("brays.")
emote_see = list("shakes her head.")
///unlike normal cows, wisdom cows speak of wisdom and won't shut the fuck up
/datum/ai_planning_subtree/random_speech/cow/wisdom
speech_chance = 15
/datum/ai_planning_subtree/random_speech/cow/wisdom/New()
. = ..()
speak = GLOB.wisdoms //Done here so it's setup properly
sound = list()
/datum/ai_planning_subtree/random_speech/deer
speech_chance = 1
speak = list("Weeeeeeee?", "Weeee", "WEOOOOOOOOOO")
emote_hear = list("brays.")
emote_see = list("shakes her head.")
/datum/ai_planning_subtree/random_speech/dog
speech_chance = 1
/datum/ai_planning_subtree/random_speech/dog/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
if(!isdog(controller.pawn))
return
// Stay in sync with dog fashion.
var/mob/living/basic/pet/dog/dog_pawn = controller.pawn
dog_pawn.update_dog_speech(src)
return ..()
/datum/ai_planning_subtree/random_speech/faithless
speech_chance = 1
emote_see = list("wails.")
/datum/ai_planning_subtree/random_speech/garden_gnome
speech_chance = 5
speak = list("Gnot a gnelf!", "Gnot a gnoblin!", "Howdy chum!")
emote_hear = list("snores.", "burps.")
emote_see = list("blinks.")
/datum/ai_planning_subtree/random_speech/tree
speech_chance = 3
emote_see = list("photosynthesizes angrily.")
/datum/ai_planning_subtree/random_speech/pig
speech_chance = 3
speak = list("oink?","oink","snurf")
sound = list('sound/mobs/non-humanoids/pig/pig1.ogg', 'sound/mobs/non-humanoids/pig/pig2.ogg')
emote_hear = list("snorts.")
emote_see = list("sniffs around.")
/datum/ai_planning_subtree/random_speech/pony
speech_chance = 3
sound = list('sound/mobs/non-humanoids/pony/whinny01.ogg', 'sound/mobs/non-humanoids/pony/whinny02.ogg', 'sound/mobs/non-humanoids/pony/whinny03.ogg')
emote_hear = list("whinnies!")
emote_see = list("horses around.")
/datum/ai_planning_subtree/random_speech/pony/tamed
speech_chance = 3
sound = list('sound/mobs/non-humanoids/pony/snort.ogg')
emote_hear = list("snorts.")
emote_see = list("snorts.")
/datum/ai_planning_subtree/random_speech/killer_tomato
speech_chance = 3
emote_hear = list("gnashes.", "growls lowly.", "snarls.")
emote_see = list("salivates.")
/datum/ai_planning_subtree/random_speech/ant
speech_chance = 1
speak = list("BZZZZT!", "CHTCHTCHT!", "Bzzz", "ChtChtCht")
sound = list('sound/mobs/non-humanoids/insect/chitter.ogg')
emote_hear = list("buzzes.", "clacks.")
emote_see = list("shakes their head.", "twitches their antennae.")
/datum/ai_planning_subtree/random_speech/fox
speech_chance = 1
speak = list("Ack-Ack", "Ack-Ack-Ack-Ackawoooo", "Geckers", "Awoo", "Tchoff")
emote_hear = list("howls.", "barks.", "screams.")
emote_see = list("shakes their head.", "shivers.")
/datum/ai_planning_subtree/random_speech/crab
speech_chance = 1
sound = list('sound/mobs/non-humanoids/crab/claw_click.ogg')
emote_hear = list("clicks.")
emote_see = list("clacks.")
/datum/ai_planning_subtree/random_speech/penguin
speech_chance = 5
speak = list("Gah Gah!", "NOOT NOOT!", "NOOT!", "Noot", "noot", "Prah!", "Grah!")
emote_hear = list("squawks", "gakkers")
/datum/ai_planning_subtree/random_speech/bear
speech_chance = 5
emote_hear = list("rawrs.","grumbles.","grawls.", "stomps!")
emote_see = list("stares ferociously.")
/datum/ai_planning_subtree/random_speech/cats
speech_chance = 10
sound = list(SFX_CAT_MEOW)
emote_hear = list("meows.")
emote_see = list("meows.")
/datum/ai_planning_subtree/random_speech/blackboard //literal tower of babel, subtree form
speech_chance = 1
/datum/ai_planning_subtree/random_speech/blackboard/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/list/speech_lines = controller.blackboard[BB_BASIC_MOB_SPEAK_LINES]
if(isnull(speech_lines))
return ..()
// Note to future developers: this behaviour a singleton so this probably doesn't work as you would expect
// The whole speech tree really needs to be refactored because this isn't how we use AI data these days
speak = speech_lines[BB_EMOTE_SAY] || list()
emote_see = speech_lines[BB_EMOTE_SEE] || list()
emote_hear = speech_lines[BB_EMOTE_HEAR] || list()
sound = speech_lines[BB_EMOTE_SOUND] || list()
speech_chance = speech_lines[BB_SPEAK_CHANCE] ? speech_lines[BB_SPEAK_CHANCE] : initial(speech_chance)
return ..()