-
-
Notifications
You must be signed in to change notification settings - Fork 444
/
Copy pathrevenant_abilities.dm
382 lines (342 loc) · 15.2 KB
/
revenant_abilities.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
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
/mob/living/simple_animal/revenant/ClickOn(atom/A, params) //revenants can't interact with the world directly.
var/list/modifiers = params2list(params)
if(modifiers["shift"])
ShiftClickOn(A)
return
if(modifiers["alt"])
AltClickNoInteract(src, A)
return
if(ishuman(A))
if(A in drained_mobs)
to_chat(src, span_revenwarning("[A]'s soul is dead and empty.") )
else if(in_range(src, A))
Harvest(A)
//Harvest; activated by clicking the target, will try to drain their essence.
/mob/living/simple_animal/revenant/proc/Harvest(mob/living/carbon/human/target)
if(!castcheck(0))
return
if(draining)
to_chat(src, span_revenwarning("You are already siphoning the essence of a soul!"))
return
if(!target.stat)
to_chat(src, span_revennotice("[target.p_their(TRUE)] soul is too strong to harvest."))
if(prob(10))
to_chat(target, "You feel as if you are being watched.")
return
face_atom(target)
draining = TRUE
essence_drained += rand(15, 20)
to_chat(src, span_revennotice("You search for the soul of [target]."))
if(do_after(src, rand(10, 20), target, timed_action_flags = IGNORE_HELD_ITEM)) //did they get deleted in that second?
if(target.ckey)
to_chat(src, span_revennotice("[target.p_their(TRUE)] soul burns with intelligence."))
essence_drained += rand(20, 30)
if(target.stat != DEAD)
to_chat(src, span_revennotice("[target.p_their(TRUE)] soul blazes with life!"))
essence_drained += rand(40, 50)
else
to_chat(src, span_revennotice("[target.p_their(TRUE)] soul is weak and faltering."))
if(do_after(src, rand(15, 20), target, timed_action_flags = IGNORE_HELD_ITEM)) //did they get deleted NOW?
switch(essence_drained)
if(1 to 30)
to_chat(src, span_revennotice("[target] will not yield much essence. Still, every bit counts."))
if(30 to 70)
to_chat(src, span_revennotice("[target] will yield an average amount of essence."))
if(70 to 90)
to_chat(src, span_revenboldnotice("Such a feast! [target] will yield much essence to you."))
if(90 to INFINITY)
to_chat(src, span_revenbignotice("Ah, the perfect soul. [target] will yield massive amounts of essence to you."))
if(do_after(src, rand(15, 25), target, timed_action_flags = IGNORE_HELD_ITEM)) //how about now
if(!target.stat)
to_chat(src, span_revenwarning("[target.p_theyre(TRUE)] now powerful enough to fight off your draining."))
to_chat(target, span_boldannounce("You feel something tugging across your body before subsiding."))
draining = 0
essence_drained = 0
return //hey, wait a minute...
to_chat(src, span_revenminor("You begin siphoning essence from [target]'s soul."))
if(target.stat != DEAD)
to_chat(target, span_warning("You feel a horribly unpleasant draining sensation as your grip on life weakens..."))
if(target.stat == SOFT_CRIT)
target.Stun(46)
reveal(46)
stun(46)
target.visible_message(span_warning("[target] suddenly rises slightly into the air, [target.p_their()] skin turning an ashy gray."))
if(target.can_block_magic(MAGIC_RESISTANCE_HOLY))
to_chat(src, span_revenminor("Something's wrong! [target] seems to be resisting the siphoning, leaving you vulnerable!"))
target.visible_message(span_warning("[target] slumps onto the ground."), \
span_revenwarning("Violet lights, dancing in your vision, receding--"))
draining = FALSE
return
var/datum/beam/draining_beam = Beam(target, icon_state = "drain_life")
if(do_after(src, 4.6 SECONDS, target, timed_action_flags = IGNORE_HELD_ITEM)) //As one cannot prove the existance of ghosts, ghosts cannot prove the existance of the target they were draining.
change_essence_amount(essence_drained, FALSE, target)
if(essence_drained <= 90 && target.stat != DEAD)
essence_regen_cap += 5
to_chat(src, span_revenboldnotice("The absorption of [target]'s living soul has increased your maximum essence level. Your new maximum essence is [essence_regen_cap]."))
if(essence_drained > 90)
essence_regen_cap += 15
perfectsouls++
to_chat(src, span_revenboldnotice("The perfection of [target]'s soul has increased your maximum essence level. Your new maximum essence is [essence_regen_cap]."))
to_chat(src, span_revennotice("[target]'s soul has been considerably weakened and will yield no more essence for the time being."))
target.visible_message(span_warning("[target] slumps onto the ground."), \
span_revenwarning("Violets lights, dancing in your vision, getting clo--"))
drained_mobs.Add(target)
target.death(0)
else
to_chat(src, span_revenwarning("[target ? "[target] has":"[target.p_theyve(TRUE)]"] been drawn out of your grasp. The link has been broken."))
if(target) //Wait, target is WHERE NOW?
target.visible_message(span_warning("[target] slumps onto the ground."), \
span_revenwarning("Violets lights, dancing in your vision, receding--"))
qdel(draining_beam)
else
to_chat(src, span_revenwarning("You are not close enough to siphon [target ? "[target]'s":"[target.p_their()]"] soul. The link has been broken."))
draining = FALSE
essence_drained = 0
//Transmit: the revemant's only direct way to communicate. Sends a single message silently to a single mob
/datum/action/cooldown/spell/list_target/telepathy/revenant
name = "Revenant Transmit"
panel = "Revenant Abilities"
background_icon_state = "bg_revenant"
overlay_icon_state = "bg_revenant_border"
telepathy_span = "revennotice"
bold_telepathy_span = "revenboldnotice"
antimagic_flags = MAGIC_RESISTANCE_HOLY|MAGIC_RESISTANCE_MIND
/datum/action/cooldown/spell/aoe/revenant
panel = "Revenant Abilities (Locked)"
background_icon_state = "bg_revenant"
overlay_icon_state = "bg_revenant_border"
button_icon = 'icons/mob/actions/actions_revenant.dmi'
antimagic_flags = MAGIC_RESISTANCE_HOLY
spell_requirements = NONE
/// If it's locked, and needs to be unlocked before use
var/locked = TRUE
/// How much essence it costs to unlock
var/unlock_amount = 100
/// How much essence it costs to use
var/cast_amount = 50
/// How long it reveals the revenant
var/reveal_duration = 8 SECONDS
// How long it stuns the revenant
var/stun_duration = 2 SECONDS
/datum/action/cooldown/spell/aoe/revenant/New(Target)
. = ..()
if(!isrevenant(target))
stack_trace("[type] was given to a non-revenant mob, please don't.")
qdel(src)
return
if(locked)
name = "[initial(name)] ([unlock_amount]SE)"
else
name = "[initial(name)] ([cast_amount]E)"
/datum/action/cooldown/spell/aoe/revenant/can_cast_spell(feedback = TRUE)
. = ..()
if(!.)
return FALSE
if(!isrevenant(owner))
stack_trace("[type] was owned by a non-revenant mob, please don't.")
return FALSE
var/mob/living/simple_animal/revenant/ghost = owner
if(ghost.inhibited)
return FALSE
if(locked && ghost.essence_excess <= unlock_amount)
return FALSE
if(ghost.essence <= cast_amount)
return FALSE
return TRUE
/datum/action/cooldown/spell/aoe/revenant/get_things_to_cast_on(atom/center)
var/list/things = list()
for(var/turf/nearby_turf in range(aoe_radius, center))
things += nearby_turf
return things
/datum/action/cooldown/spell/aoe/revenant/before_cast(mob/living/simple_animal/revenant/cast_on)
. = ..()
if(. & SPELL_CANCEL_CAST)
return FALSE
if(locked)
if(!cast_on.unlock(unlock_amount))
to_chat(cast_on, span_revenwarning("You don't have enough essence to unlock [initial(name)]!"))
reset_spell_cooldown()
return . | SPELL_CANCEL_CAST
name = "[initial(name)] ([cast_amount]E)"
to_chat(owner, span_revennotice("You have unlocked [initial(name)]!"))
to_chat(cast_on, span_revennotice("You have unlocked [initial(name)]!"))
panel = "Revenant Abilities"
locked = FALSE
reset_spell_cooldown()
return . | SPELL_CANCEL_CAST
if(!cast_on.castcheck(-cast_amount))
reset_spell_cooldown()
return . | SPELL_CANCEL_CAST
/datum/action/cooldown/spell/aoe/revenant/after_cast(mob/living/simple_animal/revenant/cast_on)
. = ..()
if(reveal_duration)
cast_on.reveal(reveal_duration)
if(stun_duration)
cast_on.stun(stun_duration)
//Overload Light: Breaks a light that's online and sends out lightning bolts to all nearby people.
/datum/action/cooldown/spell/aoe/revenant/overload
name = "Overload Lights"
desc = "Directs a large amount of essence into nearby electrical lights, causing lights to shock those nearby."
button_icon_state = "overload_lights"
cooldown_time = 20 SECONDS
aoe_radius = 5
unlock_amount = 25
cast_amount = 40
unlock_amount = 25
cast_amount = 40
stun_duration = 3 SECONDS
/// The range the shocks from the lights go
var/shock_range = 2
/// The damage the shcoskf rom the lgihts do
var/shock_damage = 15
/datum/action/cooldown/spell/aoe/revenant/overload/cast_on_thing_in_aoe(turf/victim, mob/living/simple_animal/revenant/caster)
for(var/obj/machinery/light/light in victim)
if(!light.on)
continue
light.visible_message(span_boldwarning("[light] suddenly flares brightly and begins to spark!"))
var/datum/effect_system/spark_spread/light_sparks = new /datum/effect_system/spark_spread()
light_sparks.set_up(4, 0, light)
light_sparks.start()
new /obj/effect/temp_visual/revenant(get_turf(light))
addtimer(CALLBACK(src, PROC_REF(overload_shock), light, caster), 2 SECONDS)
/datum/action/cooldown/spell/aoe/revenant/overload/proc/overload_shock(obj/machinery/light/to_shock, mob/living/simple_animal/revenant/caster)
flick("[to_shock.base_state]2", to_shock)
for(var/mob/living/carbon/human/human_mob in view(shock_range, to_shock))
if(human_mob == caster)
continue
to_shock.Beam(human_mob, icon_state = "purple_lightning", time = 0.5 SECONDS)
if(!human_mob.can_block_magic(antimagic_flags))
human_mob.electrocute_act(shock_damage, to_shock)
do_sparks(4, FALSE, human_mob)
playsound(human_mob, 'sound/machines/defib_zap.ogg', 50, TRUE, -1)
//Defile: Corrupts nearby stuff, unblesses floor tiles.
/datum/action/cooldown/spell/aoe/revenant/defile
name = "Defile"
desc = "Twists and corrupts the nearby area as well as dispelling holy auras on floors."
button_icon_state = "defile"
cooldown_time = 15 SECONDS
aoe_radius = 4
unlock_amount = 10
cast_amount = 30
reveal_duration = 4 SECONDS
stun_duration = 2 SECONDS
/datum/action/cooldown/spell/aoe/revenant/defile/cast_on_thing_in_aoe(turf/victim, mob/living/simple_animal/revenant/caster)
for(var/obj/effect/blessing/blessing in victim)
qdel(blessing)
new /obj/effect/temp_visual/revenant(victim)
if(!isplatingturf(victim) && !istype(victim, /turf/open/floor/engine/cult) && isfloorturf(victim) && prob(15))
var/turf/open/floor/floor = victim
if(floor.overfloor_placed && floor.floor_tile)
new floor.floor_tile(floor)
floor.broken = 0
floor.burnt = 0
floor.make_plating(TRUE)
if(victim.type == /turf/closed/wall && prob(15))
new /obj/effect/temp_visual/revenant(victim)
victim.AddElement(/datum/element/rust)
if(victim.type == /turf/closed/wall/r_wall && prob(10))
new /obj/effect/temp_visual/revenant(victim)
victim.AddElement(/datum/element/rust)
for(var/obj/effect/decal/cleanable/food/salt/salt in victim)
new /obj/effect/temp_visual/revenant(victim)
qdel(salt)
for(var/obj/structure/closet/closet in victim.contents)
closet.open()
for(var/obj/structure/bodycontainer/corpseholder in victim)
if(corpseholder.connected.loc == corpseholder)
corpseholder.open()
for(var/obj/machinery/dna_scannernew/dna in victim)
dna.open_machine()
for(var/obj/structure/window/window in victim)
window.take_damage(rand(30, 80))
if(window?.fulltile)
new /obj/effect/temp_visual/revenant/cracks(window.loc)
for(var/obj/machinery/light/light in victim)
light.flicker(20) //spooky
//Malfunction: Makes bad stuff happen to robots and machines.
/datum/action/cooldown/spell/aoe/revenant/malfunction
name = "Malfunction"
desc = "Corrupts and damages nearby machines and mechanical objects."
button_icon_state = "malfunction"
cooldown_time = 20 SECONDS
aoe_radius = 4
cast_amount = 60
unlock_amount = 125
//A note to future coders: do not replace this with an EMP because it will wreck malf AIs and everyone will hate you.
/datum/action/cooldown/spell/aoe/revenant/malfunction/cast_on_thing_in_aoe(turf/victim, mob/living/simple_animal/revenant/caster)
for(var/mob/living/simple_animal/bot/bot in victim)
if(!bot.emagged)
new /obj/effect/temp_visual/revenant(bot.loc)
bot.locked = FALSE
bot.open = TRUE
bot.emag_act(caster)
for(var/mob/living/carbon/human/human in victim)
if(human == caster)
continue
if(human.can_block_magic(antimagic_flags))
continue
to_chat(human, span_revenwarning("You feel [pick("your sense of direction flicker out", "a stabbing pain in your head", "your mind fill with static")]."))
new /obj/effect/temp_visual/revenant(human.loc)
human.emp_act(EMP_HEAVY)
for(var/obj/thing in victim)
//Doesn't work on SMES and APCs, to prevent kekkery.
if(istype(thing,/obj/machinery/airalarm) || istype(thing, /obj/machinery/power/apc) || istype(thing, /obj/machinery/power/smes || istype(thing, /obj/machinery/particle_accelerator/control_box)))
continue
if(prob(20))
if(prob(50))
new /obj/effect/temp_visual/revenant(thing.loc)
thing.emag_act(caster)
// Only works on cyborgs, not AI!
for(var/mob/living/silicon/robot/cyborg in victim)
playsound(cyborg, 'sound/machines/warning-buzzer.ogg', 50, TRUE)
new /obj/effect/temp_visual/revenant(cyborg.loc)
cyborg.spark_system.start()
cyborg.emp_act(EMP_HEAVY)
//Blight: Infects nearby humans and in general messes living stuff up.
/datum/action/cooldown/spell/aoe/revenant/blight
name = "Blight"
desc = "Causes nearby living things to waste away."
button_icon_state = "blight"
cooldown_time = 20 SECONDS
aoe_radius = 3
cast_amount = 50
unlock_amount = 75
/datum/action/cooldown/spell/aoe/revenant/blight/cast_on_thing_in_aoe(turf/victim, mob/living/simple_animal/revenant/caster)
for(var/mob/living/mob in victim)
if(mob == caster)
continue
if(mob.can_block_magic(antimagic_flags))
to_chat(caster, span_warning("The spell had no effect on [mob]!"))
continue
new /obj/effect/temp_visual/revenant(mob.loc)
if(iscarbon(mob))
if(ishuman(mob))
var/mob/living/carbon/human/H = mob
if(H.dna && H.dna.species)
H.dna.species.handle_hair(H,"#1d2953") //will be reset when blight is cured
var/blightfound = FALSE
for(var/datum/disease/revblight/blight in H.diseases)
blightfound = TRUE
if(blight.stage < 5)
blight.stage++
if(!blightfound)
H.ForceContractDisease(new /datum/disease/revblight(), FALSE, TRUE)
to_chat(H, span_revenminor("You feel [pick("suddenly sick", "a surge of nausea", "like your skin is <i>wrong</i>")]."))
else
if(mob.reagents)
mob.reagents.add_reagent(/datum/reagent/toxin/plasma, 5)
else
mob.adjustToxLoss(5)
for(var/obj/structure/spacevine/vine in victim) //Fucking with botanists, the ability.
vine.add_atom_colour("#823abb", TEMPORARY_COLOUR_PRIORITY)
new /obj/effect/temp_visual/revenant(vine.loc)
QDEL_IN(vine, 1 SECONDS)
for(var/obj/structure/glowshroom/shroom in victim)
shroom.add_atom_colour("#823abb", TEMPORARY_COLOUR_PRIORITY)
new /obj/effect/temp_visual/revenant(shroom.loc)
QDEL_IN(shroom, 1 SECONDS)
for(var/obj/machinery/hydroponics/tray in victim)
new /obj/effect/temp_visual/revenant(tray.loc)
tray.pestlevel = rand(8, 10)
tray.weedlevel = rand(8, 10)
tray.toxic = rand(45, 55)