mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-29 16:18:01 +01:00
[MIRROR] Light dreaming refactor + new dream [MDB IGNORE] (#24499)
* 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 /🆑 * Light dreaming refactor + new dream --------- Co-authored-by: Emmett Gaines <ninjanomnom@gmail.com>
This commit is contained in:
co-authored by
Emmett Gaines
parent
a826acb55e
commit
25cd74e9a3
+18
-3
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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])
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user