-
Notifications
You must be signed in to change notification settings - Fork 4.9k
/
cult_ritual_item.dm
467 lines (385 loc) · 17.6 KB
/
cult_ritual_item.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
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
/*
* Component for items that are used by cultists to conduct rituals.
*
* - Draws runes, including the rune to summon Nar'sie.
* - Purges cultists of holy water on attack.
* - (Un/re)anchors cult structures when hit.
* - Instantly destroys cult girders on hit.
*/
/datum/component/cult_ritual_item
/// Whether we are currently being used to draw a rune.
var/drawing_a_rune = FALSE
/// The message displayed when the parent is examined, if supplied.
var/examine_message
/// A list of turfs that we scribe runes at double speed on.
var/list/turfs_that_boost_us
/// A list of all shields surrounding us while drawing certain runes (Nar'sie).
var/list/obj/structure/emergency_shield/cult/narsie/shields
/// Weakref to an action added to our parent item that allows for quick drawing runes
var/datum/weakref/linked_action_ref
/datum/component/cult_ritual_item/Initialize(
examine_message,
action = /datum/action/item_action/cult_dagger,
turfs_that_boost_us = /turf/open/floor/engine/cult,
)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
src.examine_message = examine_message
if(islist(turfs_that_boost_us))
src.turfs_that_boost_us = turfs_that_boost_us
else if(ispath(turfs_that_boost_us))
src.turfs_that_boost_us = list(turfs_that_boost_us)
if(ispath(action))
var/obj/item/item_parent = parent
var/datum/action/added_action = item_parent.add_item_action(action)
linked_action_ref = WEAKREF(added_action)
/datum/component/cult_ritual_item/Destroy(force, silent)
cleanup_shields()
QDEL_NULL(linked_action_ref)
return ..()
/datum/component/cult_ritual_item/RegisterWithParent()
RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, PROC_REF(try_scribe_rune))
RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(try_purge_holywater))
RegisterSignal(parent, COMSIG_ITEM_ATTACK_OBJ, PROC_REF(try_hit_object))
RegisterSignal(parent, COMSIG_ITEM_ATTACK_EFFECT, PROC_REF(try_clear_rune))
if(examine_message)
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
/datum/component/cult_ritual_item/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_ITEM_ATTACK_SELF,
COMSIG_ITEM_ATTACK,
COMSIG_ITEM_ATTACK_OBJ,
COMSIG_ITEM_ATTACK_EFFECT,
COMSIG_PARENT_EXAMINE,
))
/*
* Signal proc for [COMSIG_PARENT_EXAMINE].
* Gives the examiner, if they're a cultist, our set examine message.
* Usually, this will include various instructions on how to use the thing.
*/
/datum/component/cult_ritual_item/proc/on_examine(datum/source, mob/examiner, list/examine_text)
SIGNAL_HANDLER
if(!IS_CULTIST(examiner))
return
examine_text += examine_message
/*
* Signal proc for [COMSIG_ITEM_ATTACK_SELF].
* Allows the user to begin scribing runes.
*/
/datum/component/cult_ritual_item/proc/try_scribe_rune(datum/source, mob/user)
SIGNAL_HANDLER
if(!isliving(user))
return
if(!can_scribe_rune(source, user))
return
if(drawing_a_rune)
to_chat(user, span_warning("You are already drawing a rune."))
return
INVOKE_ASYNC(src, PROC_REF(start_scribe_rune), source, user)
return COMPONENT_CANCEL_ATTACK_CHAIN
/*
* Signal proc for [COMSIG_ITEM_ATTACK].
* Allows for a cultist (user) to hit another cultist (target)
* to purge them of all holy water in their system, transforming it into unholy water.
*/
/datum/component/cult_ritual_item/proc/try_purge_holywater(datum/source, mob/living/target, mob/living/user)
SIGNAL_HANDLER
if(!IS_CULTIST(user) || !IS_CULTIST(target))
return
. = COMPONENT_CANCEL_ATTACK_CHAIN // No hurting other cultists.
if(!target.has_reagent(/datum/reagent/water/holywater))
return
INVOKE_ASYNC(src, PROC_REF(do_purge_holywater), target, user)
/*
* Signal proc for [COMSIG_ITEM_ATTACK_OBJ].
* Allows the ritual items to unanchor cult buildings or destroy rune girders.
*/
/datum/component/cult_ritual_item/proc/try_hit_object(datum/source, obj/structure/target, mob/cultist)
SIGNAL_HANDLER
if(!isliving(cultist) || !IS_CULTIST(cultist))
return
if(istype(target, /obj/structure/girder/cult))
INVOKE_ASYNC(src, PROC_REF(do_destroy_girder), target, cultist)
return COMPONENT_NO_AFTERATTACK
if(istype(target, /obj/structure/destructible/cult))
INVOKE_ASYNC(src, PROC_REF(do_unanchor_structure), target, cultist)
return COMPONENT_NO_AFTERATTACK
/*
* Signal proc for [COMSIG_ITEM_ATTACK_EFFECT].
* Allows the ritual items to remove runes.
*/
/datum/component/cult_ritual_item/proc/try_clear_rune(datum/source, obj/effect/target, mob/living/cultist, params)
SIGNAL_HANDLER
if(!isliving(cultist) || !IS_CULTIST(cultist))
return
if(istype(target, /obj/effect/rune))
INVOKE_ASYNC(src, PROC_REF(do_scrape_rune), target, cultist)
return COMPONENT_NO_AFTERATTACK
/*
* Actually go through and remove all holy water from [target] and convert it to unholy water.
*
* target - the target being hit, and having their holywater converted
* cultist - the target doing the hitting, can be the same as target
*/
/datum/component/cult_ritual_item/proc/do_purge_holywater(mob/living/target, mob/living/cultist)
// Allows cultists to be rescued from the clutches of ordained religion
to_chat(cultist, span_cult("You remove the taint from [target] using [parent]."))
var/holy_to_unholy = target.reagents.get_reagent_amount(/datum/reagent/water/holywater)
target.reagents.del_reagent(/datum/reagent/water/holywater)
// For carbonss we also want to clear out the stomach of any holywater
if(iscarbon(target))
var/mob/living/carbon/carbon_target = target
var/obj/item/organ/internal/stomach/belly = carbon_target.getorganslot(ORGAN_SLOT_STOMACH)
if(belly)
holy_to_unholy += belly.reagents.get_reagent_amount(/datum/reagent/water/holywater)
belly.reagents.del_reagent(/datum/reagent/water/holywater)
target.reagents.add_reagent(/datum/reagent/fuel/unholywater, holy_to_unholy)
log_combat(cultist, target, "smacked", parent, " removing the holy water from them")
/*
* Destoys the target cult girder [cult_girder], acted upon by [cultist].
*
* cult_girder - the girder being destoyed
* cultist - the mob doing the destroying
*/
/datum/component/cult_ritual_item/proc/do_destroy_girder(obj/structure/girder/cult/cult_girder, mob/living/cultist)
playsound(cult_girder, 'sound/weapons/resonator_blast.ogg', 40, TRUE, ignore_walls = FALSE)
cultist.visible_message(
span_warning("[cultist] strikes [cult_girder] with [parent]!"),
span_notice("You demolish [cult_girder].")
)
new /obj/item/stack/sheet/runed_metal(cult_girder.drop_location(), 1)
qdel(cult_girder)
/*
* Unanchors the target cult building.
*
* cult_structure - the structure being unanchored or reanchored.
* cultist - the mob doing the unanchoring.
*/
/datum/component/cult_ritual_item/proc/do_unanchor_structure(obj/structure/cult_structure, mob/living/cultist)
playsound(cult_structure, 'sound/items/deconstruct.ogg', 30, TRUE, ignore_walls = FALSE)
cult_structure.set_anchored(!cult_structure.anchored)
to_chat(cultist, span_notice("You [cult_structure.anchored ? "":"un"]secure \the [cult_structure] [cult_structure.anchored ? "to":"from"] the floor."))
/*
* Removes the targeted rune. If the rune is important, asks for confirmation and logs it.
*
* rune - the rune being deleted. Instance of a rune.
* cultist - the mob deleting the rune
*/
/datum/component/cult_ritual_item/proc/do_scrape_rune(obj/effect/rune/rune, mob/living/cultist)
if(rune.log_when_erased)
var/confirm = tgui_alert(cultist, "Erasing this [rune.cultist_name] rune may work against your goals.", "Begin to erase the [rune.cultist_name] rune?", list("Proceed", "Abort"))
if(confirm != "Proceed")
return
// Gee, good thing we made sure cultists can't input stall to grief their team and get banned anyway
if(!can_scrape_rune(rune, cultist))
return
SEND_SOUND(cultist, 'sound/items/sheath.ogg')
if(!do_after(cultist, rune.erase_time, target = rune))
return
if(!can_scrape_rune(rune, cultist))
return
if(rune.log_when_erased)
cultist.log_message("erased a [rune.cultist_name] rune with [parent].", LOG_GAME)
message_admins("[ADMIN_LOOKUPFLW(cultist)] erased a [rune.cultist_name] rune with [parent].")
to_chat(cultist, span_notice("You carefully erase the [lowertext(rune.cultist_name)] rune."))
qdel(rune)
/*
* Wraps the entire act of [/proc/do_scribe_rune] to ensure it properly enables or disables [var/drawing_a_rune].)
*
* tool - the parent, source of the signal - the item inscribing the rune, casted to item.
* cultist - the mob scribing the rune
*/
/datum/component/cult_ritual_item/proc/start_scribe_rune(obj/item/tool, mob/living/cultist)
drawing_a_rune = TRUE
do_scribe_rune(tool, cultist)
drawing_a_rune = FALSE
/*
* Actually give the user input to begin scribing a rune.
* Creates the new instance of the rune if successful.
*
* tool - the parent, source of the signal - the item inscribing the rune, casted to item.
* cultist - the mob scribing the rune
*/
/datum/component/cult_ritual_item/proc/do_scribe_rune(obj/item/tool, mob/living/cultist)
var/turf/our_turf = get_turf(cultist)
var/obj/effect/rune/rune_to_scribe
var/entered_rune_name
var/chosen_keyword
var/datum/antagonist/cult/user_antag = cultist.mind.has_antag_datum(/datum/antagonist/cult, TRUE)
var/datum/team/cult/user_team = user_antag?.get_team()
if(!user_antag || !user_team)
stack_trace("[type] - [cultist] attempted to scribe a rune, but did not have an associated [user_antag ? "cult team":"cult antag datum"]!")
return FALSE
if(!LAZYLEN(GLOB.rune_types))
to_chat(cultist, span_cult("There appears to be no runes to scribe. Contact your god about this!"))
stack_trace("[type] - [cultist] attempted to scribe a rune, but the global rune list is empty!")
return FALSE
entered_rune_name = tgui_input_list(cultist, "Choose a rite to scribe", "Sigils of Power", GLOB.rune_types)
if(isnull(entered_rune_name))
return FALSE
if(!can_scribe_rune(tool, cultist))
return FALSE
rune_to_scribe = GLOB.rune_types[entered_rune_name]
if(!ispath(rune_to_scribe))
stack_trace("[type] - [cultist] attempted to scribe a rune, but did not find a path from the global rune list!")
return FALSE
if(initial(rune_to_scribe.req_keyword))
chosen_keyword = tgui_input_text(cultist, "Keyword for the new rune", "Words of Power", max_length = MAX_NAME_LEN)
if(!chosen_keyword)
drawing_a_rune = FALSE
start_scribe_rune(tool, cultist)
return FALSE
our_turf = get_turf(cultist) //we may have moved. adjust as needed...
if(!can_scribe_rune(tool, cultist))
return FALSE
if(ispath(rune_to_scribe, /obj/effect/rune/summon) && (!is_station_level(our_turf.z) || istype(get_area(cultist), /area/space)))
to_chat(cultist, span_cultitalic("The veil is not weak enough here to summon a cultist, you must be on station!"))
return
if(ispath(rune_to_scribe, /obj/effect/rune/apocalypse))
if((world.time - SSticker.round_start_time) <= 6000)
var/wait = 6000 - (world.time - SSticker.round_start_time)
to_chat(cultist, span_cultitalic("The veil is not yet weak enough for this rune - it will be available in [DisplayTimeText(wait)]."))
return
if(!check_if_in_ritual_site(cultist, user_team, TRUE))
return
if(ispath(rune_to_scribe, /obj/effect/rune/narsie))
if(!scribe_narsie_rune(cultist, user_team))
return
cultist.visible_message(
span_warning("[cultist] [cultist.blood_volume ? "cuts open [cultist.p_their()] arm and begins writing in [cultist.p_their()] own blood":"begins sketching out a strange design"]!"),
span_cult("You [cultist.blood_volume ? "slice open your arm and ":""]begin drawing a sigil of the Geometer.")
)
if(cultist.blood_volume)
cultist.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM), wound_bonus = CANT_WOUND) // *cuts arm* *bone explodes* ever have one of those days?
var/scribe_mod = initial(rune_to_scribe.scribe_delay)
if(!initial(rune_to_scribe.no_scribe_boost) && (our_turf.type in turfs_that_boost_us))
scribe_mod *= 0.5
SEND_SOUND(cultist, sound('sound/weapons/slice.ogg', 0, 1, 10))
if(!do_after(cultist, scribe_mod, target = get_turf(cultist), timed_action_flags = IGNORE_SLOWDOWNS))
cleanup_shields()
return FALSE
if(!can_scribe_rune(tool, cultist))
cleanup_shields()
return FALSE
cultist.visible_message(
span_warning("[cultist] creates a strange circle[cultist.blood_volume ? " in [cultist.p_their()] own blood":""]."),
span_cult("You finish drawing the arcane markings of the Geometer.")
)
cleanup_shields()
var/obj/effect/rune/made_rune = new rune_to_scribe(our_turf, chosen_keyword)
made_rune.add_mob_blood(cultist)
to_chat(cultist, span_cult("The [lowertext(made_rune.cultist_name)] rune [made_rune.cultist_desc]"))
cultist.log_message("scribed \a [lowertext(made_rune.cultist_name)] rune using [parent] ([parent.type])", LOG_GAME)
SSblackbox.record_feedback("tally", "cult_runes_scribed", 1, made_rune.cultist_name)
return TRUE
/*
* The process of scribing the nar'sie rune.
*
* cultist - the mob placing the rune
* cult_team - the team of the mob placing the rune
*/
/datum/component/cult_ritual_item/proc/scribe_narsie_rune(mob/living/cultist, datum/team/cult/cult_team)
var/datum/objective/eldergod/summon_objective = locate() in cult_team.objectives
var/datum/objective/sacrifice/sac_objective = locate() in cult_team.objectives
if(!check_if_in_ritual_site(cultist, cult_team))
return FALSE
if(sac_objective && !sac_objective.check_completion())
to_chat(cultist, span_warning("The sacrifice is not complete. The portal would lack the power to open if you tried!"))
return FALSE
if(summon_objective.check_completion())
to_chat(cultist, span_cultlarge("\"I am already here. There is no need to try to summon me now.\""))
return FALSE
var/confirm_final = tgui_alert(cultist, "This is the FINAL step to summon Nar'Sie; it is a long, painful ritual and the crew will be alerted to your presence.", "Are you prepared for the final battle?", list("My life for Nar'Sie!", "No"))
if(confirm_final == "No")
to_chat(cultist, span_cult("You decide to prepare further before scribing the rune."))
return
if(!check_if_in_ritual_site(cultist, cult_team))
return FALSE
priority_announce("Figments from an eldritch god are being summoned by [cultist.real_name] into [get_area(cultist)] from an unknown dimension. Disrupt the ritual at all costs!", "Central Command Higher Dimensional Affairs", ANNOUNCER_SPANOMALIES, has_important_message = TRUE)
for(var/shielded_turf in spiral_range_turfs(1, cultist, 1))
LAZYADD(shields, new /obj/structure/emergency_shield/cult/narsie(shielded_turf))
return TRUE
/*
* Helper to check if a rune can be scraped by a cultist.
* Used in between inputs of [do_scrape_rune] for sanity checking.
*
* rune - the rune being deleted. Instance of a rune.
* cultist - the mob deleting the rune
*/
/datum/component/cult_ritual_item/proc/can_scrape_rune(obj/effect/rune/rune, mob/living/cultist)
if(!IS_CULTIST(cultist))
return FALSE
if(!cultist.is_holding(parent))
return FALSE
if(!rune.Adjacent(cultist))
return FALSE
if(cultist.incapacitated())
return FALSE
if(cultist.stat == DEAD)
return FALSE
return TRUE
/*
* Helper to check if a rune can be scribed by a cultist.
* Used in between inputs of [do_scribe_rune] for sanity checking.
*
* tool - the parent - the item being used to scribe the rune, casted to item
* cultist - the mob making the rune
*/
/datum/component/cult_ritual_item/proc/can_scribe_rune(obj/item/tool, mob/living/cultist)
if(!IS_CULTIST(cultist))
to_chat(cultist, span_warning("[tool] is covered in unintelligible shapes and markings."))
return FALSE
if(QDELETED(tool) || !cultist.is_holding(tool))
return FALSE
if(cultist.incapacitated() || cultist.stat == DEAD)
to_chat(cultist, span_warning("You can't draw a rune right now."))
return FALSE
if(!check_rune_turf(get_turf(cultist), cultist))
return FALSE
return TRUE
/*
* Checks if a turf is valid for having a rune placed there.
*
* target - the turf being checked
* cultist - the mob placing the rune
*/
/datum/component/cult_ritual_item/proc/check_rune_turf(turf/target, mob/living/cultist)
if(isspaceturf(target))
to_chat(cultist, span_warning("You cannot scribe runes in space!"))
return FALSE
if(locate(/obj/effect/rune) in target)
to_chat(cultist, span_cult("There is already a rune here."))
return FALSE
var/area/our_area = get_area(target)
if((!is_station_level(target.z) && !is_mining_level(target.z)) || (our_area && !(our_area.area_flags & CULT_PERMITTED)))
to_chat(cultist, span_warning("The veil is not weak enough here."))
return FALSE
return TRUE
/*
* Helper to check a cultist is located in one of the ritual / summoning sites.
*
* cultist - the mob making the rune
* cult_team - the team of the mob making the rune
* fail_if_last_site - whether the check fails if it's the last site in the summoning list.
*/
/datum/component/cult_ritual_item/proc/check_if_in_ritual_site(mob/living/cultist, datum/team/cult/cult_team, fail_if_last_site = FALSE)
var/datum/objective/eldergod/summon_objective = locate() in cult_team.objectives
var/area/our_area = get_area(cultist)
if(!summon_objective)
to_chat(cultist, span_warning("There are no ritual sites on this station to scribe this rune!"))
return FALSE
if(!(our_area in summon_objective.summon_spots))
to_chat(cultist, span_warning("This veil is not weak enough here - it can only be scribed in [english_list(summon_objective.summon_spots)]!"))
return FALSE
if(fail_if_last_site && length(summon_objective.summon_spots) <= 1)
to_chat(cultist, span_warning("This rune cannot be scribed here - the ritual site must be reserved for the final summoning!"))
return FALSE
return TRUE
/*
* Removes all shields from the shields list.
*/
/datum/component/cult_ritual_item/proc/cleanup_shields()
for(var/obj/structure/emergency_shield/cult/narsie/shield as anything in shields)
LAZYREMOVE(shields, shield)
if(!QDELETED(shield))
qdel(shield)