Skip to content

Commit

Permalink
[MIRROR] Light dreaming refactor + new dream [MDB IGNORE] (#24499)
Browse files Browse the repository at this point in the history
* 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

:cl:
add: New dream that plays sound at you
/:cl:

* Light dreaming refactor + new dream

---------

Co-authored-by: Emmett Gaines <[email protected]>
  • Loading branch information
2 people authored and FFMirrorBot committed Oct 22, 2023
1 parent 25f3841 commit 54f8dbb
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 33 deletions.
21 changes: 18 additions & 3 deletions code/__HELPERS/files.dm
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ GLOBAL_VAR_INIT(fileaccess_timer, 0)
#undef FTPDELAY
#undef ADMIN_FTPDELAY_MODIFIER

/proc/pathwalk(path)
/**
* Takes a directory and returns every file within every sub directory.
* If extensions_filter is provided then only files that end in that extension are given back.
* If extensions_filter is a list, any file that matches at least one entry is given back.
*/
/proc/pathwalk(path, extensions_filter)
var/list/jobs = list(path)
var/list/filenames = list()

Expand All @@ -82,9 +87,19 @@ GLOBAL_VAR_INIT(fileaccess_timer, 0)
for(var/new_filename in new_filenames)
// if filename ends in / it is a directory, append to currdir
if(findtext(new_filename, "/", -1))
jobs += current_dir + new_filename
jobs += "[current_dir][new_filename]"
continue
// filename extension filtering
if(extensions_filter)
if(islist(extensions_filter))
for(var/allowed_extension in extensions_filter)
if(endswith(new_filename, allowed_extension))
filenames += "[current_dir][new_filename]"
break
else if(endswith(new_filename, extensions_filter))
filenames += "[current_dir][new_filename]"
else
filenames += current_dir + new_filename
filenames += "[current_dir][new_filename]"
return filenames

/proc/pathflatten(path)
Expand Down
5 changes: 5 additions & 0 deletions code/__HELPERS/text.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1191,3 +1191,8 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
text2num(semver_regex.group[2]),
text2num(semver_regex.group[3]),
)

/// Returns TRUE if the input_text ends with the ending
/proc/endswith(input_text, ending)
var/input_length = LAZYLEN(ending)
return !!findtext(input_text, ending, -input_length)
26 changes: 25 additions & 1 deletion code/controllers/subsystem/sounds.dm
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ SUBSYSTEM_DEF(sounds)
/// higher reserve position - decremented and incremented to reserve sound channels, anything above this is reserved. The channel at this index is the highest unreserved channel.
var/channel_reserve_high

/// All valid sound files in the sound directory
var/list/all_sounds

/datum/controller/subsystem/sounds/Initialize()
setup_available_channels()
find_all_available_sounds()
return SS_INIT_SUCCESS

/datum/controller/subsystem/sounds/proc/setup_available_channels()
Expand All @@ -37,6 +41,26 @@ SUBSYSTEM_DEF(sounds)
channel_random_low = 1
channel_reserve_high = length(channel_list)

/datum/controller/subsystem/sounds/proc/find_all_available_sounds()
all_sounds = list()
// Put more common extensions first to speed this up a bit
var/static/list/valid_file_extensions = list(
".ogg",
".wav",
".mid",
".midi",
".mod",
".it",
".s3m",
".xm",
".oxm",
".raw",
".wma",
".aiff",
)

all_sounds = pathwalk("sound/", valid_file_extensions)

/// Removes a channel from using list.
/datum/controller/subsystem/sounds/proc/free_sound_channel(channel)
var/text_channel = num2text(channel)
Expand Down Expand Up @@ -78,7 +102,7 @@ SUBSYSTEM_DEF(sounds)
CRASH("Attempted to reserve sound channel without datum using the managed proc.")
.= reserve_channel()
if(!.)
return FALSE
CRASH("No more sound channels can be reserved")
var/text_channel = num2text(.)
using_channels[text_channel] = D
LAZYINITLIST(using_channels_by_datum[D])
Expand Down
146 changes: 117 additions & 29 deletions code/modules/flufftext/Dreaming.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -40,7 +120,7 @@
fragment = replacetext(fragment, "%ADJECTIVE% ", "")
if(findtext(fragment, "%A% "))
fragment = "\a [replacetext(fragment, "%A% ", "")]"
dream_fragments += fragment
. += fragment

//Verb
fragment = ""
Expand All @@ -51,10 +131,9 @@
else
fragment += "will "
fragment += pick(GLOB.verbs)
dream_fragments += fragment
. += fragment

if(prob(25))
dream_sequence(dream_fragments)
return

//Object
Expand All @@ -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))

0 comments on commit 54f8dbb

Please sign in to comment.