Light dreaming refactor + new dream (#78996)

## About The Pull Request

Makes it so new dream types can be more easily added to the game with
effects within them beyond just some text being displayed if you want. I
added a dream that plays a random sound file available in the game files
to demonstrate how it's used. Mainly I wanted this out of the way for
another thing I'm working on.

I also made it so if the sound subsystem fails to reserve a channel it
throws a runtime since this happening is likely always going to result
in a bug, like it did while I was testing this.

## Changelog

🆑
add: New dream that plays sound at you
/🆑
This commit is contained in:
Emmett Gaines
2023-10-21 13:46:35 -04:00
committed by GitHub
parent 388d70a35c
commit d8e826f570
4 changed files with 165 additions and 33 deletions
+117 -29
View File
@@ -19,14 +19,94 @@
/mob/living/carbon/proc/dream()
set waitfor = FALSE
var/list/dream_fragments = list()
var/datum/dream/chosen_dream = pick_weight(GLOB.dreams)
dreaming = TRUE
dream_sequence(chosen_dream.GenerateDream(src), chosen_dream)
/**
* Displays the passed list of dream fragments to a sleeping carbon.
*
* Displays the first string of the passed dream fragments, then either ends the dream sequence
* or performs a callback on itself depending on if there are any remaining dream fragments to display.
*
* Arguments:
* * dream_fragments - A list of strings, in the order they will be displayed.
* * current_dream - The dream datum used for the current dream
*/
/mob/living/carbon/proc/dream_sequence(list/dream_fragments, datum/dream/current_dream)
if(stat != UNCONSCIOUS || HAS_TRAIT(src, TRAIT_CRITICAL_CONDITION))
dreaming = FALSE
current_dream.OnDreamEnd(src)
return
var/next_message = dream_fragments[1]
dream_fragments.Cut(1,2)
if(istype(next_message, /datum/callback))
var/datum/callback/something_happens = next_message
next_message = something_happens.InvokeAsync(src)
to_chat(src, span_notice("<i>... [next_message] ...</i>"))
if(LAZYLEN(dream_fragments))
var/next_wait = rand(10, 30)
if(current_dream.sleep_until_finished)
AdjustSleeping(next_wait)
addtimer(CALLBACK(src, PROC_REF(dream_sequence), dream_fragments, current_dream), next_wait)
else
dreaming = FALSE
current_dream.OnDreamEnd(src)
//-------------------------
// DREAM DATUMS
GLOBAL_LIST_INIT(dreams, populate_dream_list())
/proc/populate_dream_list()
var/list/output = list()
for(var/datum/dream/dream_type as anything in subtypesof(/datum/dream))
output[new dream_type] = initial(dream_type.weight)
return output
/**
* Contains all the behavior needed to play a kind of dream.
* All dream types get randomly selected from based on weight when an appropriate mobs dreams.
*/
/datum/dream
/// The relative chance this dream will be randomly selected
var/weight = 0
/// Causes the mob to sleep long enough for the dream to finish if begun
var/sleep_until_finished = FALSE
/**
* Called when beginning a new dream for the dreamer.
* Gives back a list of dream events. Events can be text or callbacks that return text.
*/
/datum/dream/proc/GenerateDream(mob/living/carbon/dreamer)
return list()
/**
* Called when the dream ends or is interrupted.
*/
/datum/dream/proc/OnDreamEnd(mob/living/carbon/dreamer)
return
/// The classic random dream of various words that might form a cohesive narrative, but usually wont
/datum/dream/random
weight = 1000
/datum/dream/random/GenerateDream(mob/living/carbon/dreamer)
var/list/custom_dream_nouns = list()
var/fragment = ""
for(var/obj/item/bedsheet/sheet in loc)
for(var/obj/item/bedsheet/sheet in dreamer.loc)
custom_dream_nouns += sheet.dream_messages
dream_fragments += "you see"
. = list()
. += "you see"
//Subject
if(custom_dream_nouns.len && prob(90))
@@ -40,7 +120,7 @@
fragment = replacetext(fragment, "%ADJECTIVE% ", "")
if(findtext(fragment, "%A% "))
fragment = "\a [replacetext(fragment, "%A% ", "")]"
dream_fragments += fragment
. += fragment
//Verb
fragment = ""
@@ -51,10 +131,9 @@
else
fragment += "will "
fragment += pick(GLOB.verbs)
dream_fragments += fragment
. += fragment
if(prob(25))
dream_sequence(dream_fragments)
return
//Object
@@ -66,29 +145,38 @@
fragment = replacetext(fragment, "%ADJECTIVE% ", "")
if(findtext(fragment, "%A% "))
fragment = "\a [replacetext(fragment, "%A% ", "")]"
dream_fragments += fragment
. += fragment
dreaming = TRUE
dream_sequence(dream_fragments)
/// Dream plays a random sound at you, chosen from all sounds in the folder
/datum/dream/hear_something
weight = 500
/**
* Displays the passed list of dream fragments to a sleeping carbon.
*
* Displays the first string of the passed dream fragments, then either ends the dream sequence
* or performs a callback on itself depending on if there are any remaining dream fragments to display.
*
* Arguments:
* * dream_fragments - A list of strings, in the order they will be displayed.
*/
var/reserved_sound_channel
/mob/living/carbon/proc/dream_sequence(list/dream_fragments)
if(stat != UNCONSCIOUS || HAS_TRAIT(src, TRAIT_CRITICAL_CONDITION))
dreaming = FALSE
return
var/next_message = dream_fragments[1]
dream_fragments.Cut(1,2)
to_chat(src, span_notice("<i>... [next_message] ...</i>"))
if(LAZYLEN(dream_fragments))
addtimer(CALLBACK(src, PROC_REF(dream_sequence), dream_fragments), rand(10,30))
else
dreaming = FALSE
/datum/dream/hear_something/New()
. = ..()
RegisterSignal(SSsounds, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(ReserveSoundChannel))
/datum/dream/hear_something/GenerateDream(mob/living/carbon/dreamer)
. = ..()
. += pick("you wind up a toy", "you hear something strange", "you pick out a record to play", "you hit shuffle on your music player")
. += CALLBACK(src, PROC_REF(PlayRandomSound))
. += "it reminds you of something"
/datum/dream/hear_something/OnDreamEnd(mob/living/carbon/dreamer)
. = ..()
// In case we play some long ass music track
addtimer(CALLBACK(src, PROC_REF(StopSound), dreamer), 5 SECONDS)
/datum/dream/hear_something/proc/ReserveSoundChannel()
reserved_sound_channel = SSsounds.reserve_sound_channel(src)
UnregisterSignal(SSsounds, COMSIG_SUBSYSTEM_POST_INITIALIZE)
/datum/dream/hear_something/proc/PlayRandomSound(mob/living/carbon/dreamer)
var/sound/random_sound = sound(pick(SSsounds.all_sounds), channel=reserved_sound_channel)
random_sound.status = SOUND_STREAM
SEND_SOUND(dreamer, random_sound)
return "you hear something you weren't expecting!"
/datum/dream/hear_something/proc/StopSound(mob/living/carbon/dreamer)
SEND_SOUND(dreamer, sound(channel=reserved_sound_channel))