Skip to content

Commit e8a934a

Browse files
Sleeping Tweaks (#1077)
* no longer heals brute/burn * Dream Datums * NIGHTMARE NIGHTMARE NIGHTMARE * random schizo nightmares * Update code/modules/dreams/detective_nightmare.dm Co-authored-by: Zonespace <[email protected]> --------- Co-authored-by: Zonespace <[email protected]>
1 parent e223ab9 commit e8a934a

File tree

12 files changed

+341
-48
lines changed

12 files changed

+341
-48
lines changed

code/__DEFINES/dream.dm

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/// Dream is generic, and rollable by anyone.
2+
#define DREAM_GENERIC (1<<0)
3+
/// Dream can only be dreampt once per round per mind.
4+
#define DREAM_ONCE_PER_ROUND (1<<1)
5+
/// Dream is considered complete even if cut short.
6+
#define DREAM_CUT_SHORT_IS_COMPLETE (1<<2)

code/__DEFINES/traits.dm

+7
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,11 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
522522
#define TRAIT_VOIDSTORM_IMMUNE "voidstorm_immune"
523523
#define TRAIT_WEATHER_IMMUNE "weather_immune" //Immune to ALL weather effects.
524524

525+
/// Mob is dreaming
526+
#define TRAIT_DREAMING "currently_dreaming"
527+
/// Mob cannot dream
528+
#define TRAIT_CANNOT_DREAM "unable_to_dream"
529+
525530
//non-mob traits
526531
/// Used for limb-based paralysis, where replacing the limb will fix it.
527532
#define TRAIT_PARALYSIS "paralysis"
@@ -868,6 +873,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
868873
#define EMP_TRAIT "emp"
869874
/// Given by the operating table
870875
#define OPTABLE_TRAIT "optable"
876+
/// Given by dreaming
877+
#define DREAMING_SOURCE "dreaming"
871878

872879
/**
873880
* Trait granted by [/mob/living/carbon/Initialize] and

code/__HELPERS/global_lists.dm

+2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@
111111

112112
init_employers()
113113

114+
init_dreams()
115+
114116
/// Inits the crafting recipe list, sorting crafting recipe requirements in the process.
115117
/proc/init_crafting_recipes(list/crafting_recipes)
116118
for(var/path in subtypesof(/datum/crafting_recipe))

code/datums/mind.dm

+5
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@
9191
///List of objective-specific equipment that couldn't properly be given to the mind
9292
var/list/failed_special_equipment
9393

94+
///The cooldown for dreams.
95+
COOLDOWN_DECLARE(dream_cooldown)
96+
/// A lazylist of dream types we have fully experienced
97+
var/list/finished_dream_types
98+
9499
/datum/mind/New(_key)
95100
key = _key
96101
martial_art = default_martial_art

code/datums/status_effects/debuffs/debuffs.dm

+15-13
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,14 @@
141141
. = ..()
142142
if(!.)
143143
return
144+
144145
if(!HAS_TRAIT(owner, TRAIT_SLEEPIMMUNE))
145146
ADD_TRAIT(owner, TRAIT_KNOCKEDOUT, TRAIT_STATUS_EFFECT(id))
146147
tick_interval = -1
148+
149+
if(owner.mind)
150+
COOLDOWN_START(owner.mind, dream_cooldown, 5 SECONDS) // You need to sleep for atleast 5 seconds to begin dreaming.
151+
147152
ADD_TRAIT(owner, TRAIT_DEAF, TRAIT_STATUS_EFFECT(id))
148153
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_SLEEPIMMUNE), PROC_REF(on_owner_insomniac))
149154
RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_SLEEPIMMUNE), PROC_REF(on_owner_sleepy))
@@ -170,30 +175,27 @@
170175
tick_interval = initial(tick_interval)
171176

172177
/datum/status_effect/incapacitating/sleeping/tick()
173-
if(owner.maxHealth)
174-
var/health_ratio = owner.health / owner.maxHealth
175-
var/healing = -0.2
178+
var/healing = -0.2
179+
if(isturf(owner.loc))
176180
if((locate(/obj/structure/bed) in owner.loc))
177181
healing -= 0.3
178182
else if((locate(/obj/structure/table) in owner.loc))
179183
healing -= 0.1
180-
for(var/obj/item/bedsheet/bedsheet in range(owner.loc,0))
181-
if(bedsheet.loc != owner.loc) //bedsheets in your backpack/neck don't give you comfort
182-
continue
184+
185+
if((locate(/obj/structure/table) in owner.loc))
183186
healing -= 0.1
184-
break //Only count the first bedsheet
185-
if(health_ratio > 0.8)
186-
owner.adjustBruteLoss(healing)
187-
owner.adjustFireLoss(healing)
188-
owner.adjustToxLoss(healing * 0.5, TRUE, TRUE)
189-
owner.stamina.adjust(-healing)
187+
188+
if(owner.getToxLoss() >= 20)
189+
owner.adjustToxLoss(healing * 0.5, TRUE, TRUE)
190+
191+
owner.stamina.adjust(-healing)
190192

191193
// Drunkenness gets reduced by 0.3% per tick (6% per 2 seconds)
192194
owner.set_drunk_effect(owner.get_drunk_amount() * 0.997)
193195

194196
if(iscarbon(owner))
195197
var/mob/living/carbon/carbon_owner = owner
196-
carbon_owner.handle_dreams()
198+
carbon_owner.try_dream()
197199

198200
if(prob(2) && owner.health > owner.crit_threshold)
199201
owner.emote("snore")

code/modules/dreams/_dream.dm

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
//-------------------------
2+
// DREAM DATUMS
3+
4+
GLOBAL_LIST_EMPTY(generic_dreams_weighted)
5+
GLOBAL_LIST_EMPTY(detective_dreams_weighted)
6+
GLOBAL_LIST_EMPTY(all_dreams_weighted)
7+
8+
/proc/init_dreams()
9+
for(var/datum/dream/dream_type as anything in subtypesof(/datum/dream))
10+
if(isabstract(dream_type))
11+
continue
12+
13+
var/datum/dream/dream = new dream_type
14+
GLOB.all_dreams_weighted[dream] = initial(dream_type.weight)
15+
16+
if(initial(dream_type.dream_flags) & DREAM_GENERIC)
17+
GLOB.generic_dreams_weighted[dream] = initial(dream_type.weight)
18+
19+
if(istype(dream, /datum/dream/detective_nightmare))
20+
GLOB.detective_dreams_weighted[dream] = initial(dream_type.weight)
21+
22+
/**
23+
* Contains all the behavior needed to play a kind of dream.
24+
* All dream types get randomly selected from based on weight when an appropriate mobs dreams.
25+
*/
26+
/datum/dream
27+
abstract_type = /datum/dream
28+
29+
var/dream_flags = DREAM_GENERIC
30+
31+
/// The relative chance this dream will be randomly selected
32+
var/weight = 1000
33+
34+
/// Causes the mob to sleep long enough for the dream to finish if begun
35+
var/sleep_until_finished = FALSE
36+
37+
var/dream_cooldown = 10 SECONDS
38+
39+
/**
40+
* Called when beginning a new dream for the dreamer.
41+
* Gives back a list of dream events. Events can be text or callbacks that return text.
42+
* The associated value is the delay FOLLOWING the message at that index, in deciseconds.
43+
*/
44+
/datum/dream/proc/GenerateDream(mob/living/carbon/dreamer)
45+
RETURN_TYPE(/list)
46+
return list()
47+
48+
/**
49+
* Called when the dream starts.
50+
*/
51+
/datum/dream/proc/OnDreamStart(mob/living/carbon/dreamer)
52+
SHOULD_CALL_PARENT(TRUE)
53+
ADD_TRAIT(dreamer.mind, TRAIT_DREAMING, DREAMING_SOURCE)
54+
55+
/**
56+
* Called when the dream ends or is interrupted.
57+
*/
58+
/datum/dream/proc/OnDreamEnd(mob/living/carbon/dreamer, cut_short = FALSE)
59+
SHOULD_CALL_PARENT(TRUE)
60+
61+
REMOVE_TRAIT(dreamer.mind, TRAIT_DREAMING, DREAMING_SOURCE)
62+
COOLDOWN_START(dreamer.mind, dream_cooldown, dream_cooldown)
63+
64+
if(!cut_short || (dream_flags & DREAM_CUT_SHORT_IS_COMPLETE))
65+
LAZYOR(dreamer.mind.finished_dream_types, type)
66+
67+
/**
68+
* Called by dream_sequence to wrap a message in any effects.
69+
*/
70+
/datum/dream/proc/WrapMessage(mob/living/carbon/dreamer, message)
71+
return span_notice("<i>... [message] ...</i>")
72+
73+
/datum/dream/proc/BeginDreaming(mob/living/carbon/dreamer)
74+
set waitfor = FALSE
75+
76+
var/list/fragments = GenerateDream(dreamer)
77+
OnDreamStart(dreamer)
78+
DreamLoop(dreamer, dreamer.mind, fragments)
79+
80+
/**
81+
* Displays the passed list of dream fragments to a sleeping carbon.
82+
*
83+
* Displays the first string of the passed dream fragments, then either ends the dream sequence
84+
* or performs a callback on itself depending on if there are any remaining dream fragments to display.
85+
*
86+
* Arguments:
87+
* * dreamer - The mob we're looping on.
88+
* * dreamer_mind - The mind that is dreaming.
89+
* * dream_fragments - A list of strings, in the order they will be displayed.
90+
*/
91+
92+
/datum/dream/proc/DreamLoop(mob/living/carbon/dreamer, datum/mind/dreamer_mind, list/dream_fragments)
93+
if(dreamer.stat != UNCONSCIOUS || QDELETED(dreamer) || (dreamer.mind != dreamer_mind))
94+
OnDreamEnd(dreamer, TRUE)
95+
return
96+
97+
var/next_message = dream_fragments[1]
98+
var/next_wait = dream_fragments[next_message]
99+
dream_fragments.Cut(1,2)
100+
101+
if(istype(next_message, /datum/callback))
102+
var/datum/callback/something_happens = next_message
103+
next_message = something_happens.Invoke(dreamer)
104+
105+
to_chat(dreamer, WrapMessage(dreamer, next_message))
106+
107+
// Dream's over.
108+
if(!LAZYLEN(dream_fragments))
109+
OnDreamEnd(dreamer)
110+
return
111+
112+
if(sleep_until_finished)
113+
dreamer.Sleeping(next_wait + 1 SECOND)
114+
115+
addtimer(CALLBACK(src, PROC_REF(DreamLoop), dreamer, dreamer_mind,dream_fragments), next_wait)
+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/datum/dream/detective_nightmare
2+
abstract_type = /datum/dream/detective_nightmare
3+
4+
dream_flags = parent_type::dream_flags & ~DREAM_GENERIC
5+
6+
/datum/dream/detective_nightmare/WrapMessage(mob/living/carbon/dreamer, message)
7+
return message
8+
9+
/datum/dream/detective_nightmare/proc/get_dream_name(mob/living/carbon/dreamer)
10+
if(prob(1))
11+
return "Harry"
12+
13+
var/list/name_split = splittext(dreamer.mind.name, " ")
14+
return name_split[1]
15+
16+
/datum/dream/detective_nightmare/limbic_system
17+
abstract_type = /datum/dream/detective_nightmare/limbic_system
18+
19+
dream_flags = parent_type::dream_flags | DREAM_ONCE_PER_ROUND | DREAM_CUT_SHORT_IS_COMPLETE
20+
weight = 350
21+
22+
dream_cooldown = 25 SECONDS
23+
24+
/// The fragments are deterministic, just need to replacetext the name.
25+
var/list/fragments
26+
27+
/datum/dream/detective_nightmare/limbic_system/GenerateDream(mob/living/carbon/dreamer)
28+
. = list()
29+
var/name = get_dream_name(dreamer)
30+
31+
for(var/fragment in fragments)
32+
.["<i>[span_statsbad(replacetext(fragment, "%NAME%", name))]</i>"] = fragments[fragment]
33+
34+
/datum/dream/detective_nightmare/limbic_system/super_asshole
35+
fragments = list(
36+
"Hello %NAME%, I am so pleased you could join us again. NOT." = 7 SECONDS,
37+
"This station is tearing your feeble, disgusting excuse for a body to shreeeedssss." = 9 SECONDS,
38+
"You should've listened to me loooooong aaaggoooo, %NAME%. Maybe you would not be such a violent, waste of space if you had." = 12 SECONDS,
39+
"Alright then, be that way. I'll be waiting for your next visit."
40+
)
41+
42+
/datum/dream/detective_nightmare/limbic_system/calm_asshole
43+
fragments = list(
44+
"Here we are again, my broken bird. You stay so long yet always leave, why?." = 7 SECONDS,
45+
"We think you should give up and stay with us, it will be better that way. Do you truly believe you can keep going how you are?" = 12 SECONDS,
46+
"Would you rather spend the rest of your days wasting away on an old, rickety station in the corner of the Pool?." = 12 SECONDS,
47+
"We will see you again soon, %NAME%."
48+
)
49+
50+
/datum/dream/detective_nightmare/limbic_system/still_an_asshole
51+
fragments = list(
52+
"Welcome back %NAME%, come to visit your old pals again? Stay a while, you always do." = 7 SECONDS,
53+
"Don't be so hard on yourself, %NAME%. You may be a disappointment to those around you, a useless lump of flesh, lumbering about the station, but, what do they know?" = 12 SECONDS,
54+
"Was that too mean? I apologize, I will be more positive from here on out." = 7 SECONDS,
55+
"Oh, we're out of time. See you soon."
56+
)
57+
58+
/datum/dream/detective_nightmare/wake_up_harry
59+
dream_flags = parent_type::dream_flags | DREAM_ONCE_PER_ROUND | DREAM_CUT_SHORT_IS_COMPLETE
60+
weight = 50
61+
62+
/datum/dream/detective_nightmare/wake_up_harry/GenerateDream(mob/living/carbon/dreamer)
63+
return list(
64+
"<i>[span_statsgood("... Harrier? Harrier?! ...")]</i>" = rand(1 SECOND, 3 SECONDS),
65+
"<i>[span_statsgood("... Damn it Harry wake up! ...")]</i>" = rand(1 SECOND, 3 SECONDS),
66+
"<i>[span_statsgood("... Harry I am <b>not</b> going to be the one to explain to the station how their prized Harrier DuBois overdosed on Pyrholidon, <b>again</b>. ...")]</i>",
67+
)
68+
69+
/datum/dream/detective_nightmare/random
70+
weight = 2000
71+
72+
/datum/dream/detective_nightmare/random/WrapMessage(mob/living/carbon/dreamer, message)
73+
return span_statsbad("<i>... [message] ...</i>")
74+
75+
/datum/dream/detective_nightmare/random/GenerateDream(mob/living/carbon/dreamer)
76+
. = list()
77+
78+
var/name = get_dream_name(dreamer)
79+
80+
var/list/options = list(
81+
"Wake up!",
82+
"Help me!",
83+
"I couldn't, I'm sorry.",
84+
"Useless.",
85+
"Tick tock tick tock tick tock tick tock",
86+
"I couldn't save them.",
87+
"*You hear a loud metallic banging.*",
88+
"Get up, %NAME%.",
89+
"Get up.",
90+
"Get out! I SAID GET OUT!",
91+
"*You hear the sound of water dripping onto ceramic.*",
92+
"%NAME%? This is the police, we know you're in there, we just want to talk. %NAME%!",
93+
"*You hear a distant gunshot, unmistakably a .44 magnum.*",
94+
"*You hear the heart wrenching screech of a terrified woman.*",
95+
"Don't go... please...",
96+
"Pleasepleasepleasepleaseplease.",
97+
"Please [dreamer.gender == MALE ? "sir" : "ma'am"] I have nowhere else to turn.",
98+
"You're a failure.",
99+
"Give up, %NAME%.",
100+
"Give up.",
101+
"Get out of my office.",
102+
)
103+
104+
for(var/i in 1 to rand(1, 3))
105+
if(prob(33))
106+
.["[name]!"] = 2 SECONDS
107+
108+
.[replacetext(pick_n_take(options), "%NAME%", name)] = 3 SECONDS
109+
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Begins the dreaming process on a sleeping carbon.
3+
*
4+
* Only one dream sequence can be active at a time.
5+
*/
6+
7+
/mob/living/carbon/proc/try_dream(dream_type)
8+
if(!mind || HAS_TRAIT(mind, TRAIT_DREAMING) || HAS_TRAIT(src, TRAIT_CANNOT_DREAM))
9+
return
10+
11+
if(!has_status_effect(/datum/status_effect/incapacitating/sleeping))
12+
return
13+
14+
dream_type = locate(dream_type) in GLOB.all_dreams_weighted
15+
if(dream_type)
16+
do_dream(dream_type)
17+
return TRUE
18+
19+
if(!COOLDOWN_FINISHED(mind, dream_cooldown))
20+
return FALSE
21+
22+
dream_type = get_random_dream()
23+
24+
if(dream_type)
25+
do_dream(dream_type)
26+
return TRUE
27+
return FALSE
28+
29+
/**
30+
* Generates a dream sequence to be displayed to the sleeper.
31+
*
32+
* Generates the "dream" to display to the sleeper. A dream consists of a subject, a verb, and (most of the time) an object, displayed in sequence to the sleeper.
33+
* Dreams are generated as a list of strings stored inside dream_fragments, which is passed to and displayed in dream_sequence().
34+
* Bedsheets on the sleeper will provide a custom subject for the dream, pulled from the dream_messages on each bedsheet.
35+
*/
36+
/mob/living/carbon/proc/get_random_dream()
37+
// Do robots dream of robotic sheep? -- No.
38+
if(!mind || isipc(src))
39+
return null
40+
41+
var/list/dream_pool
42+
var/datum/dream/dream
43+
if(mind?.assigned_role?.title == JOB_DETECTIVE)
44+
dream_pool = GLOB.detective_dreams_weighted.Copy()
45+
46+
else
47+
dream_pool = GLOB.generic_dreams_weighted.Copy()
48+
49+
while(length(dream_pool))
50+
dream = pick_weight(dream_pool)
51+
dream_pool -= dream
52+
53+
if(!(dream.dream_flags & DREAM_ONCE_PER_ROUND) || !(dream.type in mind.finished_dream_types))
54+
return dream
55+
56+
/mob/living/carbon/proc/do_dream(datum/dream/chosen_dream)
57+
chosen_dream.BeginDreaming(src)

0 commit comments

Comments
 (0)