GLOBAL_LIST_EMPTY(sacrificed) //a mixed list of minds and mobs GLOBAL_LIST(rune_types) //Every rune that can be drawn by tomes GLOBAL_LIST_EMPTY(teleport_runes) GLOBAL_LIST_EMPTY(wall_runes) /* 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 an arcane tome 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 an arcane tome. */ /obj/effect/rune name = "rune" var/cultist_name = "basic rune" desc = "An odd collection of symbols drawn in what seems to be blood." 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. anchored = TRUE icon = 'icons/obj/rune.dmi' icon_state = "1" resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF layer = LOW_OBJ_LAYER color = RUNE_COLOR_RED var/invocation = "Aiy ele-mayo!" //This is said by cultists when the rune is invoked. var/req_cultists = 1 //The amount of cultists required around the rune to invoke it. If only 1, any cultist can invoke it. var/req_cultists_text //if we have a description override for required cultists to invoke var/rune_in_use = FALSE // Used for some runes, this is for when you want a rune to not be usable when in use. var/scribe_delay = 50 //how long the rune takes to create var/scribe_damage = 0.1 //how much damage you take doing it var/allow_excess_invokers = FALSE //if we allow excess invokers when being invoked var/invoke_damage = 0 //how much damage invokers take when invoking it var/construct_invoke = TRUE //if constructs can invoke it var/req_keyword = 0 //If the rune requires a keyword - go figure amirite var/keyword //The actual keyword for the rune /obj/effect/rune/Initialize(mapload, set_keyword) . = ..() if(set_keyword) keyword = set_keyword /obj/effect/rune/examine(mob/user) ..() if(iscultist(user) || user.stat == DEAD) //If they're a cultist or a ghost, tell them the effects to_chat(user, "Name: [cultist_name]") to_chat(user, "Effects: [capitalize(cultist_desc)]") to_chat(user, "Required Acolytes: [req_cultists_text ? "[req_cultists_text]":"[req_cultists]"]") if(req_keyword && keyword) to_chat(user, "Keyword: [keyword]") /obj/effect/rune/attackby(obj/I, mob/user, params) if(istype(I, /obj/item/tome) && iscultist(user)) to_chat(user, "You carefully erase the [lowertext(cultist_name)] rune.") qdel(src) else if(istype(I, /obj/item/nullrod)) user.say("BEGONE FOUL MAGIKS!!") to_chat(user, "You disrupt the magic of [src] with [I].") qdel(src) /obj/effect/rune/attack_hand(mob/living/user) if(!iscultist(user)) to_chat(user, "You aren't able to understand the words of [src].") return var/list/invokers = can_invoke(user) if(invokers.len >= req_cultists) invoke(invokers) else fail_invoke() /obj/effect/rune/attack_animal(mob/living/simple_animal/M) if(istype(M, /mob/living/simple_animal/shade) || istype(M, /mob/living/simple_animal/hostile/construct)) if(construct_invoke || !iscultist(M)) //if you're not a cult construct we want the normal fail message attack_hand(M) else to_chat(M, "You are unable to invoke the rune!") /obj/effect/rune/proc/talismanhide() //for talisman of revealing/hiding visible_message("[src] fades away.") invisibility = INVISIBILITY_OBSERVER alpha = 100 //To help ghosts distinguish hidden runes /obj/effect/rune/proc/talismanreveal() //for talisman of revealing/hiding invisibility = 0 visible_message("[src] suddenly appears!") alpha = initial(alpha) /* 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(var/mob/living/user=null) //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(user) chanters += user invokers += user if(req_cultists > 1 || allow_excess_invokers) for(var/mob/living/L in range(1, src)) if(iscultist(L)) if(L == user) continue if(ishuman(L)) var/mob/living/carbon/human/H = L if((H.has_disability(DISABILITY_MUTE)) || H.silent) continue if(L.stat) continue invokers += L if(invokers.len >= req_cultists) invokers -= user if(allow_excess_invokers) chanters += invokers else shuffle_inplace(invokers) for(var/i in 1 to req_cultists) var/L = pick_n_take(invokers) if(L) chanters += L return chanters /obj/effect/rune/proc/invoke(var/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. for(var/M in invokers) var/mob/living/L = M if(invocation) L.say(invocation, language = /datum/language/common, ignore_spam = TRUE) if(invoke_damage) L.apply_damage(invoke_damage, BRUTE) to_chat(L, "[src] saps your strength!") do_invoke_glow() /obj/effect/rune/proc/do_invoke_glow() set waitfor = FALSE var/oldtransform = transform animate(src, transform = matrix()*2, alpha = 0, time = 5) //fade out sleep(5) animate(src, transform = oldtransform, alpha = 255, time = 0) /obj/effect/rune/proc/fail_invoke() //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. visible_message("The markings pulse with a small flash of red light, then fall dark.") var/oldcolor = color color = rgb(255, 0, 0) animate(src, color = oldcolor, time = 5) addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 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 rune" 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/Initialize(mapload, set_keyword) . = ..() icon_state = "[rand(1,7)]" color = rgb(rand(0,255), rand(0,255), rand(0,255)) /obj/effect/rune/malformed/invoke(var/list/invokers) ..() qdel(src) /mob/proc/null_rod_check() //The null rod, if equipped, will protect the holder from the effects of most runes var/obj/item/nullrod/N = locate() in src if(N && !GLOB.ratvar_awakens) //If Nar-Sie or Ratvar are alive, null rods won't protect you return N return 0 /mob/proc/bible_check() //The bible, if held, might protect against certain things var/obj/item/storage/book/bible/B = locate() in src if(is_holding(B)) return B return 0 //Rite of Binding: A paper on top of the rune to a talisman. /obj/effect/rune/imbue cultist_name = "Create Talisman" cultist_desc = "transforms paper into powerful magic talismans." invocation = "H'drak v'loso, mir'kanas verbot!" icon_state = "3" color = RUNE_COLOR_TALISMAN /obj/effect/rune/imbue/invoke(var/list/invokers) var/mob/living/user = invokers[1] //the first invoker is always the user var/list/papers_on_rune = checkpapers() var/entered_talisman_name var/obj/item/paper/talisman/talisman_type var/list/possible_talismans = list() if(!papers_on_rune.len) to_chat(user, "There must be a blank paper on top of [src]!") fail_invoke() log_game("Talisman Creation rune failed - no blank papers on rune") return if(rune_in_use) to_chat(user, "[src] can only support one ritual at a time!") fail_invoke() log_game("Talisman Creation rune failed - already in use") return for(var/I in subtypesof(/obj/item/paper/talisman) - /obj/item/paper/talisman/malformed - /obj/item/paper/talisman/supply - /obj/item/paper/talisman/supply/weak - /obj/item/paper/talisman/summon_tome) var/obj/item/paper/talisman/J = I var/talisman_cult_name = initial(J.cultist_name) if(talisman_cult_name) possible_talismans[talisman_cult_name] = J //This is to allow the menu to let cultists select talismans by name entered_talisman_name = input(user, "Choose a talisman to imbue.", "Talisman Choices") as null|anything in possible_talismans talisman_type = possible_talismans[entered_talisman_name] if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated() || rune_in_use || !talisman_type) return papers_on_rune = checkpapers() if(!papers_on_rune.len) to_chat(user, "There must be a blank paper on top of [src]!") fail_invoke() log_game("Talisman Creation rune failed - no blank papers on rune") return var/obj/item/paper/paper_to_imbue = papers_on_rune[1] ..() visible_message("Dark power begins to channel into the paper!") rune_in_use = TRUE if(do_after(user, initial(talisman_type.creation_time), target = paper_to_imbue)) new talisman_type(get_turf(src)) visible_message("[src] glows with power, and bloody images form themselves on [paper_to_imbue].") qdel(paper_to_imbue) rune_in_use = FALSE /obj/effect/rune/imbue/proc/checkpapers() . = list() for(var/obj/item/paper/P in get_turf(src)) if(!P.info && !istype(P, /obj/item/paper/talisman)) . |= P /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 = "2" color = RUNE_COLOR_TELEPORT req_keyword = TRUE 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 return ..() /obj/effect/rune/teleport/invoke(var/list/invokers) var/mob/living/user = invokers[1] //the first invoker is always the user var/list/potential_runes = list() var/list/teleportnames = list() for(var/R in GLOB.teleport_runes) var/obj/effect/rune/teleport/T = R if(T != src && !is_away_level(T.z)) potential_runes[avoid_assoc_duplicate_keys(T.listkey, teleportnames)] = T if(!potential_runes.len) to_chat(user, "There are no valid runes to teleport to!") log_game("Teleport rune failed - no other teleport runes") fail_invoke() return var/turf/T = get_turf(src) if(is_away_level(T.z)) to_chat(user, "You are not in the right dimension!") log_game("Teleport rune failed - user in away mission") fail_invoke() return var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in 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(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated() || !actual_selected_rune) fail_invoke() return var/turf/target = get_turf(actual_selected_rune) if(is_blocked_turf(target, TRUE)) to_chat(user, "The target rune is blocked. Attempting to teleport to it would be massively unwise.") fail_invoke() return var/movedsomething = FALSE var/moveuserlater = FALSE for(var/atom/movable/A in T) if(A == user) moveuserlater = TRUE movedsomething = TRUE continue if(!A.anchored) movedsomething = TRUE A.forceMove(target) if(movedsomething) ..() visible_message("There is a sharp crack of inrushing air, and everything above the rune disappears!", null, "You hear a sharp crack.") to_chat(user, "You[moveuserlater ? "r vision blurs, and you suddenly appear somewhere else":" send everything above the rune away"].") if(moveuserlater) user.forceMove(target) target.visible_message("There is a boom of outrushing air as something appears above the rune!", null, "You hear a boom.") else fail_invoke() //Rite of Offering: Converts or sacrifices a target. /obj/effect/rune/convert cultist_name = "Offer" cultist_desc = "offers a noncultist above it to Nar-Sie, either converting them or sacrificing them." req_cultists_text = "2 for conversion, 3 for living sacrifices and sacrifice targets." invocation = "Mah'weyh pleggh at e'ntrath!" icon_state = "3" color = RUNE_COLOR_OFFER req_cultists = 1 allow_excess_invokers = TRUE rune_in_use = FALSE /obj/effect/rune/convert/do_invoke_glow() return /obj/effect/rune/convert/invoke(var/list/invokers) if(rune_in_use) return var/list/myriad_targets = list() var/turf/T = get_turf(src) for(var/mob/living/M in T) if(!iscultist(M)) myriad_targets |= M if(!myriad_targets.len) fail_invoke() log_game("Offer rune failed - no eligible targets") return rune_in_use = TRUE visible_message("[src] pulses blood red!") var/oldcolor = color color = RUNE_COLOR_DARKRED var/mob/living/L = pick(myriad_targets) var/is_clock = is_servant_of_ratvar(L) var/mob/living/F = invokers[1] var/datum/antagonist/cult/C = F.mind.has_antag_datum(/datum/antagonist/cult,TRUE) var/is_convertable = is_convertable_to_cult(L,C.cult_team) if(L.stat != DEAD && (is_clock || is_convertable)) invocation = "Mah'weyh pleggh at e'ntrath!" ..() if(is_clock) L.visible_message("[L]'s eyes glow a defiant yellow!", \ "\"Stop resisting. You will be mi-\"\n\ \"Give up and you will feel pain unlike anything you've ever felt!\"") L.Knockdown(80) else if(is_convertable) do_convert(L, invokers) else invocation = "Barhah hra zar'garis!" ..() do_sacrifice(L, invokers) animate(src, color = oldcolor, time = 5) addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 5) rune_in_use = FALSE /obj/effect/rune/convert/proc/do_convert(mob/living/convertee, list/invokers) if(invokers.len < 2) for(var/M in invokers) to_chat(M, "You need more invokers to convert [convertee]!") log_game("Offer rune failed - tried conversion with one invoker") return 0 if(convertee.null_rod_check()) for(var/M in invokers) to_chat(M, "Something is shielding [convertee]'s mind!") log_game("Offer rune failed - convertee had null rod") return 0 var/brutedamage = convertee.getBruteLoss() var/burndamage = convertee.getFireLoss() if(brutedamage || burndamage) convertee.adjustBruteLoss(-(brutedamage * 0.75)) convertee.adjustFireLoss(-(burndamage * 0.75)) convertee.visible_message("[convertee] writhes in pain \ [brutedamage || burndamage ? "even as [convertee.p_their()] wounds heal and close" : "as the markings below [convertee.p_them()] glow a bloody red"]!", \ "AAAAAAAAAAAAAA-") SSticker.mode.add_cultist(convertee.mind, 1) new /obj/item/tome(get_turf(src)) convertee.mind.special_role = "Cultist" to_chat(convertee, "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.") to_chat(convertee, "Assist your new compatriots in their dark dealings. Your goal is theirs, and theirs is yours. You serve the Geometer above all else. Bring it back.\ ") return 1 /obj/effect/rune/convert/proc/do_sacrifice(mob/living/sacrificial, list/invokers) var/mob/living/first_invoker = invokers[1] if(!first_invoker) return FALSE var/datum/antagonist/cult/C = first_invoker.mind.has_antag_datum(/datum/antagonist/cult,TRUE) if(!C) return var/big_sac = FALSE if((((ishuman(sacrificial) || iscyborg(sacrificial)) && sacrificial.stat != DEAD) || C.cult_team.is_sacrifice_target(sacrificial.mind)) && invokers.len < 3) for(var/M in invokers) to_chat(M, "[sacrificial] is too greatly linked to the world! You need three acolytes!") log_game("Offer rune failed - not enough acolytes and target is living or sac target") return FALSE if(sacrificial.mind) GLOB.sacrificed += sacrificial.mind for(var/datum/objective/sacrifice/sac_objective in C.cult_team.objectives) if(sac_objective.target == sacrificial.mind) sac_objective.sacced = TRUE sac_objective.update_explanation_text() big_sac = TRUE else GLOB.sacrificed += sacrificial new /obj/effect/temp_visual/cult/sac(get_turf(src)) for(var/M in invokers) if(big_sac) to_chat(M, "\"Yes! This is the one I desire! You have done well.\"") else if(ishuman(sacrificial) || iscyborg(sacrificial)) to_chat(M, "\"I accept this sacrifice.\"") else to_chat(M, "\"I accept this meager sacrifice.\"") var/obj/item/device/soulstone/stone = new /obj/item/device/soulstone(get_turf(src)) if(sacrificial.mind) stone.invisibility = INVISIBILITY_MAXIMUM //so it's not picked up during transfer_soul() stone.transfer_soul("FORCE", sacrificial, usr) stone.invisibility = 0 if(sacrificial) if(iscyborg(sacrificial)) playsound(sacrificial, 'sound/magic/disable_tech.ogg', 100, 1) sacrificial.dust() //To prevent the MMI from remaining else playsound(sacrificial, 'sound/magic/disintegrate.ogg', 100, 1) sacrificial.gib() return TRUE //Ritual of Dimensional Rending: Calls forth the avatar of Nar-Sie upon the station. /obj/effect/rune/narsie cultist_name = "Summon Nar-Sie" cultist_desc = "tears apart dimensional barriers, calling forth the Geometer. Requires 9 invokers." invocation = "TOK-LYR RQA-NAP G'OLT-ULOFT!!" req_cultists = 9 icon = 'icons/effects/96x96.dmi' color = RUNE_COLOR_DARKRED icon_state = "rune_large" pixel_x = -32 //So the big ol' 96x96 sprite shows up right pixel_y = -32 scribe_delay = 500 //how long the rune takes to create scribe_damage = 40.1 //how much damage you take doing it var/used = FALSE /obj/effect/rune/narsie/Initialize(mapload, set_keyword) . = ..() GLOB.poi_list |= src /obj/effect/rune/narsie/Destroy() GLOB.poi_list -= src . = ..() /obj/effect/rune/narsie/talismanhide() //can't hide this, and you wouldn't want to return /obj/effect/rune/narsie/invoke(var/list/invokers) if(used) return if(!is_station_level(z)) return if(locate(/obj/singularity/narsie) in GLOB.poi_list) for(var/M in invokers) to_chat(M, "[src] fizzles uselessly, Nar-Sie is already on this plane!") playsound(src, 'sound/items/welder.ogg', 50, 1) log_game("Summon Nar-Sie rune at [COORD(src)] failed - already summoned") return //BEGIN THE SUMMONING used = TRUE ..() sound_to_playing_players('sound/effects/dimensional_rend.ogg') var/turf/T = get_turf(src) sleep(40) if(locate(/obj/singularity/narsie) in GLOB.poi_list) T.visible_message("A faint fizzle could be heard echoing along with a soft chorus of screams and chanting...") playsound(T, 'sound/items/welder.ogg', 25, 1) return if(src) color = RUNE_COLOR_RED new /obj/singularity/narsie/large/cult(T) //Causes Nar-Sie to spawn even if the rune has been removed /obj/effect/rune/narsie/attackby(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal. if((istype(I, /obj/item/tome) && iscultist(user))) user.visible_message("[user.name] begins erasing [src]...", "You begin erasing [src]...") if(do_after(user, 50, target = src)) //Prevents accidental erasures. log_game("Summon Narsie rune erased by [user.mind.key] (ckey) with a tome") message_admins("[key_name_admin(user)] erased a Narsie rune with a tome") ..() else if(istype(I, /obj/item/nullrod)) //Begone foul magiks. You cannot hinder me. log_game("Summon Narsie rune erased by [user.mind.key] (ckey) using a null rod") message_admins("[key_name_admin(user)] erased a Narsie rune with a null rod") ..() //Rite of Resurrection: Requires a dead or inactive cultist. When reviving the dead, you can only perform one revival for every sacrifice your cult has carried out. /obj/effect/rune/raise_dead cultist_name = "Revive Cultist" cultist_desc = "requires a dead, mindless, or inactive cultist placed upon the rune. Provided there have been sufficient sacrifices, they will be given a new life." invocation = "Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!" //Depends on the name of the user - see below icon_state = "1" color = RUNE_COLOR_MEDIUMRED var/static/revives_used = 0 /obj/effect/rune/raise_dead/examine(mob/user) ..() if(iscultist(user) || user.stat == DEAD) var/revive_number = LAZYLEN(GLOB.sacrificed) - revives_used to_chat(user, "Revives Remaining: [revive_number]") /obj/effect/rune/raise_dead/invoke(var/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 for(var/mob/living/M in T.contents) if(iscultist(M) && (M.stat == DEAD || !M.client || M.client.is_afk())) potential_revive_mobs |= M if(!potential_revive_mobs.len) to_chat(user, "There are no dead cultists on the rune!") log_game("Raise Dead rune failed - no cultists to revive") fail_invoke() rune_in_use = FALSE return if(potential_revive_mobs.len > 1) mob_to_revive = input(user, "Choose a cultist to revive.", "Cultist to Revive") as null|anything in potential_revive_mobs else mob_to_revive = potential_revive_mobs[1] if(QDELETED(src) || !validness_checks(mob_to_revive, user)) rune_in_use = FALSE return if(user.name == "Herbert West") invocation = "To life, to life, I bring them!" else invocation = initial(invocation) ..() if(mob_to_revive.stat == DEAD) if(LAZYLEN(GLOB.sacrificed) <= revives_used) to_chat(user, "Your cult must carry out another sacrifice before it can revive a cultist!") fail_invoke() rune_in_use = FALSE return revives_used++ mob_to_revive.revive(1, 1) //This does remove disabilities and such, but the rune might actually see some use because of it! mob_to_revive.grab_ghost() else if(!mob_to_revive.client || mob_to_revive.client.is_afk()) set waitfor = FALSE var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [mob_to_revive.name], an inactive blood cultist?", ROLE_CULTIST, null, ROLE_CULTIST, 50, mob_to_revive) var/mob/dead/observer/theghost = null if(candidates.len) theghost = pick(candidates) to_chat(mob_to_revive.mind, "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(theghost)] has taken control of ([key_name_admin(mob_to_revive)]) to replace an AFK player.") mob_to_revive.ghostize(0) mob_to_revive.key = theghost.key to_chat(mob_to_revive, "\"PASNAR SAVRAE YAM'TOTH. Arise.\"") mob_to_revive.visible_message("[mob_to_revive] draws in a huge breath, red light shining from [mob_to_revive.p_their()] eyes.", \ "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) var/turf/T = get_turf(src) if(QDELETED(user)) return FALSE if(!Adjacent(user) || user.incapacitated()) return FALSE if(QDELETED(target_mob)) fail_invoke() return FALSE if(!(target_mob in T.contents)) to_chat(user, "The cultist to revive has been moved!") fail_invoke() log_game("Raise Dead rune failed - revival target moved") return FALSE var/mob/dead/observer/ghost = target_mob.get_ghost(TRUE) if(!ghost && (!target_mob.mind || !target_mob.mind.active)) to_chat(user, "The corpse to revive has no spirit!") fail_invoke() log_game("Raise Dead rune failed - revival target has no ghost") return FALSE if(!GLOB.sacrificed.len || GLOB.sacrificed.len <= revives_used) to_chat(user, "You have sacrificed too few people to revive a cultist!") fail_invoke() log_game("Raise Dead rune failed - too few sacrificed") return FALSE return TRUE /obj/effect/rune/raise_dead/fail_invoke() ..() for(var/mob/living/M in range(1,src)) if(iscultist(M) && M.stat == DEAD) M.visible_message("[M] twitches.") //Rite of Disruption: Emits an EMP blast. /obj/effect/rune/emp cultist_name = "Electromagnetic Disruption" cultist_desc = "emits a large electromagnetic pulse, increasing in size for each cultist invoking it, hindering electronics and disabling silicons." invocation = "Ta'gh fara'qha fel d'amar det!" icon_state = "5" allow_excess_invokers = TRUE color = RUNE_COLOR_EMP /obj/effect/rune/emp/invoke(var/list/invokers) var/turf/E = get_turf(src) ..() visible_message("[src] glows blue for a moment before vanishing.") switch(invokers.len) if(1 to 2) playsound(E, 'sound/items/welder2.ogg', 25, 1) for(var/M in invokers) to_chat(M, "You feel a minute vibration pass through you...") if(3 to 6) playsound(E, 'sound/magic/disable_tech.ogg', 50, 1) for(var/M in invokers) to_chat(M, "Your hair stands on end as a shockwave emanates from the rune!") if(7 to INFINITY) playsound(E, 'sound/magic/disable_tech.ogg', 100, 1) for(var/M in invokers) var/mob/living/L = M to_chat(L, "You chant in unison and a colossal burst of energy knocks you backward!") L.Knockdown(40) qdel(src) //delete before pulsing because it's a delay reee empulse(E, 9*invokers.len, 12*invokers.len) // Scales now, from a single room to most of the station depending on # of chanters //Rite of Spirit Sight: Separates one's spirit from their body. They will take damage while it is active. /obj/effect/rune/spirit cultist_name = "Spirit Sight" cultist_desc = "severs the link between one's spirit and body. This effect is taxing and one's physical body will take damage while this is active." invocation = "Fwe'sh mah erl nyag r'ya!" icon_state = "7" color = RUNE_COLOR_DARKRED rune_in_use = FALSE //One at a time, please! construct_invoke = FALSE var/mob/living/affecting = null /obj/effect/rune/spirit/Destroy() affecting = null return ..() /obj/effect/rune/spirit/examine(mob/user) ..() if(affecting) to_chat(user, "A translucent field encases [affecting] above the rune!") /obj/effect/rune/spirit/can_invoke(mob/living/user) if(rune_in_use) to_chat(user, "[src] cannot support more than one body!") log_game("Spirit Sight rune failed - more than one user") return list() var/turf/T = get_turf(src) if(!(user in T)) to_chat(user, "You must be standing on top of [src]!") log_game("Spirit Sight rune failed - user not standing on rune") return list() return ..() /obj/effect/rune/spirit/invoke(var/list/invokers) var/mob/living/user = invokers[1] ..() var/turf/T = get_turf(src) rune_in_use = TRUE affecting = user affecting.add_atom_colour(RUNE_COLOR_DARKRED, ADMIN_COLOUR_PRIORITY) affecting.visible_message("[affecting] freezes statue-still, glowing an unearthly red.", \ "You see what lies beyond. All is revealed. While this is a wondrous experience, your physical form will waste away in this state. Hurry...") affecting.ghostize(1) while(!QDELETED(affecting)) affecting.apply_damage(0.1, BRUTE) if(!(affecting in T)) user.visible_message("A spectral tendril wraps around [affecting] and pulls [affecting.p_them()] back to the rune!") Beam(affecting, icon_state="drainbeam", time=2) affecting.forceMove(get_turf(src)) //NO ESCAPE :^) if(affecting.key) affecting.visible_message("[affecting] slowly relaxes, the glow around [affecting.p_them()] dimming.", \ "You are re-united with your physical form. [src] releases its hold over you.") affecting.remove_atom_colour(ADMIN_COLOUR_PRIORITY, RUNE_COLOR_DARKRED) affecting.Knockdown(60) break if(affecting.stat == UNCONSCIOUS) if(prob(1)) var/mob/dead/observer/G = affecting.get_ghost() to_chat(G, "You feel the link between you and your body weakening... you must hurry!") else if(affecting.stat == DEAD) affecting.remove_atom_colour(ADMIN_COLOUR_PRIORITY, RUNE_COLOR_DARKRED) var/mob/dead/observer/G = affecting.get_ghost() to_chat(G, "You suddenly feel your physical form pass on. [src]'s exertion has killed you!") break sleep(1) affecting = null rune_in_use = FALSE //Rite of the Corporeal Shield: When invoked, becomes solid and cannot be passed. Invoke again to undo. /obj/effect/rune/wall cultist_name = "Form Barrier" cultist_desc = "when invoked, makes a temporary invisible wall to block passage. Can be invoked again to reverse this." invocation = "Khari'd! Eske'te tannin!" icon_state = "1" color = RUNE_COLOR_MEDIUMRED CanAtmosPass = ATMOS_PASS_DENSITY var/density_timer var/recharging = FALSE /obj/effect/rune/wall/Initialize(mapload, set_keyword) . = ..() GLOB.wall_runes += src /obj/effect/rune/wall/examine(mob/user) ..() if(density) to_chat(user, "There is a barely perceptible shimmering of the air above [src].") /obj/effect/rune/wall/Destroy() density = FALSE GLOB.wall_runes -= src air_update_turf(1) return ..() /obj/effect/rune/wall/BlockSuperconductivity() return density /obj/effect/rune/wall/invoke(var/list/invokers) if(recharging) return var/mob/living/user = invokers[1] ..() density = !density update_state() if(density) spread_density() var/carbon_user = iscarbon(user) user.visible_message("[user] [carbon_user ? "places [user.p_their()] hands on":"stares intently at"] [src], and [density ? "the air above it begins to shimmer" : "the shimmer above it fades"].", \ "You channel [carbon_user ? "your life ":""]energy into [src], [density ? "temporarily preventing" : "allowing"] passage above it.") if(carbon_user) var/mob/living/carbon/C = user C.apply_damage(2, BRUTE, pick("l_arm", "r_arm")) /obj/effect/rune/wall/proc/spread_density() for(var/R in GLOB.wall_runes) var/obj/effect/rune/wall/W = R if(W.z == z && get_dist(src, W) <= 2 && !W.density && !W.recharging) W.density = TRUE W.update_state() W.spread_density() density_timer = addtimer(CALLBACK(src, .proc/lose_density), 900, TIMER_STOPPABLE) /obj/effect/rune/wall/proc/lose_density() if(density) recharging = TRUE density = FALSE update_state() var/oldcolor = color add_atom_colour("#696969", FIXED_COLOUR_PRIORITY) animate(src, color = oldcolor, time = 50, easing = EASE_IN) addtimer(CALLBACK(src, .proc/recharge), 50) /obj/effect/rune/wall/proc/recharge() recharging = FALSE add_atom_colour(RUNE_COLOR_MEDIUMRED, FIXED_COLOUR_PRIORITY) /obj/effect/rune/wall/proc/update_state() deltimer(density_timer) air_update_turf(1) if(density) var/mutable_appearance/shimmer = mutable_appearance('icons/effects/effects.dmi', "barriershimmer", ABOVE_MOB_LAYER) shimmer.appearance_flags |= RESET_COLOR shimmer.alpha = 60 shimmer.color = "#701414" add_overlay(shimmer) add_atom_colour(RUNE_COLOR_RED, FIXED_COLOUR_PRIORITY) else cut_overlays() add_atom_colour(RUNE_COLOR_MEDIUMRED, FIXED_COLOUR_PRIORITY) //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. Requires 2 invokers." invocation = "N'ath reth sh'yro eth d'rekkathnor!" req_cultists = 2 invoke_damage = 10 icon_state = "5" color = RUNE_COLOR_SUMMON /obj/effect/rune/summon/invoke(var/list/invokers) var/mob/living/user = invokers[1] var/list/cultists = list() for(var/datum/mind/M in SSticker.mode.cult) if(!(M.current in invokers) && M.current && M.current.stat != DEAD) cultists |= M.current var/mob/living/cultist_to_summon = input(user, "Who do you wish to call to [src]?", "Followers of the Geometer") as null|anything in cultists if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated()) return if(!cultist_to_summon) to_chat(user, "You require a summoning target!") fail_invoke() log_game("Summon Cultist rune failed - no target") return if(cultist_to_summon.stat == DEAD) to_chat(user, "[cultist_to_summon] has died!") fail_invoke() log_game("Summon Cultist rune failed - target died") return if(!iscultist(cultist_to_summon)) to_chat(user, "[cultist_to_summon] is not a follower of the Geometer!") fail_invoke() log_game("Summon Cultist rune failed - target was deconverted") return if(is_away_level(cultist_to_summon.z)) to_chat(user, "[cultist_to_summon] is not in our dimension!") fail_invoke() log_game("Summon Cultist rune failed - target in away mission") return cultist_to_summon.visible_message("[cultist_to_summon] suddenly disappears in a flash of red light!", \ "Overwhelming vertigo consumes you as you are hurled through the air!") ..() visible_message("A foggy shape materializes atop [src] and solidifes into [cultist_to_summon]!") cultist_to_summon.forceMove(get_turf(src)) qdel(src) //Rite of Boiling Blood: Deals extremely high amounts of damage to non-cultists nearby /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 3 invokers." invocation = "Dedo ol'btoh!" icon_state = "4" color = RUNE_COLOR_MEDIUMRED light_color = LIGHT_COLOR_LAVA req_cultists = 3 invoke_damage = 10 construct_invoke = FALSE var/tick_damage = 25 rune_in_use = FALSE /obj/effect/rune/blood_boil/do_invoke_glow() return /obj/effect/rune/blood_boil/invoke(var/list/invokers) if(rune_in_use) return ..() rune_in_use = TRUE var/turf/T = get_turf(src) visible_message("[src] turns a bright, glowing orange!") color = "#FC9B54" set_light(6, 1, color) for(var/mob/living/L in viewers(T)) if(!iscultist(L) && L.blood_volume) var/obj/item/nullrod/N = L.null_rod_check() if(N) to_chat(L, "\The [N] suddenly burns hotly before returning to normal!") continue to_chat(L, "Your blood boils in your veins!") if(is_servant_of_ratvar(L)) to_chat(L, "You feel an unholy darkness dimming the Justiciar's light!") animate(src, color = "#FCB56D", time = 4) sleep(4) if(QDELETED(src)) return do_area_burn(T, 0.5) animate(src, color = "#FFDF80", time = 5) sleep(5) if(QDELETED(src)) return do_area_burn(T, 1) animate(src, color = "#FFFDF4", time = 6) sleep(6) if(QDELETED(src)) return do_area_burn(T, 1.5) new /obj/effect/hotspot(T) qdel(src) /obj/effect/rune/blood_boil/proc/do_area_burn(turf/T, multiplier) set_light(6, 1, color) for(var/mob/living/L in viewers(T)) if(!iscultist(L) && L.blood_volume) var/obj/item/nullrod/N = L.null_rod_check() if(N) continue L.take_overall_damage(tick_damage*multiplier, tick_damage*multiplier) if(is_servant_of_ratvar(L)) L.adjustStaminaLoss(tick_damage*0.5) //Rite of Spectral Manifestation: Summons a ghost on top of the rune as a cultist human with no items. User must stand on the rune at all times, and takes damage for each summoned ghost. /obj/effect/rune/manifest cultist_name = "Manifest Spirit" cultist_desc = "manifests a spirit as a servant of the Geometer. 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 = "6" invoke_damage = 10 construct_invoke = FALSE color = RUNE_COLOR_MEDIUMRED var/ghost_limit = 5 var/ghosts = 0 /obj/effect/rune/manifest/Initialize() . = ..() notify_ghosts("Manifest rune created in [get_area(src)].", 'sound/effects/ghost2.ogg', source = src) /obj/effect/rune/manifest/can_invoke(mob/living/user) if(!(user in get_turf(src))) to_chat(user, "You must be standing on [src]!") fail_invoke() log_game("Manifest rune failed - user not standing on rune") return list() if(user.has_status_effect(STATUS_EFFECT_SUMMONEDGHOST)) to_chat(user, "Ghosts can't summon more ghosts!") fail_invoke() log_game("Manifest rune failed - user is a ghost") return list() if(ghosts >= ghost_limit) to_chat(user, "You are sustaining too many ghosts to summon more!") fail_invoke() log_game("Manifest rune failed - too many summoned ghosts") return list() var/list/ghosts_on_rune = list() for(var/mob/dead/observer/O in get_turf(src)) if(O.client && !jobban_isbanned(O, ROLE_CULTIST)) ghosts_on_rune += O if(!ghosts_on_rune.len) to_chat(user, "There are no spirits near [src]!") fail_invoke() log_game("Manifest rune failed - no nearby ghosts") return list() return ..() /obj/effect/rune/manifest/invoke(var/list/invokers) var/mob/living/user = invokers[1] var/list/ghosts_on_rune = list() for(var/mob/dead/observer/O in get_turf(src)) if(O.client && !jobban_isbanned(O, ROLE_CULTIST)) ghosts_on_rune += O var/mob/dead/observer/ghost_to_spawn = pick(ghosts_on_rune) var/mob/living/carbon/human/cult_ghost/new_human = new(get_turf(src)) new_human.real_name = ghost_to_spawn.real_name new_human.alpha = 150 //Makes them translucent new_human.equipOutfit(/datum/outfit/ghost_cultist) //give them armor new_human.apply_status_effect(STATUS_EFFECT_SUMMONEDGHOST) //ghosts can't summon more ghosts ..() ghosts++ playsound(src, 'sound/magic/exit_blood.ogg', 50, 1) visible_message("A cloud of red mist forms above [src], and from within steps... a [new_human.gender == FEMALE ? "wo":""]man.") to_chat(user, "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/turf/T = get_turf(src) var/obj/structure/emergency_shield/invoker/N = new(T) new_human.key = ghost_to_spawn.key SSticker.mode.add_cultist(new_human.mind, 0) to_chat(new_human, "You are a servant of the Geometer. You have been made semi-corporeal by the cult of Nar-Sie, and you are to serve them at all costs.") while(!QDELETED(src) && !QDELETED(user) && !QDELETED(new_human) && (user in T)) if(user.stat || new_human.InCritical()) break user.apply_damage(0.1, BRUTE) sleep(1) qdel(N) ghosts-- if(new_human) new_human.visible_message("[new_human] suddenly dissolves into bones and ashes.", \ "Your link to the world fades. Your form breaks apart.") for(var/obj/I in new_human) new_human.dropItemToGround(I, TRUE) new_human.dust() /mob/living/carbon/human/cult_ghost/spill_organs(no_brain, no_organs, no_bodyparts) //cult ghosts never drop a brain no_brain = TRUE . = ..() /mob/living/carbon/human/cult_ghost/getorganszone(zone, subzones = 0) . = ..() for(var/obj/item/organ/brain/B in .) //they're not that smart, really . -= B