Files
Paradise/code/game/gamemodes/cult/runes.dm
203105788c [IDB Ignore] Heretic: The Mansus Re-Opened (#30738)
* fixes

* fuck my stupid chungus life

* Minion limit, heal fix, dead sac fix

* cooldown, no sacrificing star gazer or ascended alive heretics

* blade debuff

* oopsy

* Update tgui.bundle.js

* map diff bot what ya doing

* fuck that chat spam

* lets heretic armour hold a haunted longsword

* why not it makes sense

* do_after

* god I hate this bullshit

* other lewc stuff

* push

* heretic id card fix

* she tg on my ui till I css

* yes

* spent

* fix / ipc buff (real)™️

* moderate again

* revert

* no reserve

* bringing up to master

* update map files to master

* didnt replace centcomm

* beginning some rebalancing

* aggressive spread tweaks

* lots of tweaks and fixes

* trying to un-key the maps

* maybe this time

* this time????

* oops

* sql fix

* basicmob conversion

* paintings! and a critical influence fix

* rust + tweaks

* monster tweak

* small change

* removing this

* more tweaks. no more dusting

* added some examine_more

* flower seeds

* various tweaks. more to come

* no more conduit spacing

* fixed some dumb stuff

* silly stuff

* its always prettier

* bugfixes and linters

* linters, wow

* oops

* bah

* linter

* fuck you

* temp check

* hidden influence drain

* influence visible message

* tweak fix

* void cloak bugfix

* small fixes

* fixes

* do_after_once broken

* fixes and tweaks

* heretic blade potential fix + sacrifice changes

* batch of fixes

* tiny tweak

* rebuilt TGUI

* no greentext + rerolls

* logging + bugfix

* unused var

* small fix

* various fixes

* comment

* projectile change

* tgui rebuild

* tgui bundle redo

* rune issue solved

* influence visible now

* fix ui reloading

* new moon ascension + fixes + icons

* tweaks, species sprites

* tgui rebuild

* small tweak + linter

* harvester icon tweak

* spans

* fixes and tweaks

* caretaker nerf + tweaks

* potential fix for knowledge

* roller fix

* mad mask

* Update tgui.bundle.js

* void phase tweak

* Update tgui.bundle.js

* misc tweaks

* fix heretic not retargeting correctly with cryo

* simplify logic

* this is better

* lots of fixes and tweaks

* Update tgui.bundle.js

* linter

* linter

* fireshark and greyscale insanity

* fish

* Update tgui.bundle.js

* linter

* linter

* tgui

* no window shopping

* fish fix

* tgui rebundle

* moon smile runtime fix

* various fixes

* sacrifice fixes

* insanity is easier now, madness mask changes.

* bugfixing + teleport change

* linters + tweaks

* Update code/modules/antagonists/heretic/status_effects/mark_effects.dm

Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com>
Signed-off-by: Kyani <65205627+EmeraldCandy@users.noreply.github.com>

* Update code/modules/antagonists/heretic/status_effects/mark_effects.dm

Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com>
Signed-off-by: Kyani <65205627+EmeraldCandy@users.noreply.github.com>

---------

Signed-off-by: Kyani <65205627+EmeraldCandy@users.noreply.github.com>
Co-authored-by: Qwertytoforty <52090703+Qwertytoforty@users.noreply.github.com>
Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
Co-authored-by: warriorstar-orion <orion@snowfrost.garden>
Co-authored-by: Paul <pmerkamp@gmail.com>
Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com>
2026-01-27 20:36:52 +00:00

1156 lines
48 KiB
Plaintext

GLOBAL_LIST_EMPTY(sacrificed) // A mixed list of minds and mobs
GLOBAL_LIST_EMPTY(teleport_runes) // I'll give you two guesses
/*
This file contains runes.
Runes are used by the cult to cause many different effects and are paramount to their success.
They are drawn with a ritual dagger in blood, and are distinguishable to cultists and normal crew by examining.
Fake runes can be drawn in crayon to fool people.
Runes can either be invoked by one's self or with many different cultists. Each rune has a specific incantation that the cultists will say when invoking it.
To draw a rune, use a ritual dagger.
*/
/obj/effect/rune
/// Name non-cultists see
name = "rune"
/// Name that cultists see
var/cultist_name = "basic rune"
/// Description that non-cultists see
desc = "An odd collection of symbols drawn in what seems to be blood."
/// Description that cultists see
var/cultist_desc = "a basic rune with no function." //This is shown to cultists who examine the rune in order to determine its true purpose.
icon = 'icons/obj/rune.dmi'
icon_state = "1"
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
mouse_opacity = MOUSE_OPACITY_OPAQUE // So that runes aren't so hard to click
var/visibility = 0
var/view_range = 7
invisibility = INVISIBILITY_RUNES
layer = SIGIL_LAYER
color = COLOR_BLOOD_BASE
/// What is said by cultists when the rune is invoked
var/invocation = "Aiy ele-mayo!"
///The amount of cultists required around the rune to invoke it. If only 1, any cultist can invoke it.
var/req_cultists = 1
/// Used for some runes, this is for when you want a rune to not be usable when in use.
var/rune_in_use = FALSE
/// How long the rune takes to create (Currently only different for the Nar'Sie rune)
var/scribe_delay = 5 SECONDS
/// How much damage you take from drawing the rune
var/scribe_damage = 1
/// If nearby cultists will also chant when invoked
var/allow_excess_invokers = FALSE
/// If constructs can invoke it
var/construct_invoke = TRUE
/// If the rune requires a keyword (e.g. Teleport runes)
var/req_keyword = FALSE
/// The actual keyword for the rune
var/keyword
/// How much damage cultists take when invoking it (This includes constructs)
var/invoke_damage = 0
/// The color of the rune. (Based on species blood color)
var/rune_blood_color = COLOR_BLOOD_BASE
/obj/effect/rune/Initialize(mapload, set_keyword)
. = ..()
if(set_keyword)
keyword = set_keyword
var/image/blood = image(loc = src)
blood.override = 1
for(var/mob/living/silicon/ai/AI in GLOB.ai_list)
AI.client.images += blood
/obj/effect/rune/examine(mob/user)
. = ..()
if(IS_CULTIST(user) || isobserver(user)) //If they're a cultist or a ghost, tell them the effects
. += "<b>Name:</b> [cultist_name]"
. += "<b>Effects:</b> [capitalize(cultist_desc)]"
. += "<b>Required Acolytes:</b> [req_cultists]"
if(req_keyword && keyword)
. += "<b>Keyword:</b> [SPAN_CULTITALIC("[keyword]")]"
/obj/effect/rune/item_interaction(mob/living/user, obj/item/used, list/modifiers)
if(istype(used, /obj/item/melee/cultblade/dagger) && IS_CULTIST(user))
if(!can_dagger_erase_rune(user))
return ITEM_INTERACT_COMPLETE
var/obj/item/melee/cultblade/dagger/dagger = used
user.visible_message(SPAN_WARNING("[user] begins to erase [src] with [dagger]."))
if(do_after(user, initial(scribe_delay) * dagger.toolspeed, target = src))
to_chat(user, SPAN_NOTICE("You carefully erase the [lowertext(cultist_name)] rune."))
qdel(src)
return ITEM_INTERACT_COMPLETE
if(istype(used, /obj/item/nullrod))
var/obj/item/nullrod/nullrod = used
if(IS_CULTIST(user)) // cultist..what are doing..cultist..staph...
user.drop_item()
user.visible_message(SPAN_WARNING("[nullrod] suddenly glows with a white light, forcing [user] to drop it in pain!"), \
SPAN_DANGER("[nullrod] suddenly glows with a white light that sears your hand, forcing you to drop it!")) // TODO: Make this actually burn your hand
return ITEM_INTERACT_COMPLETE
to_chat(user,SPAN_DANGER("You disrupt the magic of [src] with [nullrod]."))
qdel(src)
return ITEM_INTERACT_COMPLETE
/obj/effect/rune/proc/can_dagger_erase_rune(mob/user)
return TRUE
/obj/effect/rune/attack_hand(mob/living/user)
user.Move_Pulled(src) // So that you can still drag things onto runes
if(!IS_CULTIST(user))
to_chat(user, SPAN_WARNING("You aren't able to understand the words of [src]."))
return
var/list/invokers = can_invoke(user)
if(length(invokers) >= req_cultists)
invoke(invokers)
else
fail_invoke()
/obj/effect/rune/attack_animal(mob/living/simple_animal/M)
if(isshade(M) || isconstruct(M))
if(construct_invoke || !IS_CULTIST(M)) //if you're not a cult construct we want the normal fail message
attack_hand(M)
else
to_chat(M, SPAN_WARNING("You are unable to invoke the rune!"))
/obj/effect/rune/cult_conceal() //for concealing spell
visible_message(SPAN_DANGER("[src] fades away."))
invisibility = INVISIBILITY_HIDDEN_RUNES
alpha = 100 //To help ghosts distinguish hidden runes
/obj/effect/rune/cult_reveal() //for revealing spell
invisibility = initial(invisibility)
visible_message(SPAN_DANGER("[src] suddenly appears!"))
alpha = initial(alpha)
/obj/effect/rune/is_cleanable()
return TRUE
/obj/effect/rune/cleaning_act(mob/user, atom/cleaner, cleanspeed = 5 SECONDS, text_verb = "scrub out", text_description = " with [cleaner].")
if(issimulatedturf(loc))
var/turf/simulated/T = get_turf(src)
T.cleaning_act(user, cleaner, cleanspeed = cleanspeed, text_verb = text_verb, text_description = text_description, text_targetname = name) //Strings are deliberately "A = A" to avoid overrides
return
else
..()
/*
There are a few different procs each rune runs through when a cultist activates it.
can_invoke() is called when a cultist activates the rune with an empty hand. If there are multiple cultists, this rune determines if the required amount is nearby.
invoke() is the rune's actual effects.
fail_invoke() is called when the rune fails, via not enough people around or otherwise. Typically this just has a generic 'fizzle' effect.
structure_check() searches for nearby cultist structures required for the invocation. Proper structures are pylons, forges, archives, and altars.
*/
/obj/effect/rune/proc/can_invoke(mob/living/user)
if(user.holy_check())
return
//This proc determines if the rune can be invoked at the time. If there are multiple required cultists, it will find all nearby cultists.
var/list/invokers = list() //people eligible to invoke the rune
var/list/chanters = list() //people who will actually chant the rune when passed to invoke()
if(invisibility == INVISIBILITY_HIDDEN_RUNES)//hidden rune
return
// Get the user
if(user)
chanters |= user
invokers |= user
// Get anyone nearby
if(req_cultists > 1 || allow_excess_invokers)
for(var/mob/living/L in range(1, src))
if(IS_CULTIST(L))
if(L == user)
continue
if(L.stat)
continue
invokers |= L
if(length(invokers) >= req_cultists) // If there's enough invokers
if(allow_excess_invokers)
chanters |= invokers // Let the others join in too
else
invokers -= user
shuffle(invokers)
for(var/i in 0 to req_cultists)
var/L = pick_n_take(invokers)
chanters |= L
return chanters
/obj/effect/rune/proc/invoke(list/invokers)
//This proc contains the effects of the rune as well as things that happen afterwards. If you want it to spawn an object and then delete itself, have both here.
SHOULD_CALL_PARENT(TRUE)
var/ghost_invokers = 0
for(var/M in invokers)
var/mob/living/L = M
if(!L)
return
if(L.has_status_effect(STATUS_EFFECT_SUMMONEDGHOST))
ghost_invokers++
if(invocation)
if(!L.IsVocal() || L.cannot_speak_loudly())
L.custom_emote(EMOTE_VISIBLE, message = pick("draws arcane sigils in the air.","gestures ominously.","silently mouths out an invocation.","places their hands on the rune, activating it."))
else
L.say(invocation)
L.changeNext_move(CLICK_CD_MELEE)//THIS IS WHY WE CAN'T HAVE NICE THINGS
if(invoke_damage)
L.apply_damage(invoke_damage, BRUTE)
to_chat(L, SPAN_CULTITALIC("[src] saps your strength!"))
do_invoke_glow()
SSblackbox.record_feedback("nested tally", "runes_invoked", 1, list("[initial(cultist_name)]", "[length(SSticker.mode.cult_team.members)]")) // the name of the rune, and the number of cultists in the cult when it was invoked
if(ghost_invokers)
SSblackbox.record_feedback("nested tally", "runes_invoked_with_ghost", 1, list("[initial(cultist_name)]", "[ghost_invokers]")) //the name of the rune and the number of ghosts used to invoke it.
/**
* Spawns the phase in/out effects for a cult teleport.
*
* Arguments:
* * user - Mob to teleport
* * location - Location to teleport from
* * target - Location to teleport to
*/
/obj/effect/rune/proc/teleport_effect(mob/living/user, turf/location, turf/target)
new /obj/effect/temp_visual/dir_setting/cult/phase/out(location, user.dir)
new /obj/effect/temp_visual/dir_setting/cult/phase(target, user.dir)
// So that the mob only appears after the effect is finished
user.notransform = TRUE
user.invisibility = INVISIBILITY_MAXIMUM
sleep(12)
user.notransform = FALSE
user.invisibility = 0
/obj/effect/rune/proc/do_invoke_glow()
var/oldtransform = transform
animate(src, transform = matrix() * 2, alpha = 0, time = 5) // Fade out
animate(transform = oldtransform, alpha = 255, time = 0)
/obj/effect/rune/proc/fail_invoke(show_message = TRUE)
//This proc contains the effects of a rune if it is not invoked correctly, through either invalid wording or not enough cultists. By default, it's just a basic fizzle.
if(!invisibility && show_message) // No visible messages if not visible
visible_message(SPAN_WARNING("The markings pulse with a small flash of red light, then fall dark."))
animate(src, color = rgb(255, 0, 0), time = 0)
animate(src, color = rune_blood_color, time = 5)
//Malformed Rune: This forms if a rune is not drawn correctly. Invoking it does nothing but hurt the user.
/obj/effect/rune/malformed
cultist_name = "Malformed"
cultist_desc = "a senseless rune written in gibberish. No good can come from invoking this."
invocation = "Ra'sha yoka!"
invoke_damage = 30
/obj/effect/rune/malformed/invoke(list/invokers)
..()
for(var/M in invokers)
var/mob/living/L = M
to_chat(L, SPAN_CULTITALIC("<b>You feel your life force draining. [GET_CULT_DATA(entity_title3, "Your god")] is displeased.</b>"))
qdel(src)
/mob/proc/null_rod_check() //The null rod, if equipped, will protect the holder from the effects of most runes
if(can_block_magic(MAGIC_RESISTANCE_HOLY))
return TRUE
return FALSE
//Rite of Enlightenment: Converts a normal crewmember to the cult, or offer them as sacrifice if cant be converted.
/obj/effect/rune/convert
cultist_name = "Offer"
cultist_desc = "offers non-cultists on top of it to the Dark One, either converting or sacrificing them. Sacrifices with a soul will result in a captured soulshard. This can be done with brains as well."
invocation = "Mah'weyh pleggh at e'ntrath!"
icon_state = "offering"
allow_excess_invokers = TRUE
/obj/effect/rune/convert/invoke(list/invokers)
if(rune_in_use)
return
var/list/offer_targets = list()
var/turf/T = get_turf(src)
for(var/mob/living/M in T)
if(!IS_CULTIST(M) || (M.mind && IS_SACRIFICE_TARGET(M.mind)))
if(isconstruct(M)) // No offering constructs please
continue
offer_targets += M
// Offering a head/brain
for(var/obj/item/organ/O in T)
var/mob/living/brain/b_mob
if(istype(O, /obj/item/organ/external/head)) // Offering a head
var/obj/item/organ/external/head/H = O
for(var/obj/item/organ/internal/brain/brain in H.contents)
b_mob = brain.brainmob
brain.forceMove(T)
else if(istype(O, /obj/item/organ/internal/brain)) // Offering a brain
var/obj/item/organ/internal/brain/brain = O
b_mob = brain.brainmob
if(b_mob && b_mob.mind && (!IS_CULTIST(b_mob) || IS_SACRIFICE_TARGET(b_mob.mind)))
offer_targets += b_mob
if(!length(offer_targets))
fail_invoke()
log_game("Offer rune failed - no eligible targets")
rune_in_use = FALSE
return
rune_in_use = TRUE
var/mob/living/L = pick(offer_targets)
if(HAS_TRAIT(L, TRAIT_CULT_IMMUNITY))
fail_invoke(FALSE)
for(var/I in invokers)
to_chat(I, SPAN_WARNING("This sacrifice was already converted recently. Wait a minute before trying again!"))
rune_in_use = FALSE
return
if(L.stat != DEAD && SSticker.mode.cult_team.is_convertable_to_cult(L.mind))
..()
do_convert(L, invokers)
else
invocation = "Barhah hra zar'garis!"
..()
do_sacrifice(L, invokers)
rune_in_use = FALSE
/obj/effect/rune/convert/proc/do_convert(mob/living/convertee, list/invokers)
if(length(invokers) < 2)
fail_invoke()
for(var/I in invokers)
to_chat(I, SPAN_WARNING("You need at least two invokers to convert!"))
return
if(HAS_TRAIT(convertee, TRAIT_WAS_SACRIFICED) && length(invokers) < 3) // harder to convert heretic-touched
fail_invoke()
for(var/I in invokers)
to_chat(I, SPAN_WARNING("This one struggles against our influence with an unknown force. We require at least 3 invokers to convert them!"))
return
convertee.visible_message(SPAN_WARNING("[convertee] writhes in pain as the markings below them glow a bloody red!"), \
SPAN_CULTLARGE("<i>AAAAAAAAAAAAAA-</i>"))
convertee.mind.add_antag_datum(/datum/antagonist/cultist)
to_chat(convertee, SPAN_CULTITALIC("<b>Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible, truth. The veil of reality has been ripped away \
and something evil takes root.</b>"))
to_chat(convertee, SPAN_CULTITALIC("<b>Assist your new compatriots in their dark dealings. Your goal is theirs, and theirs is yours. You serve [GET_CULT_DATA(entity_title3, "your god")] above all else. Bring it back.\
</b>"))
if(HAS_TRAIT(convertee, TRAIT_WAS_SACRIFICED)) // Augh! heretic backlash!
playsound(convertee, 'sound/magic/voidblink.ogg', 50, FALSE)
if(prob(20))
var/mob_type = pick(list(
/mob/living/basic/heretic_summon/rust_spirit,
/mob/living/basic/heretic_summon/ash_spirit,
/mob/living/basic/heretic_summon/stalker,
/mob/living/basic/heretic_summon/raw_prophet,
/mob/living/basic/heretic_summon/maid_in_the_mirror,
))
convertee.visible_message(SPAN_DANGER("A heretical beast is pulled out of [convertee] during the offering!"))
new mob_type(convertee.loc)
for(var/mob in invokers)
if(iscarbon(mob))
to_chat(mob, SPAN_HIEROPHANT_WARNING("AUGH! A vile magic recoils against our influence and poisons us!"))
var/mob/living/carbon/carbon = mob
carbon.reagents.add_reagent("toxin", 15)
carbon.reagents.add_reagent("histamine", 15)
if(ishuman(convertee))
var/mob/living/carbon/human/H = convertee
var/brutedamage = convertee.getBruteLoss()
var/burndamage = convertee.getFireLoss()
if(brutedamage || burndamage) // If the convertee is injured
// Heal 90% of all damage, including robotic limbs
H.adjustBruteLoss(-(brutedamage * 0.9), robotic = TRUE)
H.adjustFireLoss(-(burndamage * 0.9), robotic = TRUE)
if(ismachineperson(H))
H.visible_message(SPAN_WARNING("A dark force repairs [convertee]!"),
SPAN_CULTITALIC("Your damage has been repaired. Now spread the blood to others."))
else
H.visible_message(SPAN_WARNING("[convertee]'s wounds heal and close!"),
SPAN_CULTITALIC("Your wounds have been healed. Now spread the blood to others."))
for(var/obj/item/organ/external/E in H.bodyparts)
E.mend_fracture()
E.fix_internal_bleeding()
E.fix_burn_wound()
for(var/datum/disease/critical/crit in H.viruses) // cure all crit conditions
crit.cure()
H.clear_restraints()
H.Silence(6 SECONDS) //Prevent "HALP MAINT CULT" before you realise you're converted
if(H.reagents?.has_reagent("holywater"))
H.reagents.del_reagent("holywater") // Also prevent fill stomach with holy water and "forgot" about it after converting
var/obj/item/melee/cultblade/dagger/D = new(get_turf(src))
if(H.equip_to_slot_if_possible(D, ITEM_SLOT_IN_BACKPACK, FALSE, TRUE))
to_chat(H, SPAN_CULTLARGE("You have a dagger in your backpack. Use it to do [GET_CULT_DATA(entity_title1, "your god")]'s bidding."))
else
to_chat(H, SPAN_CULTLARGE("There is a dagger on the floor. Use it to do [GET_CULT_DATA(entity_title1, "your god")]'s bidding."))
/obj/effect/rune/convert/proc/do_sacrifice(mob/living/offering, list/invokers)
var/mob/living/user = invokers[1] //the first invoker is always the user
if(offering.stat != DEAD || (offering.mind && IS_SACRIFICE_TARGET(offering.mind))) //Requires three people to sacrifice living targets/sacrifice objective
if(length(invokers) < 3)
for(var/M in invokers)
to_chat(M, SPAN_CULTITALIC("[offering] is too greatly linked to the world! You need three acolytes!"))
fail_invoke()
log_game("Sacrifice rune failed - not enough acolytes and target is living")
return
var/datum/antagonist/heretic/ascended_heretic = IS_HERETIC(offering)
if(ascended_heretic?.ascended && offering.stat != DEAD || istype(offering, /mob/living/basic/heretic_summon/star_gazer)) // No sliping an ascended heretic on a sacrifice rune to sacrifice them, lmao
for(var/M in invokers)
to_chat(M, SPAN_CULTITALIC("[offering] is too powerful to be sacrificed alive!"))
fail_invoke()
log_game("Sacrifice rune failed - heretic is ascended and alive")
return
var/mob/living/first_invoker = invokers[1]
var/datum/antagonist/cultist/first_invoker_datum = first_invoker.mind.has_antag_datum(/datum/antagonist/cultist)
var/datum/team/cult/cult_team = first_invoker_datum.get_team()
var/signal_result = SEND_SIGNAL(offering, COMSIG_LIVING_CULT_SACRIFICED, invokers, cult_team)
var/do_message = TRUE
if(signal_result & SILENCE_SACRIFICE_MESSAGE)
do_message = FALSE
var/sacrifice_fulfilled
var/worthless = FALSE
if(isliving(offering) && !isbrain(offering))
var/mob/living/L = offering
if(isrobot(L) || ismachineperson(L))
L.adjustBruteLoss(250)
else
L.adjustCloneLoss(120)
L.death(FALSE)
if(offering.mind)
GLOB.sacrificed += offering.mind
if(IS_SACRIFICE_TARGET(offering.mind))
sacrifice_fulfilled = TRUE
else
GLOB.sacrificed += offering
new /obj/effect/temp_visual/cult/sac(loc)
if(do_message)
for(var/M in invokers)
if(sacrifice_fulfilled)
to_chat(M, SPAN_CULTLARGE("\"Yes! This is the one I desire! You have done well.\""))
if(!SSticker.mode.cult_team.mirror_shields_active) // Only show once
to_chat(M, SPAN_CULTLARGE("You are now able to construct mirror shields inside the daemon forge."))
SSticker.mode.cult_team.mirror_shields_active = TRUE
else
if((ishuman(offering) && offering.mind?.offstation_role && offering.mind.special_role != SPECIAL_ROLE_ERT) || HAS_MIND_TRAIT(offering, TRAIT_XENOBIO_SPAWNED_HUMAN)) //If you try it on a ghost role, or an envolved caterpillar/nymph, you get nothing
to_chat(M, SPAN_CULTLARGE("\"This soul is of no use to either of us.\""))
worthless = TRUE
else if(ishuman(offering) || isrobot(offering))
to_chat(M, SPAN_CULTLARGE("\"I accept this sacrifice.\""))
else
to_chat(M, SPAN_CULTLARGE("\"I accept this meager sacrifice.\""))
// post-message
if(signal_result & STOP_SACRIFICE)
return FALSE
if(offering && (signal_result & DUST_SACRIFICE)) // No soulstone when dusted
playsound(offering, 'sound/magic/teleport_diss.ogg', 100, TRUE)
offering.dust()
else
playsound(offering, 'sound/misc/demon_consume.ogg', 100, TRUE, SOUND_RANGE_SET(10))
if(((ishuman(offering) || isrobot(offering) || isbrain(offering)) && offering.mind) && !worthless)
var/obj/item/soulstone/stone = new /obj/item/soulstone(get_turf(src))
stone.invisibility = INVISIBILITY_MAXIMUM // So it's not picked up during transfer_soul()
stone.transfer_soul("FORCE", offering, user) // If it cannot be added
stone.invisibility = 0
var/put_in_hands = user.put_in_any_hand_if_possible(stone)
if(put_in_hands)
to_chat(user, SPAN_CULTITALIC("A glowing crimson shard appears in your hand - your new ally contained within."))
else
if(isrobot(offering))
offering.dust() //To prevent the MMI from remaining
else
offering.gib()
playsound(offering, 'sound/magic/disintegrate.ogg', 100, TRUE, SOUND_RANGE_SET(10))
if(sacrifice_fulfilled)
SSticker.mode.cult_team.successful_sacrifice()
return TRUE
/obj/effect/rune/teleport
cultist_name = "Teleport"
cultist_desc = "warps everything above it to another chosen teleport rune."
invocation = "Sas'so c'arta forbici!"
icon_state = "teleport"
req_keyword = TRUE
light_power = 4
var/obj/effect/temp_visual/cult/portal/inner_portal //The portal "hint" for off-station teleportations
var/obj/effect/temp_visual/cult/rune_spawn/rune2/outer_portal
var/listkey
/obj/effect/rune/teleport/Initialize(mapload, set_keyword)
. = ..()
var/area/A = get_area(src)
var/locname = initial(A.name)
listkey = set_keyword ? "[set_keyword] [locname]":"[locname]"
GLOB.teleport_runes += src
/obj/effect/rune/teleport/Destroy()
GLOB.teleport_runes -= src
QDEL_NULL(inner_portal)
QDEL_NULL(outer_portal)
return ..()
/obj/effect/rune/teleport/can_dagger_erase_rune(mob/user)
// Can't erase telerunes if they have a portal open
if(inner_portal || outer_portal)
to_chat(user, SPAN_WARNING("The portal needs to close first!"))
return FALSE
return TRUE
/obj/effect/rune/teleport/invoke(list/invokers)
var/mob/living/user = invokers[1] //the first invoker is always the user
var/list/potential_runes = list()
var/list/teleportnames = list()
var/list/duplicaterunecount = list()
for(var/I in GLOB.teleport_runes)
var/obj/effect/rune/teleport/R = I
var/resultkey = R.listkey
if(resultkey in teleportnames)
duplicaterunecount[resultkey]++
resultkey = "[resultkey] ([duplicaterunecount[resultkey]])"
else
teleportnames += resultkey
duplicaterunecount[resultkey] = 1
if(R != src && is_level_reachable(R.z))
potential_runes[resultkey] = R
if(!length(potential_runes))
to_chat(user, SPAN_WARNING("There are no valid runes to teleport to!"))
log_game("Teleport rune failed - no other teleport runes")
fail_invoke()
return
if(!is_level_reachable(user.z))
to_chat(user, SPAN_CULTITALIC("You are too far away from the station to teleport!"))
log_game("Teleport rune failed - user in away mission")
fail_invoke()
return
var/input_rune_key = tgui_input_list(user, "Choose a rune to teleport to.", "Rune to Teleport to", potential_runes) //we know what key they picked
var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to?
if(QDELETED(src) || QDELETED(actual_selected_rune) ||!Adjacent(user) || user.incapacitated())
fail_invoke()
return
var/turf/T = get_turf(src)
var/turf/target = get_turf(actual_selected_rune)
var/movedsomething = FALSE
var/moveuser = FALSE
for(var/atom/movable/A in T)
if(SEND_SIGNAL(A, COMSIG_MOVABLE_TELEPORTING, target) & COMPONENT_BLOCK_TELEPORT)
continue
if(ishuman(A))
if(A != user) // Teleporting someone else
INVOKE_ASYNC(src, PROC_REF(teleport_effect), A, T, target)
else // Teleporting yourself
INVOKE_ASYNC(src, PROC_REF(teleport_effect), user, T, target)
if(A.move_resist == INFINITY)
continue //object cant move, shouldnt teleport
if(A == user)
moveuser = TRUE
movedsomething = TRUE
continue
if(!A.anchored)
movedsomething = TRUE
A.forceMove(target)
if(movedsomething)
..()
if(is_mining_level(z) && !is_mining_level(target.z)) //No effect if you stay on lavaland
actual_selected_rune.handle_portal("lava")
else if(!is_station_level(z) || isspacearea(get_area(src)))
actual_selected_rune.handle_portal("space", T)
user.visible_message(SPAN_WARNING("There is a sharp crack of inrushing air, and everything above the rune disappears!"),
SPAN_CULT("You[moveuser ? "r vision blurs, and you suddenly appear somewhere else":" send everything above the rune away"]."))
if(moveuser)
user.forceMove(target)
else
fail_invoke()
/obj/effect/rune/teleport/proc/handle_portal(portal_type, turf/origin)
var/turf/T = get_turf(src)
if(inner_portal || outer_portal)
close_portal() // To avoid stacking descriptions/animations
playsound(T, pick('sound/effects/sparks1.ogg', 'sound/effects/sparks2.ogg', 'sound/effects/sparks3.ogg', 'sound/effects/sparks4.ogg'), 100, TRUE, 14)
inner_portal = new /obj/effect/temp_visual/cult/portal(T)
if(portal_type == "space")
light_color = color
desc += "<br>[SPAN_BOLDWARNING("A tear in reality reveals a black void interspersed with dots of light... something recently teleported here from space.")]<br>"
// Space base near the station
if(is_station_level(origin.z))
desc += "<u>[SPAN_WARNING("The void feels like it's trying to pull you to the [dir2text(get_dir(T, origin))], near the station!")]</u>"
// Space base on another Z-level
else
desc += "<u>[SPAN_WARNING("The void feels like it's trying to pull you to the [dir2text(get_dir(T, origin))], in the direction of space sector [origin.z]!")]</u>"
else
inner_portal.icon_state = "lava"
light_color = LIGHT_COLOR_FIRE
desc += "<br>[SPAN_BOLDWARNING("A tear in reality reveals a coursing river of lava... something recently teleported here from the Lavaland Mines!")]"
outer_portal = new(T, 60 SECONDS, color)
light_range = 4
update_light()
addtimer(CALLBACK(src, PROC_REF(close_portal)), 60 SECONDS, TIMER_UNIQUE)
/obj/effect/rune/teleport/proc/close_portal()
qdel(inner_portal)
qdel(outer_portal)
desc = initial(desc)
light_range = 0
update_light()
//Rune of Empowering : Enables carrying 4 blood spells, greatly reduce blood cost
/obj/effect/rune/empower
cultist_name = "Empower"
cultist_desc = "allows cultists to prepare greater amounts of blood magic at far less of a cost."
invocation = "H'drak v'loso, mir'kanas verbot!"
icon_state = "empower"
construct_invoke = FALSE
/obj/effect/rune/empower/invoke(list/invokers)
. = ..()
var/mob/living/user = invokers[1] //the first invoker is always the user
for(var/datum/action/innate/cult/blood_magic/BM in user.actions)
BM.Activate()
//Rite of Resurrection: Requires a dead or inactive cultist. When reviving the dead, you can only perform one revival for every three sacrifices your cult has carried out.
/obj/effect/rune/raise_dead
cultist_name = "Revive"
cultist_desc = "requires a dead, alive, mindless, or inactive cultist placed upon the rune. For each three bodies sacrificed to the dark patron, one body will be mended and their mind awoken. Mending living cultist requires two cultists at the rune"
invocation = "Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!" //Depends on the name of the user - see below
icon_state = "revive"
var/static/sacrifices_used = -SOULS_TO_REVIVE // Cultists get one "free" revive
allow_excess_invokers = TRUE
/obj/effect/rune/raise_dead/examine(mob/user)
. = ..()
if(IS_CULTIST(user) || user.stat == DEAD)
. += "<b>Sacrifices unrewarded:</b>[SPAN_CULTITALIC(" [length(GLOB.sacrificed) - sacrifices_used]")]"
. += "<b>Sacrifice cost per ressurection:</b>[SPAN_CULTITALIC(" [SOULS_TO_REVIVE]")]"
/obj/effect/rune/raise_dead/proc/revive_alive(mob/living/target)
target.visible_message(SPAN_WARNING("Dark magic begins to surround [target], regenerating their body."))
if(!do_after(target, 10 SECONDS, FALSE, target, allow_moving = FALSE, progress = TRUE))
target.visible_message(SPAN_WARNING("Dark magic silently disappears."))
return FALSE
target.revive()
return TRUE
/obj/effect/rune/raise_dead/proc/revive_dead(mob/living/target)
target.revive()
if(target.ghost_can_reenter())
target.grab_ghost()
return TRUE
/obj/effect/rune/raise_dead/invoke(list/invokers)
var/turf/T = get_turf(src)
var/mob/living/mob_to_revive
var/list/potential_revive_mobs = list()
var/mob/living/user = invokers[1]
if(rune_in_use)
return
rune_in_use = TRUE
var/diff = length(GLOB.sacrificed) - SOULS_TO_REVIVE - sacrifices_used
var/revived_from_dead = FALSE
if(diff < 0)
to_chat(user, SPAN_CULT("Your cult must carry out [abs(diff)] more sacrifice\s before it can revive another cultist!"))
fail_invoke()
return
for(var/mob/living/M in T.contents)
if(!IS_CULTIST(M))
continue
potential_revive_mobs |= M
if(!length(potential_revive_mobs))
to_chat(user, SPAN_CULTITALIC("There are no cultists on the rune!"))
log_game("Raise Dead rune failed - no cultists to revive")
fail_invoke()
return
if(length(potential_revive_mobs) > 1)
mob_to_revive = tgui_input_list(user, "Choose a cultist to revive.", "Cultist to Revive", potential_revive_mobs)
else // If there's only one, no need for a menu
mob_to_revive = potential_revive_mobs[1]
if(!validness_checks(mob_to_revive, user))
fail_invoke()
return
if(mob_to_revive.stat != DEAD && length(invokers) < 2)
to_chat(user, SPAN_CULTITALIC("You need at least two cultists to heal cultist!"))
log_game("Raise Dead rune failed - not enough cultists to heal alive")
fail_invoke()
return
if(mob_to_revive.stat != DEAD)
if(!revive_alive(mob_to_revive))
fail_invoke()
return
else
if(!revive_dead(mob_to_revive))
fail_invoke()
return
revived_from_dead = TRUE
..()
sacrifices_used += SOULS_TO_REVIVE
if(!mob_to_revive.get_ghost() && (!mob_to_revive.client || mob_to_revive.client.is_afk()))
set waitfor = FALSE
to_chat(user, SPAN_CULT("[mob_to_revive] was revived, but their mind is lost! Seeking a lost soul to replace it."))
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Would you like to play as a revived Cultist?", ROLE_CULTIST, TRUE, poll_time = 20 SECONDS, source = /obj/item/melee/cultblade/dagger)
if(length(candidates) && !QDELETED(mob_to_revive))
var/mob/dead/observer/C = pick(candidates)
to_chat(mob_to_revive, SPAN_BIGGERDANGER("Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form."))
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(mob_to_revive)]) to replace an AFK player.")
mob_to_revive.ghostize(GHOST_FLAGS_NO_REENTER)
mob_to_revive.key = C.key
dust_if_respawnable(C)
else
fail_invoke()
return
if(!revived_from_dead)
mob_to_revive.visible_message(SPAN_WARNING("[mob_to_revive] draws in a huge breath, red light shining from [mob_to_revive.p_their()] eyes."), \
SPAN_CULTLARGE("All your injuries are now gone!"))
rune_in_use = FALSE
return
SEND_SOUND(mob_to_revive, sound('sound/ambience/antag/bloodcult.ogg'))
to_chat(mob_to_revive, SPAN_CULTLARGE("\"PASNAR SAVRAE YAM'TOTH. Arise.\""))
mob_to_revive.visible_message(SPAN_WARNING("[mob_to_revive] draws in a huge breath, red light shining from [mob_to_revive.p_their()] eyes."), \
SPAN_CULTLARGE("You awaken suddenly from the void. You're alive!"))
rune_in_use = FALSE
/obj/effect/rune/raise_dead/proc/validness_checks(mob/living/target_mob, mob/living/user)
if(QDELETED(src))
return FALSE
if(QDELETED(user))
return FALSE
if(!Adjacent(user) || user.incapacitated())
return FALSE
if(QDELETED(target_mob))
return FALSE
var/turf/T = get_turf(src)
if(target_mob.loc != T)
to_chat(user, SPAN_CULTITALIC("The cultist to revive has been moved!"))
log_game("Raise Dead rune failed - revival target moved")
return FALSE
return TRUE
/obj/effect/rune/raise_dead/fail_invoke()
..()
rune_in_use = FALSE
for(var/mob/living/M in range(0, src))
if(IS_CULTIST(M) && M.stat == DEAD)
M.visible_message(SPAN_WARNING("[M] twitches."))
//Rite of the Corporeal Shield: When invoked, becomes solid and cannot be passed. Invoke again to undo.
/obj/effect/rune/wall
cultist_name = "Barrier"
cultist_desc = "when invoked makes a temporary wall to block passage. Can be destroyed by brute force. Can be invoked again to reverse this."
invocation = "Khari'd! Eske'te tannin!"
icon_state = "barrier"
///The barrier summoned by the rune when invoked. Tracked as a variable to prevent refreshing the barrier's integrity. shieldgen.dm
var/obj/machinery/shield/cult/barrier/B
/obj/effect/rune/wall/Initialize(mapload)
. = ..()
B = new /obj/machinery/shield/cult/barrier(loc)
B.parent_rune = src
/obj/effect/rune/wall/Destroy()
if(B && !QDELETED(B))
QDEL_NULL(B)
return ..()
/obj/effect/rune/wall/invoke(list/invokers)
var/mob/living/user = invokers[1]
..()
var/amount = 1
if(B.Toggle()) // Toggling on
for(var/obj/effect/rune/wall/rune in orange(1, src)) // Chaining barriers
if(!rune.B.density) // Barrier is currently invisible
amount++ // Count the invoke damage for each rune
rune.do_invoke_glow()
rune.B.Toggle()
if(iscarbon(user))
var/mob/living/carbon/C = user
C.cult_self_harm(2 * amount)
//Rite of Joined Souls: Summons a single cultist.
/obj/effect/rune/summon
cultist_name = "Summon Cultist"
cultist_desc = "summons a single cultist to the rune. <b><i>(Cannot summon restrained cultists!)</b></i>"
invocation = "N'ath reth sh'yro eth d'rekkathnor!"
req_cultists = 2
invoke_damage = 10
icon_state = "summon"
/obj/effect/rune/summon/invoke(list/invokers)
var/mob/living/user = invokers[1]
var/list/cultists = list()
for(var/datum/mind/M in SSticker.mode.cult_team.members)
if(!(M.current in invokers) && M.current && M.current.stat != DEAD)
cultists[M.current.real_name] = M.current
var/input = tgui_input_list(user, "Who do you wish to call to [src]?", "Acolytes", cultists)
var/mob/living/cultist_to_summon = cultists[input]
if(QDELETED(src) || !Adjacent(user) || user.incapacitated())
return
if(!cultist_to_summon)
log_game("Summon Cultist rune failed - no target")
return
if(cultist_to_summon.stat == DEAD)
to_chat(user, SPAN_CULTITALIC("[cultist_to_summon] has died!"))
fail_invoke()
log_game("Summon Cultist rune failed - target died")
return
if(cultist_to_summon.pulledby || cultist_to_summon.buckled)
to_chat(user, SPAN_CULTITALIC("[cultist_to_summon] is being held in place!"))
to_chat(cultist_to_summon, "<span class='cult'>You feel a tugging sensation, but you are being held in place!")
fail_invoke()
log_game("Summon Cultist rune failed - target restrained")
return
if(!IS_CULTIST(cultist_to_summon))
to_chat(user, SPAN_CULTITALIC("[cultist_to_summon] is not a follower of [GET_CULT_DATA(entity_title3, "our god")]!"))
fail_invoke()
log_game("Summon Cultist rune failed - target was deconverted")
return
if(is_away_level(cultist_to_summon.z))
to_chat(user, SPAN_CULTITALIC("[cultist_to_summon] is not in our dimension!"))
fail_invoke()
log_game("Summon Cultist rune failed - target in away mission")
return
if(SEND_SIGNAL(cultist_to_summon, COMSIG_MOVABLE_TELEPORTING, get_turf(src)) & COMPONENT_BLOCK_TELEPORT)
to_chat(user, SPAN_CULTITALIC("[cultist_to_summon] is anchored in bluespace!"))
fail_invoke()
log_game("Summon Cultist rune failed - anchored in bluespace")
return
cultist_to_summon.visible_message(SPAN_WARNING("[cultist_to_summon] suddenly disappears in a flash of red light!"), \
SPAN_CULTITALIC("<b>Overwhelming vertigo consumes you as you are hurled through the air!</b>"))
..()
INVOKE_ASYNC(src, PROC_REF(teleport_effect), cultist_to_summon, get_turf(cultist_to_summon), get_turf(src))
visible_message(SPAN_WARNING("[src] begins to bubble and rises into the form of [cultist_to_summon]!"))
cultist_to_summon.forceMove(get_turf(src))
qdel(src)
/**
* # Blood Boil Rune
*
* When invoked deals up to 30 burn damage to nearby non-cultists and sets them on fire.
*
* On activation the rune charges for six seconds, changing colour, glowing, and giving out a warning to all nearby mobs.
* After the charging period the rune burns any non-cultists in view and sets them on fire. After another short wait it does the same again with slightly higher damage.
* If the cultists channeling the rune move away or are stunned at any point, the rune is deleted. So it can be countered pretty easily with flashbangs.
*/
/obj/effect/rune/blood_boil
cultist_name = "Boil Blood"
cultist_desc = "boils the blood of non-believers who can see the rune, rapidly dealing extreme amounts of damage. Requires 2 invokers channeling the rune."
invocation = "Dedo ol'btoh!"
icon_state = "blood_boil"
light_color = LIGHT_COLOR_LAVA
req_cultists = 2
invoke_damage = 15
construct_invoke = FALSE
var/tick_damage = 10 // 30 burn damage total + damage taken by being on fire/overheating
/obj/effect/rune/blood_boil/invoke(list/invokers)
if(rune_in_use)
return
..()
rune_in_use = TRUE
var/turf/T = get_turf(src)
var/list/targets = list()
for(var/mob/living/L in viewers(T))
if(!IS_CULTIST(L) && L.blood_volume && !ismachineperson(L))
var/atom/I = L.null_rod_check()
if(I)
if(isitem(I))
to_chat(L, SPAN_USERDANGER("[I] suddenly burns hotly before returning to normal!"))
continue
targets += L
// Six seconds buildup
visible_message(SPAN_WARNING("A haze begins to form above [src]!"))
animate(src, color = "#FC9A6D", time = 6 SECONDS)
set_light(6, 1, color)
sleep(6 SECONDS)
visible_message(SPAN_BOLDWARNING("[src] turns a bright, burning orange!"))
if(!burn_check())
return
for(var/I in targets)
to_chat(I, SPAN_USERDANGER("Your blood boils in your veins!"))
do_area_burn(T, 1)
animate(src, color = "#FFDF80", time = 5 SECONDS)
sleep(5 SECONDS)
if(!burn_check())
return
do_area_burn(T, 2)
animate(src, color = "#FFFFFF", time = 5 SECONDS)
sleep(5 SECONDS)
if(!burn_check())
return
do_area_burn(T, 3)
qdel(src)
/obj/effect/rune/blood_boil/proc/do_area_burn(turf/T, iteration)
var/multiplier = iteration / 2 // Iteration 1 = 0.5, Iteration 2 = 1, etc.
set_light(6, 1 * iteration, color)
for(var/mob/living/L in viewers(T))
if(!IS_CULTIST(L) && L.blood_volume && !ismachineperson(L))
if(L.null_rod_check())
continue
L.take_overall_damage(0, tick_damage * multiplier)
L.adjust_fire_stacks(2)
L.IgniteMob()
playsound(src, 'sound/effects/bamf.ogg', 100, TRUE)
do_invoke_glow()
sleep(0.6 SECONDS) // Only one 'animate()' can play at once, so this waits for the pulse to finish
/obj/effect/rune/blood_boil/proc/burn_check()
. = TRUE
if(QDELETED(src))
return FALSE
var/list/cultists = list()
for(var/mob/living/M in range(1, src)) // Get all cultists currently in range
if(IS_CULTIST(M) && !M.incapacitated())
cultists += M
if(length(cultists) < req_cultists) // Stop the rune there's not enough invokers
visible_message(SPAN_WARNING("[src] loses its glow and dissipates!"))
qdel(src)
/obj/effect/rune/manifest
cultist_name = "Spirit Realm"
cultist_desc = "manifests a spirit servant of the Dark One and allows you to ascend as a spirit yourself. The invoker must not move from atop the rune, and will take damage for each summoned spirit."
invocation = "Gal'h'rfikk harfrandid mud'gib!" //how the fuck do you pronounce this
icon_state = "spirit_realm"
construct_invoke = FALSE
var/mob/dead/observer/ghost = null //The cult ghost of the user
var/default_ghost_limit = 4 //Lowered by the amount of cult objectives done
var/minimum_ghost_limit = 2 //But cant go lower than this
var/ghosts = 0
/obj/effect/rune/manifest/examine(mob/user)
. = ..()
if(IS_CULTIST(user) || user.stat == DEAD)
. += "<b>Amount of ghosts summoned:</b>[SPAN_CULTITALIC(" [ghosts]")]"
. += "<b>Maximum amount of ghosts:</b>[SPAN_CULTITALIC(" [clamp(default_ghost_limit - SSticker.mode.cult_team.sacrifices_done, minimum_ghost_limit, default_ghost_limit)]")]"
. += "Lowers to a minimum of [minimum_ghost_limit] for each objective accomplished."
/obj/effect/rune/manifest/invoke(list/invokers)
. = ..()
var/mob/living/user = invokers[1]
var/turf/T = get_turf(src)
if(!(user in get_turf(src)))
to_chat(user, SPAN_CULTITALIC("You must be standing on [src]!"))
fail_invoke()
log_game("Manifest rune failed - user not standing on rune")
return
if(user.has_status_effect(STATUS_EFFECT_SUMMONEDGHOST))
to_chat(user, SPAN_CULTITALIC("Ghosts can't summon more ghosts!"))
fail_invoke()
log_game("Manifest rune failed - user is a ghost")
return
var/choice = tgui_alert(user, "You tear open a connection to the spirit realm...", "Invoke", list("Summon a Cult Ghost", "Ascend as a Dark Spirit", "Cancel"))
if(choice == "Summon a Cult Ghost")
if(!is_station_level(z) || isspacearea(get_area(src)))
to_chat(user, SPAN_CULTITALIC("The veil is not weak enough here to manifest spirits, you must be on station!"))
fail_invoke()
log_game("Manifest rune failed - not on station")
return
if(user.health <= 40)
to_chat(user, SPAN_CULTITALIC("Your body is too weak to manifest spirits, heal yourself first."))
fail_invoke()
log_game("Manifest rune failed - not enough health")
return list()
if(ghosts >= clamp(default_ghost_limit - SSticker.mode.cult_team.sacrifices_done, minimum_ghost_limit, default_ghost_limit))
to_chat(user, SPAN_CULTITALIC("You are sustaining too many ghosts to summon more!"))
fail_invoke()
log_game("Manifest rune failed - too many summoned ghosts")
return list()
summon_ghosts(user, T)
else if(choice == "Ascend as a Dark Spirit")
if(length(SSticker.mode.heretics))
to_chat(user, SPAN_HIEROPHANT("The Mansus fogs your view into the spirit wrold..."))
fail_invoke()
log_game("Manifest rune failed - heretic blocked ghosting")
return list()
ghostify(user, T)
/obj/effect/rune/manifest/proc/summon_ghosts(mob/living/user, turf/T)
notify_ghosts("Manifest rune created in [get_area(src)].", ghost_sound = 'sound/effects/ghost2.ogg', source = src)
var/list/ghosts_on_rune = list()
for(var/mob/dead/observer/O in T)
if(!O.client)
continue
if(IS_CULTIST(O) || jobban_isbanned(O, ROLE_CULTIST))
continue
if(!HAS_TRAIT(O, TRAIT_RESPAWNABLE) || QDELETED(src) || QDELETED(O))
continue
if(!O.mind)
continue
if(O.mind.current && HAS_TRAIT(O.mind.current, SCRYING))
continue
ghosts_on_rune += O
if(!length(ghosts_on_rune))
to_chat(user, SPAN_CULTITALIC("There are no spirits near [src]!"))
fail_invoke()
log_game("Manifest rune failed - no nearby ghosts")
return list()
var/mob/dead/observer/ghost_to_spawn = pick(ghosts_on_rune)
var/mob/living/carbon/human/new_human = new(T)
new_human.real_name = ghost_to_spawn.real_name
new_human.key = ghost_to_spawn.key
new_human.gender = ghost_to_spawn.gender
new_human.alpha = 150 //Makes them translucent
ADD_TRAIT(new_human, TRAIT_MUTE, UID())
new_human.equipOutfit(/datum/outfit/ghost_cultist) //give them armor
new_human.apply_status_effect(STATUS_EFFECT_SUMMONEDGHOST, user) //ghosts can't summon more ghosts, also lets you see actual ghosts
for(var/obj/item/organ/external/current_organ in new_human.bodyparts)
current_organ.limb_flags |= CANNOT_DISMEMBER //you can't chop of the limbs of a ghost, silly
ghosts++
playsound(src, 'sound/misc/exit_blood.ogg', 50, TRUE, SOUND_RANGE_SET(10))
user.visible_message(SPAN_WARNING("A cloud of red mist forms above [src], and from within steps... a [new_human.gender == FEMALE ? "wo" : ""]man."),
SPAN_CULTITALIC("Your blood begins flowing into [src]. You must remain in place and conscious to maintain the forms of those summoned. This will hurt you slowly but surely..."))
var/obj/machinery/shield/cult/weak/shield = new(T)
new_human.mind.add_antag_datum(/datum/antagonist/cultist)
to_chat(new_human, SPAN_CULTLARGE("You are a servant of [GET_CULT_DATA(entity_title3, "the cult")]. You have been made semi-corporeal by the cult of [GET_CULT_DATA(entity_name, "your god")], and you are to serve them at all costs."))
while(!QDELETED(src) && !QDELETED(user) && !QDELETED(new_human) && (user in T))
if(new_human.InCritical())
to_chat(user, SPAN_CULTITALIC("You feel your connection to [new_human.real_name] severs as they are destroyed."))
if(ghost)
to_chat(ghost, SPAN_CULTITALIC("You feel your connection to [new_human.real_name] severs as they are destroyed."))
break
if(user.stat || user.health <= 40)
to_chat(user, SPAN_CULTITALIC("Your body can no longer sustain the connection, and your link to the spirit realm fades."))
if(ghost)
to_chat(ghost, SPAN_CULTITALIC("Your body is damaged and your connection to the spirit realm weakens, any ghost you may have manifested are destroyed."))
break
user.apply_damage(0.1, BRUTE)
user.apply_damage(0.1, BURN)
sleep(2) //Takes two pylons to sustain the damage taken by summoning one ghost
qdel(shield)
ghosts--
if(new_human)
new_human.visible_message(SPAN_WARNING("[new_human] suddenly dissolves into bones and ashes."),
SPAN_CULTLARGE("Your link to the world fades. Your form breaks apart."))
for(var/obj/item/I in new_human.get_all_slots())
new_human.drop_item_to_ground(I)
new_human.mind.remove_antag_datum(/datum/antagonist/cultist, silent_removal = TRUE)
new_human.dust()
/obj/effect/rune/manifest/proc/ghostify(mob/living/user, turf/T)
ADD_TRAIT(user, SCRYING, CULT_TRAIT)
user.visible_message(SPAN_WARNING("[user] freezes statue-still, glowing an unearthly red."),
SPAN_CULT("You see what lies beyond. All is revealed. In this form you find that your voice booms above all others."))
ghost = user.ghostize(ghost_name = "Dark Spirit of [user.name]", ghost_color = RUNE_COLOR_DARKRED)
var/datum/action/innate/cult/comm/spirit/CM = new
var/datum/action/innate/cult/check_progress/V = new
//var/datum/action/innate/cult/ghostmark/GM = new
CM.Grant(ghost)
V.Grant(ghost)
//GM.Grant(ghost)
while(!QDELETED(user))
if(user.key || QDELETED(src))
user.visible_message(SPAN_WARNING("[user] slowly relaxes, the glow around [user.p_them()] dimming."),
SPAN_DANGER("You are re-united with your physical form. [src] releases its hold over you."))
user.Weaken(6 SECONDS)
break
if(user.health <= 10)
to_chat(ghost, SPAN_CULTITALIC("Your body can no longer sustain the connection!"))
break
if(!(user in T))
user.visible_message(SPAN_WARNING("A spectral tendril wraps around [user] and pulls [user.p_them()] back to the rune!"))
Beam(user, icon_state = "drainbeam", time = 2)
user.forceMove(get_turf(src)) //NO ESCAPE :^)
sleep(5)
if(user.grab_ghost())
CM.Remove(ghost)
V.Remove(ghost)
//GM.Remove(ghost)
REMOVE_TRAIT(user, SCRYING, CULT_TRAIT)
user.remove_atom_colour(ADMIN_COLOUR_PRIORITY, RUNE_COLOR_DARKRED)
user = null
rune_in_use = FALSE
//Ritual of Dimensional Rending: Calls forth the avatar of Nar'Sie upon the station.
/obj/effect/rune/narsie
cultist_name = "Tear Veil"
cultist_desc = "tears apart dimensional barriers, calling forth your god."
invocation = "TOK-LYR RQA-NAP G'OLT-ULOFT!!"
req_cultists = 9
icon = 'icons/effects/96x96.dmi'
icon_state = "rune_large"
pixel_x = -32 //So the big ol' 96x96 sprite shows up right
pixel_y = -32
mouse_opacity = MOUSE_OPACITY_ICON //we're huge and easy to click
scribe_delay = 45 SECONDS //how long the rune takes to create
scribe_damage = 10 //how much damage you take doing it
var/used = FALSE
/obj/effect/rune/narsie/Initialize(mapload)
. = ..()
cultist_name = "Summon [GET_CULT_DATA(entity_name, "your god")]"
cultist_desc = "tears apart dimensional barriers, calling forth [GET_CULT_DATA(entity_title3, "your god")]."
/obj/effect/rune/narsie/cult_conceal() //can't hide this, and you wouldn't want to
return
/obj/effect/rune/narsie/is_cleanable() //No, you can't just yeet a cleaning grenade to remove it.
return FALSE
/obj/effect/rune/narsie/invoke(list/invokers)
if(used)
return
var/mob/living/user = invokers[1]
if(!is_station_level(user.z))
message_admins("[key_name_admin(user)] tried to summon an eldritch horror off station")
log_game("Summon Nar'Sie rune failed - off station Z level")
return
if(SSticker.mode.cult_team.cult_status == NARSIE_HAS_RISEN)
for(var/M in invokers)
to_chat(M, SPAN_CULTLARGE("\"I am already here. There is no need to try to summon me now.\""))
log_game("Summon god rune failed - already summoned")
return
//BEGIN THE SUMMONING
SSticker.mode.cult_team.successful_summon()
used = TRUE
color = COLOR_RED
..()
for(var/mob/M in GLOB.player_list)
if(!isnewplayer(M)) // exclude people in the lobby
SEND_SOUND(M, sound('sound/effects/dimensional_rend.ogg'))
to_chat(M, SPAN_CULTITALIC("<b>The veil... [SPAN_BIG("is...")] [SPAN_REALLYBIG("TORN!!!--")]</b>"))
icon_state = "rune_large_distorted"
var/turf/T = get_turf(src)
sleep(40)
new /obj/singularity/narsie/large(T) //Causes Nar'Sie to spawn even if the rune has been removed
/obj/effect/rune/narsie/item_interaction(mob/living/user, obj/item/used, list/modifiers)
. = ..()
if((istype(used, /obj/item/melee/cultblade/dagger) && IS_CULTIST(user)))
log_game("Summon Narsie rune erased by [key_name(user)] with a cult dagger")
message_admins("[key_name_admin(user)] erased a Narsie rune with a cult dagger")
if(istype(used, /obj/item/nullrod)) //Begone foul magiks. You cannot hinder me.
log_game("Summon Narsie rune erased by [key_name(user)] using a null rod")
message_admins("[key_name_admin(user)] erased a Narsie rune with a null rod")