-
-
Notifications
You must be signed in to change notification settings - Fork 444
/
Copy pathstatus_effect.dm
283 lines (238 loc) · 10.1 KB
/
status_effect.dm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/// Status effects are used to apply temporary or permanent effects to mobs.
/// This file contains their code, plus code for applying and removing them.
/datum/status_effect
/// The ID of the effect. ID is used in adding and removing effects to check for duplicates, among other things.
var/id = "effect"
/// When set initially / in on_creation, this is how long the status effect lasts in deciseconds.
/// While processing, this becomes the world.time when the status effect will expire.
/// -1 = infinite duration.
var/duration = -1
/// When set initially / in on_creation, this is how long between [proc/tick] calls in deciseconds.
/// While processing, this becomes the world.time when the next tick will occur.
/// -1 = will stop processing, if duration is also unlimited (-1).
var/tick_interval = 1 SECONDS
/// The mob affected by the status effect.
var/mob/living/owner
/// How many of the effect can be on one mob, and/or what happens when you try to add a duplicate.
var/status_type = STATUS_EFFECT_UNIQUE
/// If TRUE, we call [proc/on_remove] when owner is deleted. Otherwise, we call [proc/be_replaced].
var/on_remove_on_mob_delete = FALSE
/// The typepath to the alert thrown by the status effect when created.
/// Status effect "name"s and "description"s are shown to the owner here.
var/alert_type = /atom/movable/screen/alert/status_effect
/// The alert itself, created in [proc/on_creation] (if alert_type is specified).
var/atom/movable/screen/alert/status_effect/linked_alert
/// Used to define if the status effect should be using SSfastprocess or SSprocessing
var/processing_speed = STATUS_EFFECT_FAST_PROCESS
/// Do we self-terminate when a fullheal is called?
var/remove_on_fullheal = FALSE
// If remove_on_fullheal is TRUE, what flag do we need to be removed?
//var/heal_flag_necessary = HEAL_STATUS
///If defined, this text will appear when the mob is examined - to use he, she etc. use "SUBJECTPRONOUN" and replace it in the examines themselves
var/examine_text
/// A particle effect, for things like embers - Should be set on update_particles()
var/obj/effect/abstract/particle_holder/particle_effect
/datum/status_effect/New(list/arguments)
on_creation(arglist(arguments))
/// Called from New() with any supplied status effect arguments.
/// Not guaranteed to exist by the end.
/// Returning FALSE from on_apply will stop on_creation and self-delete the effect.
/datum/status_effect/proc/on_creation(mob/living/new_owner, ...)
if(new_owner)
owner = new_owner
if(QDELETED(owner) || !on_apply())
qdel(src)
return
if(owner)
LAZYADD(owner.status_effects, src)
RegisterSignal(owner, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(remove_effect_on_heal))
if(duration != -1)
duration = world.time + duration
tick_interval = world.time + tick_interval
if(alert_type)
var/atom/movable/screen/alert/status_effect/new_alert = owner.throw_alert(id, alert_type)
new_alert.attached_effect = src //so the alert can reference us, if it needs to
linked_alert = new_alert //so we can reference the alert, if we need to
if(duration > 0 || initial(tick_interval) > 0) //don't process if we don't care
switch(processing_speed)
if(STATUS_EFFECT_FAST_PROCESS)
START_PROCESSING(SSfastprocess, src)
if(STATUS_EFFECT_NORMAL_PROCESS)
START_PROCESSING(SSprocessing, src)
update_particles()
return TRUE
/datum/status_effect/Destroy()
switch(processing_speed)
if(STATUS_EFFECT_FAST_PROCESS)
STOP_PROCESSING(SSfastprocess, src)
if(STATUS_EFFECT_NORMAL_PROCESS)
STOP_PROCESSING(SSprocessing, src)
if(owner)
linked_alert = null
owner.clear_alert(id)
LAZYREMOVE(owner.status_effects, src)
on_remove()
UnregisterSignal(owner, COMSIG_LIVING_POST_FULLY_HEAL)
owner = null
if(particle_effect)
QDEL_NULL(particle_effect)
return ..()
// Status effect process. Handles adjusting its duration and ticks.
// If you're adding processed effects, put them in [proc/tick]
// instead of extending / overriding the process() proc.
/datum/status_effect/process(delta_time, times_fired)
SHOULD_NOT_OVERRIDE(TRUE)
if(QDELETED(owner))
qdel(src)
return
if(tick_interval <= world.time)
tick(delta_time, times_fired)
tick_interval = world.time + initial(tick_interval)
if(duration != -1 && duration < world.time)
qdel(src)
/datum/status_effect/proc/on_apply() //Called whenever the buff is applied; returning FALSE will cause it to autoremove itself.
return TRUE
/// Gets and formats examine text associated with our status effect.
/// Return 'null' to have no examine text appear (default behavior).
/datum/status_effect/proc/get_examine_text()
return null
/// Called every tick from process().
/datum/status_effect/proc/tick(delta_time, times_fired)
return
/// Called whenever the buff expires or is removed (qdeleted)
/// Note that at the point this is called, it is out of the
/// owner's status_effects list, but owner is not yet null
/datum/status_effect/proc/on_remove()
return
/// Called instead of on_remove when a status effect
/// of status_type STATUS_EFFECT_REPLACE is replaced by itself,
/// or when a status effect with on_remove_on_mob_delete
/// set to FALSE has its mob deleted
/datum/status_effect/proc/be_replaced()
linked_alert = null
owner.clear_alert(id)
LAZYREMOVE(owner.status_effects, src)
owner = null
qdel(src)
/// Called before being fully removed (before on_remove)
/// Returning FALSE will cancel removal
/datum/status_effect/proc/before_remove()
return TRUE
/// Called when a status effect of status_type STATUS_EFFECT_REFRESH
/// has its duration refreshed in apply_status_effect - is passed New() args
/datum/status_effect/proc/refresh(effect, ...)
var/original_duration = initial(duration)
if(original_duration == -1)
return
duration = world.time + original_duration
/// Adds nextmove modifier multiplicatively to the owner while applied
/datum/status_effect/proc/nextmove_modifier()
return 1
/datum/status_effect/proc/interact_speed_modifier()
return 1
/// Adds nextmove adjustment additiviely to the owner while applied
/datum/status_effect/proc/nextmove_adjust()
return 0
/// Signal proc for [COMSIG_LIVING_POST_FULLY_HEAL] to remove us on fullheal
/datum/status_effect/proc/remove_effect_on_heal(datum/source, heal_flags)
SIGNAL_HANDLER
if(!remove_on_fullheal)
return
// if(!heal_flag_necessary || (heal_flags & heal_flag_necessary))
qdel(src)
/// Remove [seconds] of duration from the status effect, qdeling / ending if we eclipse the current world time.
/datum/status_effect/proc/remove_duration(seconds)
if(duration == -1) // Infinite duration
return FALSE
duration -= seconds
if(duration <= world.time)
qdel(src)
return TRUE
return FALSE
/**
* Updates the particles for the status effects
* Should be handled by subtypes!
*/
/datum/status_effect/proc/update_particles()
SHOULD_CALL_PARENT(FALSE)
////////////////
// ALERT HOOK //
////////////////
/atom/movable/screen/alert/status_effect
name = "Curse of Mundanity"
desc = "You don't feel any different..."
var/datum/status_effect/attached_effect
//////////////////
// HELPER PROCS //
//////////////////
/mob/living/proc/apply_status_effect(datum/status_effect/new_effect, ...)
RETURN_TYPE(/datum/status_effect)
// The arguments we pass to the start effect. The 1st argument is this mob.
var/list/arguments = args.Copy()
arguments[1] = src
// If the status effect we're applying doesn't allow multiple effects, we need to handle it
if(initial(new_effect.status_type) != STATUS_EFFECT_MULTIPLE)
for(var/datum/status_effect/existing_effect as anything in status_effects)
if(existing_effect.id != initial(new_effect.id))
continue
switch(existing_effect.status_type)
// Multiple are allowed, continue as normal. (Not normally reachable)
if(STATUS_EFFECT_MULTIPLE)
break
// Only one is allowed of this type - early return
if(STATUS_EFFECT_UNIQUE)
return
// Replace the existing instance (deletes it).
if(STATUS_EFFECT_REPLACE)
existing_effect.be_replaced()
// Refresh the existing type, then early return
if(STATUS_EFFECT_REFRESH)
existing_effect.refresh(arglist(arguments))
return
// Create the status effect with our mob + our arguments
var/datum/status_effect/new_instance = new new_effect(arguments)
if(!QDELETED(new_instance))
return new_instance
/mob/living/proc/remove_status_effect(datum/status_effect/removed_effect, ...)
var/list/arguments = args.Copy(2)
. = FALSE
for(var/datum/status_effect/existing_effect as anything in status_effects)
if(existing_effect.id == initial(removed_effect.id) && existing_effect.before_remove(arguments))
qdel(existing_effect)
. = TRUE
return .
/**
* Checks if this mob has a status effect that shares the passed effect's ID
*
* checked_effect - TYPEPATH of a status effect to check for. Checks for its ID, not it's typepath
*
* Returns an instance of a status effect, or NULL if none were found.
*/
/mob/proc/has_status_effect(datum/status_effect/checked_effect)
// Yes I'm being cringe and putting this on the mob level even though status effects only apply to the living level
// There's quite a few places (namely examine and, bleh, cult code) where it's easier to not need to cast to living before checking
// for an effect such as blindness
return null
/mob/living/has_status_effect(datum/status_effect/checked_effect)
RETURN_TYPE(/datum/status_effect)
for(var/datum/status_effect/present_effect as anything in status_effects)
if(present_effect.id == initial(checked_effect.id))
return present_effect
return null
/**
* Returns a list of all status effects that share the passed effect type's ID
*
* checked_effect - TYPEPATH of a status effect to check for. Checks for its ID, not it's typepath
*
* Returns a list
*/
/mob/proc/has_status_effect_list(datum/status_effect/checked_effect)
// See [/mob/proc/has_status_effect] for reason behind having this on the mob level
return null
/mob/living/has_status_effect_list(datum/status_effect/checked_effect)
RETURN_TYPE(/list)
var/list/effects_found = list()
for(var/datum/status_effect/present_effect as anything in status_effects)
if(present_effect.id == initial(checked_effect.id))
effects_found += present_effect
return effects_found