diff --git a/code/__defines/gamemode.dm b/code/__defines/gamemode.dm index 6d1755f27a8..491d8fd5d56 100644 --- a/code/__defines/gamemode.dm +++ b/code/__defines/gamemode.dm @@ -166,11 +166,35 @@ var/global/list/be_special_flags = list( /* Changeling Defines -*/ +*/ #define CHANGELING_POWER_INHERENT "Inherent" #define CHANGELING_POWER_ARMOR "Armor" #define CHANGELING_POWER_STINGS "Stings" #define CHANGELING_POWER_SHRIEKS "Shrieks" #define CHANGELING_POWER_HEALTH "Health" #define CHANGELING_POWER_ENHANCEMENTS "Enhancements" -#define CHANGELING_POWER_WEAPONS "Weapons" \ No newline at end of file +#define CHANGELING_POWER_WEAPONS "Weapons" + +// English cult words. Each one should represent a concept. +#define CULT_WORD_BLOOD "blood" +#define CULT_WORD_DESTROY "destroy" +#define CULT_WORD_HELL "hell" +#define CULT_WORD_HIDE "hide" +#define CULT_WORD_JOIN "join" +#define CULT_WORD_OTHER "other" +#define CULT_WORD_SELF "self" +#define CULT_WORD_SEE "see" +#define CULT_WORD_TECHNOLOGY "technology" +#define CULT_WORD_TRAVEL "travel" + +// Culty cult words. These are gibberish and can basically be whatever. +#define CULT_WORD_BALAQ "balaq" +#define CULT_WORD_CERTUM "certum" +#define CULT_WORD_EGO "ego" +#define CULT_WORD_GEERI "geeri" +#define CULT_WORD_IRE "ire" +#define CULT_WORD_KARAZET "karazet" +#define CULT_WORD_JATKAA "jatkaa" +#define CULT_WORD_MGAR "mgar" +#define CULT_WORD_NAHLIZET "nahlizet" +#define CULT_WORD_VERI "veri" diff --git a/code/__defines/is_helpers.dm b/code/__defines/is_helpers.dm index 3bfc444c9bd..96eaee3a30c 100644 --- a/code/__defines/is_helpers.dm +++ b/code/__defines/is_helpers.dm @@ -51,6 +51,8 @@ #define isbot(A) istype(A, /mob/living/bot) +#define isconstruct(A) istype(A, /mob/living/simple_mob/construct) + #define ismecha(A) istype(A, /obj/mecha) #define isvehicle(A) istype(A, /obj/vehicle) diff --git a/code/game/gamemodes/cult/arcane_tome.dm b/code/game/gamemodes/cult/arcane_tome.dm new file mode 100644 index 00000000000..431a6f216fa --- /dev/null +++ b/code/game/gamemodes/cult/arcane_tome.dm @@ -0,0 +1,178 @@ +#define TAB_ARCHIVES 1 +#define TAB_RUNES 2 + +/// Arcane tomes are the quintessential cultist "tool" and are used to draw runes, smack things with, and other such things. +/// The interface they open (arcane_tome.tmpl) also contains a lot of in-game documentation about how the antagonist works. +/obj/item/arcane_tome + name = "arcane tome" + desc = "An old, dusty tome with frayed edges and a sinister-looking cover." + icon = 'icons/obj/weapons.dmi' + item_icons = list( + icon_l_hand = 'icons/mob/items/lefthand_books.dmi', + icon_r_hand = 'icons/mob/items/righthand_books.dmi', + ) + icon_state = "tome" + item_state = "tome" + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_SMALL + drop_sound = 'sound/items/drop/book.ogg' + pickup_sound = 'sound/items/pickup/book.ogg' + + /// Whether or not a rune is already being written with this tome. + var/scribing = FALSE + /// How long it takes to draw a rune using this tome. Non-positive values are instant. + var/scribe_speed = 5 SECONDS + /// This tome's selected UI tab. + var/tab = TAB_ARCHIVES + /// The selected archive entry in the UI. + var/archive = 0 + /// If TRUE, the rune list in the tome's UI will be displayed in a more concise way. + var/compact_mode = FALSE + +/obj/item/arcane_tome/get_examine_desc() + if (iscultist(usr) || isobserver(usr)) + return "The scriptures of Nar-Sie, The One Who Sees, The Geometer of Blood. Contains the details of every ritual its followers could think of." + else + return desc + +/obj/item/arcane_tome/attack_self(mob/user) + if (!iscultist(user)) + to_chat(user, "The book is full of illegible scribbles and crudely-drawn shapes. Is this a joke...?") + return + ui_interact(user) + +/obj/item/arcane_tome/ui_interact(mob/user, ui_key, datum/nanoui/ui, force_open, master_ui, datum/topic_state/state) + if (!iscultist(user)) + return + var/list/data = build_ui_data() + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open = force_open) + if (!ui) + archive = 0 + data["archive"] = archive + ui = new(user, src, ui_key, "arcane_tome.tmpl", "Arcane Tome", 500, 600) + ui.set_initial_data(data) + ui.open() + playsound(user, 'sound/bureaucracy/bookopen.ogg', 25, TRUE) + +/obj/item/arcane_tome/proc/build_ui_data() + var/dat[0] + var/runes[0] + for (var/V in subtypesof(/obj/effect/rune)) + var/obj/effect/rune/NR = V + if (!initial(NR.can_write)) + continue + var/obj/item/paper/talisman/T = initial(NR.talisman_path) + var/req_invokers = initial(NR.required_invokers) + var/invokers_text = initial(NR.invokers_text) + runes[++runes.len] = list( + "name" = initial(NR.rune_name), + "invoker_data" = invokers_text != null ? invokers_text : (req_invokers > 1 ? req_invokers : null), + "talisman" = T ? initial(T.tome_desc) : null, + "shorthand" = initial(NR.rune_shorthand) ? initial(NR.rune_shorthand) : initial(NR.rune_desc), + "typepath" = NR + ) + dat["runes"] = runes + dat["tab"] = tab + dat["archive"] = archive + dat["compact_mode"] = compact_mode + return dat + +/obj/item/arcane_tome/Topic(href, href_list, datum/topic_state/state) + if (..() || !iscultist(usr)) + return TRUE + if (href_list["switch_tab"]) + tab = text2num(href_list["switch_tab"]) + playsound(src, "pageturn", 25, TRUE) + if (href_list["switch_archive"]) + archive = text2num(href_list["switch_archive"]) + playsound(src, "pageturn", 25, TRUE) + if (href_list["compact_mode"]) + compact_mode = !compact_mode + if (href_list["write_rune"]) + var/obj/effect/rune/R = text2path(href_list["write_rune"]) + if (ispath(R, /obj/effect/rune)) + scribe_rune(usr, R) + SSnanoui.update_uis(src) + +/obj/item/arcane_tome/attack(mob/living/M, mob/living/user, target_zone, attack_modifier) + // This is basically a reimplementation of weapon logic for cultists only + // It is far from ideal, but it's what we got. Having it always do damage but only letting cultists attack with it + // would mean that it could be used to smash lights and windows etc, and we only want it to work on living things + if (iscultist(user) && !iscultist(M) && user.a_intent == I_HURT) + user.break_cloak() + user.setClickCooldown(user.get_attack_speed(src)) + user.do_attack_animation(M) + var/hit_zone = M.resolve_item_attack(src, user, target_zone) + if (hit_zone) + M.interact_message(user, + SPAN_DANGER("\The [user] bathes \the [M] in red light from \the [src]!"), + SPAN_DANGER("\The [user] bathes you in burning red light from \the [src]!"), + SPAN_DANGER("You burn \the [M] with \the [src]!") + ) + playsound(src, 'sound/weapons/sear.ogg', 50, TRUE, -1) + M.apply_damage(rand(5, 20), BURN, hit_zone, used_weapon = "internal burns") + return + . = ..() + +/// Causes `user` to attempt to create a rune of type `rune_type`, using their blood (or equivalent) as the medium. +/// Inflicts a small amount of damage to the hands and creates blood decals in the process that remain if interrupted. +/obj/item/arcane_tome/proc/scribe_rune(mob/living/user, obj/effect/rune_type) + if (locate(/obj/effect/rune) in get_turf(user)) + to_chat(user, SPAN_WARNING("You can only fit one rune on any given space.")) + return + if (scribing) + to_chat(user, SPAN_WARNING("You can only scribe one rune at a time.")) + return + var/datum/gender/G = gender_datums[user.get_visible_gender()] + var/blood_name = "blood" + var/synth = user.isSynthetic() + if (ishuman(user)) + var/mob/living/carbon/human/H = user + blood_name = H.species?.get_blood_name() + user.apply_damage(1, BRUTE, pick(BP_L_HAND, BP_R_HAND), sharp = TRUE, edge = TRUE, used_weapon = "long, precise cut") + user.visible_message( + SPAN_WARNING("\The [user] [!synth ? "slices open [G.his] skin" : "tears open [G.his] circulation"] and begins painting on symbols on the floor with [G.his] own [blood_name]!"), + SPAN_NOTICE("You [!synth ? "slice open your skin" : "tear open your circulation"] and begin drawing a rune on the floor whilst invoking the ritual that binds your life essence with the dark arcane energies flowing through the surrounding world."), + SPAN_WARNING("You hear droplets softly spattering onto the ground."), + range = 3 + ) + scribing = TRUE + if (ishuman(user) && !max(0, scribe_speed)) // Only drip blood if it actually takes time to write the rune + var/mob/living/carbon/human/H = user + for (var/i in 1 to 4) + spawn (max(0, scribe_speed - 1 SECOND) / i) + H.drip(1) + if (!do_after(user, max(0, scribe_speed))) + to_chat(user, SPAN_WARNING("The ritual is interrupted. Your [blood_name] splatters onto the ground.")) + scribing = FALSE + return + scribing = FALSE + if (locate(/obj/effect/rune) in get_turf(user)) + to_chat(user, SPAN_WARNING("You can only fit one rune on any given space.")) + return + for (var/obj/effect/decal/cleanable/blood/B in get_turf(user)) + qdel(B) + user.visible_message( + SPAN_WARNING("\The [user] paints arcane markings with [G.his] own [blood_name]!"), + SPAN_NOTICE("You finish drawing the arcane markings of the Geometer."), + range = 3 + ) + var/obj/effect/rune/NR = new rune_type (get_turf(user)) + NR.after_scribe(user) + +/// Debug tome that automatically converts on pickup. Should never appear regularly. +/obj/item/arcane_tome/debug + name = "debug tome" + scribe_speed = 1 SECOND // Not quite instant, but close to it + +/obj/item/arcane_tome/debug/get_examine_desc() + return SPAN_DANGER("This should never appear in normal play - report this on the issue tracker if you see it in a normal round!") + +/obj/item/arcane_tome/debug/pickup(mob/living/user) + . = ..() + if (user.mind) + cult.add_antagonist(user.mind) + +#undef TAB_ARCHIVES +#undef TAB_RUNES diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index d56bf19af5f..8f6bf7a48a4 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -1,6 +1,7 @@ /obj/item/melee/cultblade name = "cult blade" - desc = "An arcane weapon wielded by the followers of an unknown god." + desc = "A wicked sword made of serrated black metal, with a very slightly curving blade evoking a khopesh. The grip is studded with dull spikes." + description_antag = "Through some anomalous means, this weapon refuses to be wielded by any other than the followers of Nar-Sie. Attempting to wield it without being a cultist yourself is like to result in disaster." icon_state = "cultblade" origin_tech = list(TECH_COMBAT = 1, TECH_ARCANE = 1) w_class = ITEMSIZE_LARGE @@ -10,48 +11,50 @@ drop_sound = 'sound/items/drop/sword.ogg' pickup_sound = 'sound/items/pickup/sword.ogg' attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - edge = 1 - sharp = 1 + edge = TRUE + sharp = TRUE /obj/item/melee/cultblade/cultify() return -/obj/item/melee/cultblade/attack(mob/living/M, mob/living/user, var/target_zone) - if(iscultist(user) && !istype(user, /mob/living/simple_mob/construct)) +/obj/item/melee/cultblade/attack(mob/living/M, mob/living/user, target_zone) + if (iscultist(user) && !isconstruct(user)) return ..() - var/zone = (user.hand ? "l_arm":"r_arm") - if(ishuman(user)) + var/zone = (user.hand ? BP_L_ARM : BP_R_ARM) + if (ishuman(user)) var/mob/living/carbon/human/H = user var/obj/item/organ/external/affecting = H.get_organ(zone) - to_chat(user, "An inexplicable force rips through your [affecting.name], tearing the sword from your grasp!") - //random amount of damage between half of the blade's force and the full force of the blade. - user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp=1, edge=1) + to_chat(user, SPAN_DANGER("An inexplicable force rips through your [affecting.name], tearing \the [src] from your grip!")) + user.apply_damage(rand(force / 2, force), damtype, zone, 0, sharp = sharp, edge = edge) user.Weaken(5) - else if(!istype(user, /mob/living/simple_mob/construct)) - to_chat(user, "An inexplicable force rips through you, tearing the sword from your grasp!") + else if (isconstruct(user)) + to_chat(user, SPAN_WARNING("The blade hisses, forcing itself from your manipulators. \The [src] will only allow mortals to wield it against foes, not kin.")) else - to_chat(user, "The blade hisses, forcing itself from your manipulators. \The [src] will only allow mortals to wield it against foes, not kin.") + to_chat(user, SPAN_DANGER("An inexplicable force rips through you, tearing the sword from your grip!")) - user.drop_from_inventory(src, src.loc) - throw_at(get_edge_target_turf(src, pick(alldirs)), rand(1,3), throw_speed) + user.drop_from_inventory(src, get_turf(user)) + throw_at(get_edge_target_turf(src, pick(alldirs)), rand(1, 3), throw_speed) var/spooky = pick('sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg', 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/wail.ogg') - playsound(src, spooky, 50, 1) + playsound(src, spooky, 50, TRUE) - return 1 + return TRUE -/obj/item/melee/cultblade/pickup(mob/living/user as mob) - if(!iscultist(user) && !istype(user, /mob/living/simple_mob/construct)) - to_chat(user, "An overwhelming feeling of dread comes over you as you pick up the cultist's sword. It would be wise to be rid of this blade quickly.") +/obj/item/melee/cultblade/pickup(mob/living/user) + . = ..() + if (!iscultist(user)) + to_chat(user, SPAN_WARNING("An overwhelming feeling of dread comes over you as you pick up \the [src]. It would be wise to be rid of this blade quickly.")) user.make_dizzy(120) - if(istype(user, /mob/living/simple_mob/construct)) - to_chat(user, "\The [src] hisses, as it is discontent with your acquisition of it. It would be wise to return it to a worthy mortal quickly.") + else if (isconstruct(user)) + to_chat(user, SPAN_WARNING("\The [src] hisses, as it is discontent with your acquisition of it. It would be wise to return it to a worthy mortal quickly.")) + +// Actual cultists spawn with a hooded variant found in hooded.dm; everything below here is legacy for older maps, and has yet to be gutted /obj/item/clothing/head/culthood name = "cult hood" icon_state = "culthood" - desc = "A hood worn by the followers of an unknown god." + desc = "Chips of wet magmellite and strips of leathery textile are melded together into stiff cloth. Its surface is warm and shudders at the touch." origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) flags_inv = HIDEFACE body_parts_covered = HEAD @@ -75,11 +78,11 @@ /obj/item/clothing/suit/cultrobes name = "cult robes" - desc = "A set of armored robes worn by the followers of an unknown god." + desc = "Chips of wet magmellite and strips of leathery textile are melded together into stiff cloth. Its surface is warm and shudders at the touch." icon_state = "cultrobes" origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/book/tome,/obj/item/melee/cultblade) + allowed = list(/obj/item/arcane_tome,/obj/item/melee/cultblade) armor = list(melee = 50, bullet = 30, laser = 50, energy = 80, bomb = 25, bio = 10, rad = 0) flags_inv = HIDEJUMPSUIT siemens_coefficient = 0 @@ -114,7 +117,7 @@ origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) desc = "A bulky suit of armour, bristling with spikes. It looks space-worthy." w_class = ITEMSIZE_NORMAL - allowed = list(/obj/item/book/tome,/obj/item/melee/cultblade,/obj/item/tank/emergency/oxygen,/obj/item/suit_cooling_unit) + allowed = list(/obj/item/arcane_tome,/obj/item/melee/cultblade,/obj/item/tank/emergency/oxygen,/obj/item/suit_cooling_unit) slowdown = 0.5 armor = list(melee = 60, bullet = 50, laser = 30, energy = 80, bomb = 30, bio = 30, rad = 30) siemens_coefficient = 0 diff --git a/code/game/gamemodes/cult/cultify/mob.dm b/code/game/gamemodes/cult/cultify/mob.dm index fa263e8c855..1ce8db1b241 100644 --- a/code/game/gamemodes/cult/cultify/mob.dm +++ b/code/game/gamemodes/cult/cultify/mob.dm @@ -28,7 +28,7 @@ G.invisibility = 0 to_chat(G, "You feel relieved as what's left of your soul finally escapes its prison of flesh.") - cult.harvested += G.mind + LAZYADD(cult.harvested, G.mind) else dust() diff --git a/code/game/gamemodes/cult/cultify/obj.dm b/code/game/gamemodes/cult/cultify/obj.dm index a0f03a3f6d3..34e48f1dba7 100644 --- a/code/game/gamemodes/cult/cultify/obj.dm +++ b/code/game/gamemodes/cult/cultify/obj.dm @@ -18,7 +18,7 @@ return /obj/item/book/cultify() - new /obj/item/book/tome(loc) + new /obj/item/arcane_tome(loc) ..() /obj/item/material/sword/cultify() diff --git a/code/game/gamemodes/cult/hell_universe.dm b/code/game/gamemodes/cult/hell_universe.dm index 2bb00a38b21..52428092efb 100644 --- a/code/game/gamemodes/cult/hell_universe.dm +++ b/code/game/gamemodes/cult/hell_universe.dm @@ -50,8 +50,6 @@ In short: OverlayAndAmbientSet() lightsout(0,0) - runedec += 9000 //basically removing the rune cap - /datum/universal_state/hell/proc/AreaSet() for(var/area/A in world) diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm deleted file mode 100644 index a1c94348a3f..00000000000 --- a/code/game/gamemodes/cult/ritual.dm +++ /dev/null @@ -1,611 +0,0 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -var/global/cultwords = list() -var/global/runedec = 0 -var/global/list/engwords = list("travel", "blood", "join", "hell", "destroy", "technology", "self", "see", "other", "hide") -var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa","mgar","balaq", "karazet", "geeri") - -/client/proc/check_words() // -- Urist - set category = "Special Verbs" - set name = "Check Rune Words" - set desc = "Check the rune-word meaning" - if(!cultwords["travel"]) - runerandom() - for (var/word in engwords) - to_chat(usr, "[cultwords[word]] is [word]") - -/proc/runerandom() //randomizes word meaning - var/list/runewords=rnwords - for (var/word in engwords) - cultwords[word] = pick(runewords) - runewords-=cultwords[word] - -/obj/effect/rune - desc = "A strange collection of symbols drawn in blood." - anchored = 1 - icon = 'icons/obj/rune.dmi' - icon_state = "1" - var/visibility = 0 - unacidable = 1 - layer = TURF_LAYER - - - var/word1 - var/word2 - var/word3 - var/image/blood_image - var/list/converting = list() - -// Places these combos are mentioned: this file - twice in the rune code, once in imbued tome, once in tome's HTML runes.dm - in the imbue rune code. If you change a combination - don't forget to change it everywhere. - -// travel self [word] - Teleport to random [rune with word destination matching] -// travel other [word] - Portal to rune with word destination matching - kinda doesn't work. At least the icon. No idea why. -// see blood Hell - Create a new tome -// join blood self - Incorporate person over the rune into the group -// Hell join self - Summon TERROR -// destroy see technology - EMP rune -// travel blood self - Drain blood -// see Hell join - See invisible -// blood join Hell - Raise dead - -// hide see blood - Hide nearby runes -// blood see hide - Reveal nearby runes - The point of this rune is that its reversed obscure rune. So you always know the words to reveal the rune once you have obscured it. - -// Hell travel self - Leave your body and ghost around -// blood see travel - Manifest a ghost into a mortal body -// Hell tech join - Imbue a rune into a talisman -// Hell blood join - Sacrifice rune -// destroy travel self - Wall rune -// join other self - Summon cultist rune -// travel technology other - Freeing rune // other blood travel was freedom join other - -// hide other see - Deafening rune // was destroy see hear -// destroy see other - Blinding rune -// destroy see blood - BLOOD BOIL - -// self other technology - Communication rune //was other hear blood -// join hide technology - stun rune. Rune color: bright pink. -/obj/effect/rune/Initialize() - . = ..() - blood_image = image(loc = src) - blood_image.override = 1 - for(var/mob/living/silicon/ai/AI in player_list) - if(AI.client) - AI.client.images += blood_image - rune_list.Add(src) - -/obj/effect/rune/Destroy() - for(var/mob/living/silicon/ai/AI in player_list) - if(AI.client) - AI.client.images -= blood_image - qdel(blood_image) - blood_image = null - rune_list.Remove(src) - ..() - -/obj/effect/rune/examine(mob/user) - . = ..() - if(iscultist(user)) - . += "This spell circle reads: [word1] [word2] [word3]." - - -/obj/effect/rune/attackby(I as obj, user as mob) - if(istype(I, /obj/item/book/tome) && iscultist(user)) - to_chat(user, "You retrace your steps, carefully undoing the lines of the rune.") - qdel(src) - return - else if(istype(I, /obj/item/nullrod)) - to_chat(user, "You disrupt the vile magic with the deadening field of the null rod!") - qdel(src) - return - return - - -/obj/effect/rune/attack_hand(mob/living/user as mob) - if(!iscultist(user)) - to_chat(user, "You can't mouth the arcane scratchings without fumbling over them.") - return - if(user.is_muzzled()) - to_chat(user, "You are unable to speak the words of the rune.") - return - if(!word1 || !word2 || !word3 || prob(user.getBrainLoss())) - return fizzle() -// if(!src.visibility) -// src.visibility=1 - if(word1 == cultwords["travel"] && word2 == cultwords["self"]) - return teleport(src.word3) - if(word1 == cultwords["see"] && word2 == cultwords["blood"] && word3 == cultwords["hell"]) - return tomesummon() - if(word1 == cultwords["hell"] && word2 == cultwords["destroy"] && word3 == cultwords["other"]) - return armor() - if(word1 == cultwords["join"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) - return convert() - if(word1 == cultwords["hell"] && word2 == cultwords["join"] && word3 == cultwords["self"]) - return tearreality() - if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["technology"]) - return emp(src.loc,5) - if(word1 == cultwords["travel"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) - return drain() - if(word1 == cultwords["see"] && word2 == cultwords["hell"] && word3 == cultwords["join"]) - return seer() - if(word1 == cultwords["blood"] && word2 == cultwords["join"] && word3 == cultwords["hell"]) - return raise() - if(word1 == cultwords["hide"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) - return obscure(4) - if(word1 == cultwords["hell"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) - return ajourney() - if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["travel"]) - return manifest() - if(word1 == cultwords["hell"] && word2 == cultwords["technology"] && word3 == cultwords["join"]) - return talisman() - if(word1 == cultwords["hell"] && word2 == cultwords["blood"] && word3 == cultwords["join"]) - return sacrifice() - if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["hide"]) - return revealrunes(src) - if(word1 == cultwords["destroy"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) - return wall() - if(word1 == cultwords["travel"] && word2 == cultwords["technology"] && word3 == cultwords["other"]) - return freedom() - if(word1 == cultwords["join"] && word2 == cultwords["other"] && word3 == cultwords["self"]) - return cultsummon() - if(word1 == cultwords["hide"] && word2 == cultwords["other"] && word3 == cultwords["see"]) - return deafen() - if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["other"]) - return blind() - if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) - return bloodboil() - if(word1 == cultwords["self"] && word2 == cultwords["other"] && word3 == cultwords["technology"]) - return communicate() - if(word1 == cultwords["travel"] && word2 == cultwords["other"]) - return itemport(src.word3) - if(word1 == cultwords["join"] && word2 == cultwords["hide"] && word3 == cultwords["technology"]) - return runestun() - else - return fizzle() - - -/obj/effect/rune/proc/fizzle() - if(istype(src,/obj/effect/rune)) - usr.say(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) - else - usr.whisper(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) - for (var/mob/V in viewers(src)) - V.show_message("The markings pulse with a small burst of light, then fall dark.", 3, "You hear a faint fizzle.", 2) - return - -/obj/effect/rune/proc/check_icon() - icon = get_uristrune_cult(word1, word2, word3) - -/obj/item/book/tome - name = "arcane tome" - icon = 'icons/obj/weapons.dmi' - item_icons = list( - icon_l_hand = 'icons/mob/items/lefthand_books.dmi', - icon_r_hand = 'icons/mob/items/righthand_books.dmi', - ) - icon_state ="tome" - item_state = "tome" - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_SMALL - unique = 1 - var/tomedat = "" - var/list/words = list("ire" = "ire", "ego" = "ego", "nahlizet" = "nahlizet", "certum" = "certum", "veri" = "veri", "jatkaa" = "jatkaa", "balaq" = "balaq", "mgar" = "mgar", "karazet" = "karazet", "geeri" = "geeri") - - tomedat = {" - - - - -

The scriptures of Nar-Sie, The One Who Sees, The Geometer of Blood.

- - The book is written in an unknown dialect, there are lots of pictures of various complex geometric shapes. You find some notes in English that give you basic understanding of the many runes written in the book. The notes give you an understanding what the words for the runes should be. However, you do not know how to write all these words in this dialect.
- Below is the summary of the runes.
- -

Contents

-

- Teleport self: Travel Self (word)
- Teleport other: Travel Other (word)
- Summon new tome: See Blood Hell
- Convert a person: Join Blood Self
- Summon Nar-Sie: Hell Join Self
- Disable technology: Destroy See Technology
- Drain blood: Travel Blood Self
- Raise dead: Blood Join Hell
- Hide runes: Hide See Blood
- Reveal hidden runes: Blood See Hide
- Leave your body: Hell travel self
- Ghost Manifest: Blood See Travel
- Imbue a talisman: Hell Technology Join
- Sacrifice: Hell Blood Join
- Create a wall: Destroy Travel Self
- Summon cultist: Join Other Self
- Free a cultist: Travel technology other
- Deafen: Hide Other See
- Blind: Destroy See Other
- Blood Boil: Destroy See Blood
- Communicate: Self Other Technology
- Stun: Join Hide Technology
- Summon Cultist Armor: Hell Destroy Other
- See Invisible: See Hell Join
-

-

Rune Descriptions

-

Teleport self

- Teleport rune is a special rune, as it only needs two words, with the third word being destination. Basically, when you have two runes with the same destination, invoking one will teleport you to the other one. If there are more than 2 runes, you will be teleported to a random one. Runes with different third words will create separate networks. You can imbue this rune into a talisman, giving you a great escape mechanism.
-

Teleport other

- Teleport other allows for teleportation for any movable object to another rune with the same third word. You need 3 cultists chanting the invocation for this rune to work.
-

Summon new tome

- Invoking this rune summons a new arcane tome. -

Convert a person

- This rune opens target's mind to the realm of Nar-Sie, which usually results in this person joining the cult. However, some people (mostly the ones who possess high authority) have strong enough will to stay true to their old ideals.
-

Summon Nar-Sie

- The ultimate rune. It summons the Avatar of Nar-Sie himself, tearing a huge hole in reality and consuming everything around it. Summoning it is the final goal of any cult.
-

Disable Technology

- Invoking this rune creates a strong electromagnetic pulse in a small radius, making it basically analogic to an EMP grenade. You can imbue this rune into a talisman, making it a decent defensive item.
-

Drain Blood

- This rune instantly heals you of some brute damage at the expense of a person placed on top of the rune. Whenever you invoke a drain rune, ALL drain runes on the station are activated, draining blood from anyone located on top of those runes. This includes yourself, though the blood you drain from yourself just comes back to you. This might help you identify this rune when studying words. One drain gives up to 25HP per each victim, but you can repeat it if you need more. Draining only works on living people, so you might need to recharge your "Battery" once its empty. Drinking too much blood at once might cause blood hunger.
-

Raise Dead

- This rune allows for the resurrection of any dead person. You will need a dead human body and a living human sacrifice. Make 2 raise dead runes. Put a living, awake human on top of one, and a dead body on the other one. When you invoke the rune, the life force of the living human will be transferred into the dead body, allowing a ghost standing on top of the dead body to enter it, instantly and fully healing it. Use other runes to ensure there is a ghost ready to be resurrected.
-

Hide runes

- This rune makes all nearby runes completely invisible. They are still there and will work if activated somehow, but you cannot invoke them directly if you do not see them.
-

Reveal runes

- This rune is made to reverse the process of hiding a rune. It reveals all hidden runes in a rather large area around it. -

Leave your body

- This rune gently rips your soul out of your body, leaving it intact. You can observe the surroundings as a ghost as well as communicate with other ghosts. Your body takes damage while you are there, so ensure your journey is not too long, or you might never come back.
-

Manifest a ghost

- Unlike the Raise Dead rune, this rune does not require any special preparations or vessels. Instead of using full lifeforce of a sacrifice, it will drain YOUR lifeforce. Stand on the rune and invoke it. If there's a ghost standing over the rune, it will materialise, and will live as long as you don't move off the rune or die. You can put a paper with a name on the rune to make the new body look like that person.
-

Imbue a talisman

- This rune allows you to imbue the magic of some runes into paper talismans. Create an imbue rune, then an appropriate rune beside it. Put an empty piece of paper on the imbue rune and invoke it. You will now have a one-use talisman with the power of the target rune. Using a talisman drains some health, so be careful with it. You can imbue a talisman with power of the following runes: summon tome, reveal, conceal, teleport, disable technology, communicate, deafen, blind and stun.
-

Sacrifice

- Sacrifice rune allows you to sacrifice a living thing or a body to the Geometer of Blood. Monkeys and dead humans are the most basic sacrifices, they might or might not be enough to gain His favor. A living human is what a real sacrifice should be, however, you will need 3 people chanting the invocation to sacrifice a living person. -

Create a wall

- Invoking this rune solidifies the air above it, creating an an invisible wall. To remove the wall, simply invoke the rune again. -

Summon cultist

- This rune allows you to summon a fellow cultist to your location. The target cultist must be unhandcuffed ant not buckled to anything. You also need to have 3 people chanting at the rune to successfully invoke it. Invoking it takes heavy strain on the bodies of all chanting cultists.
-

Free a cultist

- This rune unhandcuffs and unbuckles any cultist of your choice, no matter where he is. You need to have 3 people invoking the rune for it to work. Invoking it takes heavy strain on the bodies of all chanting cultists.
-

Deafen

- This rune temporarily deafens all non-cultists around you.
-

Blind

- This rune temporarily blinds all non-cultists around you. Very robust. Use together with the deafen rune to leave your enemies completely helpless.
-

Blood boil

- This rune boils the blood all non-cultists in visible range. The damage is enough to instantly critically hurt any person. You need 3 cultists invoking the rune for it to work. This rune is unreliable and may cause unpredicted effect when invoked. It also drains significant amount of your health when successfully invoked.
-

Communicate

- Invoking this rune allows you to relay a message to all cultists on the station and nearby space objects. -

Stun

- Unlike other runes, this one is supposed to be used in talisman form. When invoked directly, it simply releases some dark energy, briefly stunning everyone around. When imbued into a talisman, you can force all of its energy into one person, stunning him so hard he can't even speak. However, effect wears off rather fast.
-

Equip Armor

- When this rune is invoked, either from a rune or a talisman, it will equip the user with the armor of the followers of Nar-Sie. To use this rune to its fullest extent, make sure you are not wearing any form of headgear, armor, gloves or shoes, and make sure you are not holding anything in your hands.
-

See Invisible

- When invoked when standing on it, this rune allows the user to see the the world beyond as long as he does not move.
- - - "} - -/obj/item/book/tome/Initialize() - . = ..() - if(!cultwords["travel"]) - runerandom() - for(var/V in cultwords) - words[cultwords[V]] = V - -/obj/item/book/tome/attack(mob/living/M as mob, mob/living/user as mob) - add_attack_logs(user,M,"Hit with [name]") - - if(istype(M,/mob/observer/dead)) - var/mob/observer/dead/D = M - D.manifest(user) - return - if(!istype(M)) - return - if(!iscultist(user)) - return ..() - if(iscultist(M)) - return - M.take_organ_damage(0,rand(5,20)) //really lucky - 5 hits for a crit - for(var/mob/O in viewers(M, null)) - O.show_message("\The [user] beats \the [M] with \the [src]!", 1) - to_chat(M, "You feel searing heat inside!") - - -/obj/item/book/tome/attack_self(mob/living/user as mob) - usr = user - if(!usr.canmove || usr.stat || usr.restrained()) - return - - if(!cultwords["travel"]) - runerandom() - if(iscultist(user)) - var/C = 0 - for(var/obj/effect/rune/N in rune_list) - C++ - if (!istype(user.loc,/turf)) - to_chat(user, "You do not have enough space to write a proper rune.") - return - - if (C>=26 + runedec + cult.current_antagonists.len) //including the useless rune at the secret room, shouldn't count against the limit of 25 runes - Urist - alert("The cloth of reality can't take that much of a strain. Remove some runes first!") - return - else - switch(alert("You open the tome",,"Read it","Scribe a rune", "Cancel")) - if("Cancel") - return - if("Read it") - if(usr.get_active_hand() != src) - return - user << browse("[tomedat]", "window=Arcane Tome") - return - if(usr.get_active_hand() != src) - return - - var/list/dictionary = list ( - "convert" = list("join","blood","self"), - "wall" = list("destroy","travel","self"), - "blood boil" = list("destroy","see","blood"), - "blood drain" = list("travel","blood","self"), - "raise dead" = list("blood","join","hell"), - "summon narsie" = list("hell","join","self"), - "communicate" = list("self","other","technology"), - "emp" = list("destroy","see","technology"), - "manifest" = list("blood","see","travel"), - "summon tome" = list("see","blood","hell"), - "see invisible" = list("see","hell","join"), - "hide" = list("hide","see","blood"), - "reveal" = list("blood","see","hide"), - "astral journey" = list("hell","travel","self"), - "imbue" = list("hell","technology","join"), - "sacrifice" = list("hell","blood","join"), - "summon cultist" = list("join","other","self"), - "free cultist" = list("travel","technology","other"), - "deafen" = list("hide","other","see"), - "blind" = list("destroy","see","other"), - "stun" = list("join","hide","technology"), - "armor" = list("hell","destroy","other"), - "teleport" = list("travel","self"), - "teleport other" = list("travel","other") - ) - - var/list/english = list() - - var/list/scribewords = list("none") - - for (var/entry in words) - if (words[entry] != entry) - english += list(words[entry] = entry) - - for (var/entry in dictionary) - var/list/required = dictionary[entry] - if (length(english&required) == required.len) - scribewords += entry - - var/chosen_rune = null - - if(usr) - chosen_rune = input ("Choose a rune to scribe.") in scribewords - if (!chosen_rune) - return - if (chosen_rune == "none") - to_chat(user, "You decide against scribing a rune, perhaps you should take this time to study your notes.") - return - if (chosen_rune == "teleport") - dictionary[chosen_rune] += input ("Choose a destination word") in english - if (chosen_rune == "teleport other") - dictionary[chosen_rune] += input ("Choose a destination word") in english - - if(usr.get_active_hand() != src) - return - - for (var/mob/V in viewers(src)) - V.show_message("\The [user] slices open a finger and begins to chant and paint symbols on the floor.", 3, "You hear chanting.", 2) - to_chat(user, "You slice open one of your fingers and begin drawing a rune on the floor whilst chanting the ritual that binds your life essence with the dark arcane energies flowing through the surrounding world.") - user.take_overall_damage((rand(9)+1)/10) // 0.1 to 1.0 damage - if(do_after(user, 50)) - var/area/A = get_area(user) - log_and_message_admins("created \an [chosen_rune] rune at \the [A.name] - [user.loc.x]-[user.loc.y]-[user.loc.z].", user) - if(usr.get_active_hand() != src) - return - var/mob/living/carbon/human/H = user - var/obj/effect/rune/R = new /obj/effect/rune(user.loc) - to_chat(user, "You finish drawing the arcane markings of the Geometer.") - var/list/required = dictionary[chosen_rune] - R.word1 = english[required[1]] - R.word2 = english[required[2]] - R.word3 = english[required[3]] - R.check_icon() - R.blood_DNA = list() - R.blood_DNA[H.dna.unique_enzymes] = H.dna.b_type - return - else - to_chat(user, "The book seems full of illegible scribbles. Is this a joke?") - return - -/obj/item/book/tome/examine(mob/user) - . = ..() - if(!iscultist(user)) - . += "An old, dusty tome with frayed edges and a sinister looking cover." - else - . += "The scriptures of Nar-Sie, The One Who Sees, The Geometer of Blood. Contains the details of every ritual his followers could think of. Most of these are useless, though." - -/obj/item/book/tome/cultify() - return - -/obj/item/book/tome/imbued //admin tome, spawns working runes without waiting - w_class = ITEMSIZE_SMALL - var/cultistsonly = 1 -/obj/item/book/tome/imbued/attack_self(mob/user as mob) - if(src.cultistsonly && !iscultist(usr)) - return - if(!cultwords["travel"]) - runerandom() - if(user) - var/r - if (!istype(user.loc,/turf)) - to_chat(user, "You do not have enough space to write a proper rune.") - var/list/runes = list("teleport", "itemport", "tome", "armor", "convert", "tear in reality", "emp", "drain", "seer", "raise", "obscure", "reveal", "astral journey", "manifest", "imbue talisman", "sacrifice", "wall", "freedom", "cultsummon", "deafen", "blind", "bloodboil", "communicate", "stun") - r = input("Choose a rune to scribe", "Rune Scribing") in runes //not cancellable. - var/obj/effect/rune/R = new /obj/effect/rune - if(istype(user, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - R.blood_DNA = list() - R.blood_DNA[H.dna.unique_enzymes] = H.dna.b_type - var/area/A = get_area(user) - log_and_message_admins("created \an [r] rune at \the [A.name] - [user.loc.x]-[user.loc.y]-[user.loc.z].", user) - switch(r) - if("teleport") - var/list/words = list("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") - var/beacon - if(usr) - beacon = input("Select the last rune", "Rune Scribing") in words - R.word1=cultwords["travel"] - R.word2=cultwords["self"] - R.word3=beacon - R.loc = user.loc - R.check_icon() - if("itemport") - var/list/words = list("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") - var/beacon - if(usr) - beacon = input("Select the last rune", "Rune Scribing") in words - R.word1=cultwords["travel"] - R.word2=cultwords["other"] - R.word3=beacon - R.loc = user.loc - R.check_icon() - if("tome") - R.word1=cultwords["see"] - R.word2=cultwords["blood"] - R.word3=cultwords["hell"] - R.loc = user.loc - R.check_icon() - if("armor") - R.word1=cultwords["hell"] - R.word2=cultwords["destroy"] - R.word3=cultwords["other"] - R.loc = user.loc - R.check_icon() - if("convert") - R.word1=cultwords["join"] - R.word2=cultwords["blood"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("tear in reality") - R.word1=cultwords["hell"] - R.word2=cultwords["join"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("emp") - R.word1=cultwords["destroy"] - R.word2=cultwords["see"] - R.word3=cultwords["technology"] - R.loc = user.loc - R.check_icon() - if("drain") - R.word1=cultwords["travel"] - R.word2=cultwords["blood"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("seer") - R.word1=cultwords["see"] - R.word2=cultwords["hell"] - R.word3=cultwords["join"] - R.loc = user.loc - R.check_icon() - if("raise") - R.word1=cultwords["blood"] - R.word2=cultwords["join"] - R.word3=cultwords["hell"] - R.loc = user.loc - R.check_icon() - if("obscure") - R.word1=cultwords["hide"] - R.word2=cultwords["see"] - R.word3=cultwords["blood"] - R.loc = user.loc - R.check_icon() - if("astral journey") - R.word1=cultwords["hell"] - R.word2=cultwords["travel"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("manifest") - R.word1=cultwords["blood"] - R.word2=cultwords["see"] - R.word3=cultwords["travel"] - R.loc = user.loc - R.check_icon() - if("imbue talisman") - R.word1=cultwords["hell"] - R.word2=cultwords["technology"] - R.word3=cultwords["join"] - R.loc = user.loc - R.check_icon() - if("sacrifice") - R.word1=cultwords["hell"] - R.word2=cultwords["blood"] - R.word3=cultwords["join"] - R.loc = user.loc - R.check_icon() - if("reveal") - R.word1=cultwords["blood"] - R.word2=cultwords["see"] - R.word3=cultwords["hide"] - R.loc = user.loc - R.check_icon() - if("wall") - R.word1=cultwords["destroy"] - R.word2=cultwords["travel"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("freedom") - R.word1=cultwords["travel"] - R.word2=cultwords["technology"] - R.word3=cultwords["other"] - R.loc = user.loc - R.check_icon() - if("cultsummon") - R.word1=cultwords["join"] - R.word2=cultwords["other"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("deafen") - R.word1=cultwords["hide"] - R.word2=cultwords["other"] - R.word3=cultwords["see"] - R.loc = user.loc - R.check_icon() - if("blind") - R.word1=cultwords["destroy"] - R.word2=cultwords["see"] - R.word3=cultwords["other"] - R.loc = user.loc - R.check_icon() - if("bloodboil") - R.word1=cultwords["destroy"] - R.word2=cultwords["see"] - R.word3=cultwords["blood"] - R.loc = user.loc - R.check_icon() - if("communicate") - R.word1=cultwords["self"] - R.word2=cultwords["other"] - R.word3=cultwords["technology"] - R.loc = user.loc - R.check_icon() - if("stun") - R.word1=cultwords["join"] - R.word2=cultwords["hide"] - R.word3=cultwords["technology"] - R.loc = user.loc - R.check_icon() diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm deleted file mode 100644 index 4c649749bf0..00000000000 --- a/code/game/gamemodes/cult/runes.dm +++ /dev/null @@ -1,1123 +0,0 @@ -var/global/list/sacrificed = list() - -/obj/effect/rune/cultify() - return - -/* - * Use as a general guideline for this and related files: - * * ... - when something non-trivial or an error happens, so something similar to "Sparks come out of the machine!" - * * ... - when something that is fit for 'warning' happens but there is some damage or pain as well. - * * ... - when there is a private message to the cultists. This guideline is very arbitrary but there has to be some consistency! - */ - - -/////////////////////////////////////////FIRST RUNE -/obj/effect/rune/proc/teleport(var/key) - var/mob/living/user = usr - var/allrunesloc[] - allrunesloc = new/list() - var/index = 0 - for(var/obj/effect/rune/R in rune_list) - if(R == src) - continue - if(R.word1 == cultwords["travel"] && R.word2 == cultwords["self"] && R.word3 == key && isPlayerLevel(R.z)) - index++ - allrunesloc.len = index - allrunesloc[index] = R.loc - if(index >= 5) - to_chat(user, "You feel pain, as rune disappears in reality shift caused by too much wear of space-time fabric.") - if (istype(user, /mob/living)) - user.take_overall_damage(5, 0) - qdel(src) - if(allrunesloc && index != 0) - if(istype(src,/obj/effect/rune)) - user.say("Sas[pick("'","`")]so c'arta forbici!")//Only you can stop auto-muting - else - user.whisper("Sas[pick("'","`")]so c'arta forbici!") - user.visible_message("[user] disappears in a flash of red light!", \ - "You feel as your body gets dragged through the dimension of Nar-Sie!", \ - "You hear a sickening crunch and sloshing of viscera.") - user.loc = allrunesloc[rand(1,index)] - return - if(istype(src,/obj/effect/rune)) - return fizzle() //Use friggin manuals, Dorf, your list was of zero length. - else - call(/obj/effect/rune/proc/fizzle)() - return - - -/obj/effect/rune/proc/itemport(var/key) - var/culcount = 0 - var/runecount = 0 - var/obj/effect/rune/IP = null - var/mob/living/user = usr - for(var/obj/effect/rune/R in rune_list) - if(R == src) - continue - if(R.word1 == cultwords["travel"] && R.word2 == cultwords["other"] && R.word3 == key) - IP = R - runecount++ - if(runecount >= 2) - to_chat(user, "You feel pain, as rune disappears in reality shift caused by too much wear of space-time fabric.") - if (istype(user, /mob/living)) - user.take_overall_damage(5, 0) - qdel(src) - for(var/mob/living/carbon/C in orange(1,src)) - if(iscultist(C) && !C.stat) - culcount++ - if(culcount>=3) - user.say("Sas[pick("'","`")]so c'arta forbici tarem!") - user.visible_message("You feel air moving from the rune - like as it was swapped with somewhere else.", \ - "You feel air moving from the rune - like as it was swapped with somewhere else.", \ - "You smell ozone.") - for(var/obj/O in src.loc) - if(!O.anchored) - O.loc = IP.loc - for(var/mob/M in src.loc) - M.loc = IP.loc - return - - return fizzle() - - -/////////////////////////////////////////SECOND RUNE - -/obj/effect/rune/proc/tomesummon() - if(istype(src,/obj/effect/rune)) - usr.say("N[pick("'","`")]ath reth sh'yro eth d'raggathnor!") - else - usr.whisper("N[pick("'","`")]ath reth sh'yro eth d'raggathnor!") - usr.visible_message("Rune disappears with a flash of red light, and in its place now a book lies.", \ - "You are blinded by the flash of red light! After you're able to see again, you see that now instead of the rune there's a book.", \ - "You hear a pop and smell ozone.") - if(istype(src,/obj/effect/rune)) - new /obj/item/book/tome(src.loc) - else - new /obj/item/book/tome(usr.loc) - qdel(src) - return - - - -/////////////////////////////////////////THIRD RUNE - -/obj/effect/rune/proc/convert() - var/mob/attacker = usr - var/mob/living/carbon/target = null - for(var/mob/living/carbon/M in src.loc) - if(!iscultist(M) && M.stat < DEAD && !(M in converting)) - target = M - break - - if(!target) //didn't find any new targets - if(!converting.len) - fizzle() - else - to_chat(usr, "You sense that the power of the dark one is already working away at them.") - return - - usr.say("Mah[pick("'","`")]weyh pleggh at e'ntrath!") - - converting |= target - var/list/waiting_for_input = list(target = 0) //need to box this up in order to be able to reset it again from inside spawn, apparently - var/initial_message = 0 - while(target in converting) - if(target.loc != src.loc || target.stat == DEAD) - converting -= target - if(target.getFireLoss() < 100) - target.hallucination = min(target.hallucination, 500) - return 0 - - target.take_overall_damage(0, rand(5, 20)) // You dirty resister cannot handle the damage to your mind. Easily. - even cultists who accept right away should experience some effects - // Resist messages go! - if(initial_message) //don't do this stuff right away, only if they resist or hesitate. - add_attack_logs(attacker,target,"Convert rune") - switch(target.getFireLoss()) - if(0 to 25) - to_chat(target, "Your blood boils as you force yourself to resist the corruption invading every corner of your mind.") - if(25 to 45) - to_chat(target, "Your blood boils and your body burns as the corruption further forces itself into your body and mind.") - if(45 to 75) - to_chat(target, "You begin to hallucinate images of a dark and incomprehensible being and your entire body feels like its engulfed in flame as your mental defenses crumble.") - target.apply_effect(rand(1,10), STUTTER) - if(75 to 100) - to_chat(target, "Your mind turns to ash as the burning flames engulf your very soul and images of an unspeakable horror begin to bombard the last remnants of mental resistance.") - //broken mind - 5000 may seem like a lot I wanted the effect to really stand out for maxiumum losing-your-mind-spooky - //hallucination is reduced when the step off as well, provided they haven't hit the last stage... - - //5000 is waaaay too much, in practice. - target.hallucination = min(target.hallucination + 100, 500) - target.apply_effect(10, STUTTER) - target.adjustBrainLoss(1) - if(100 to INFINITY) - to_chat(target, "Your entire broken soul and being is engulfed in corruption and flames as your mind shatters away into nothing.") - //5000 is waaaay too much, in practice. - target.hallucination = min(target.hallucination + 100, 500) - target.apply_effect(15, STUTTER) - target.adjustBrainLoss(1) - - initial_message = 1 - if (!target.can_feel_pain()) - target.visible_message("The markings below \the [target] glow a bloody red.") - else - var/datum/gender/TT = gender_datums[target.get_visible_gender()] - target.visible_message("[target] writhes in pain as the markings below [TT.him] glow a bloody red.", "AAAAAAHHHH!", "You hear an anguished scream.") - - if(!waiting_for_input[target]) //so we don't spam them with dialogs if they hesitate - waiting_for_input[target] = 1 - - if(!cult.can_become_antag(target.mind) || jobban_isbanned(target, "cultist"))//putting jobban check here because is_convertable uses mind as argument - //waiting_for_input ensures this is only shown once, so they basically auto-resist from here on out. They still need to find a way to get off the freaking rune if they don't want to burn to death, though. - to_chat(target, "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 in the festering wound left behind something sinister takes root.") - to_chat(target, "And you were able to force it out of your mind. You now know the truth, there's something horrible out there, stop it and its minions at all costs.") - - else spawn() - var/choice = alert(target,"Do you want to join the cult?","Submit to Nar'Sie","Resist","Submit") - waiting_for_input[target] = 0 - if(choice == "Submit") //choosing 'Resist' does nothing of course. - cult.add_antagonist(target.mind) - converting -= target - target.hallucination = 0 //sudden clarity - - sleep(100) //proc once every 10 seconds - return 1 - -/////////////////////////////////////////FOURTH RUNE - -/obj/effect/rune/proc/tearreality() - if(!cult.allow_narsie) - return fizzle() - - var/list/cultists = new() - for(var/mob/M in range(1,src)) - if(iscultist(M) && !M.stat) - M.say("Tok-lyr rqa'nap g[pick("'","`")]lt-ulotf!") - if(istype(M, /mob/living/carbon/human/dummy)) //No manifest cheese. - continue - cultists.Add(M) - if(cultists.len >= 9) - if(!narsie_cometh)//so we don't initiate Hell more than one time. - to_world("THE VEIL HAS BEEN SHATTERED!") - world << sound('sound/effects/weather/wind/wind_5_1.ogg') - - SetUniversalState(/datum/universal_state/hell) - narsie_cometh = 1 - - spawn(10 SECONDS) - if(emergency_shuttle) - emergency_shuttle.call_evac() - emergency_shuttle.launch_time = 0 // Cannot recall - - log_and_message_admins_many(cultists, "summoned the end of days.") - return - else - return fizzle() - -/////////////////////////////////////////FIFTH RUNE - -/obj/effect/rune/proc/emp(var/U,var/range_red) //range_red - var which determines by which number to reduce the default emp range, U is the source loc, needed because of talisman emps which are held in hand at the moment of using and that apparently messes things up -- Urist - log_and_message_admins("activated an EMP rune.", usr) - if(istype(src,/obj/effect/rune)) - usr.say("Ta'gh fara[pick("'","`")]qha fel d'amar det!") - else - usr.whisper("Ta'gh fara[pick("'","`")]qha fel d'amar det!") - playsound(U, 'sound/items/Welder2.ogg', 25, 1) - var/turf/T = get_turf(U) - if(T) - T.hotspot_expose(700,125) - var/rune = src // detaching the proc - in theory - empulse(U, (range_red - 3), (range_red - 2), (range_red - 1), range_red) - qdel(rune) - return - -/////////////////////////////////////////SIXTH RUNE - -/obj/effect/rune/proc/drain() - var/drain = 0 - for(var/obj/effect/rune/R in rune_list) - if(R.word1==cultwords["travel"] && R.word2==cultwords["blood"] && R.word3==cultwords["self"]) - for(var/mob/living/carbon/D in R.loc) - if(D.stat!=2) - add_attack_logs(usr,D,"Blood drain rune") - var/bdrain = rand(1,25) - to_chat(D, "You feel weakened.") - D.take_overall_damage(bdrain, 0) - drain += bdrain - if(!drain) - return fizzle() - usr.say ("Yu[pick("'","`")]gular faras desdae. Havas mithum javara. Umathar uf'kal thenar!") - usr.visible_message("Blood flows from the rune into [usr]!", \ - "The blood starts flowing from the rune and into your frail mortal body. You feel... empowered.", \ - "You hear a liquid flowing.") - var/mob/living/user = usr - if(user.bhunger) - user.bhunger = max(user.bhunger-2*drain,0) - if(drain>=50) - user.visible_message("[user]'s eyes give off eerie red glow!", \ - "...but it wasn't nearly enough. You crave, crave for more. The hunger consumes you from within.", \ - "You hear a heartbeat.") - user.bhunger += drain - src = user - spawn() - for (,user.bhunger>0,user.bhunger--) - sleep(50) - user.take_overall_damage(3, 0) - return - user.heal_organ_damage(drain%5, 0) - drain-=drain%5 - for (,drain>0,drain-=5) - sleep(2) - user.heal_organ_damage(5, 0) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - for(var/obj/item/organ/I in H.internal_organs) - if(I.damage > 0) - I.damage = max(I.damage - 5, 0) //Heals 5 damage per organ per use - if(I.damage <= 5 && I.organ_tag == O_EYES) - H.sdisabilities &= ~BLIND - for(var/obj/item/organ/E in H.bad_external_organs) - var/obj/item/organ/external/affected = E - if((affected.damage < affected.min_broken_damage * config.organ_health_multiplier) && (affected.status & ORGAN_BROKEN)) - affected.status &= ~ORGAN_BROKEN - for(var/datum/wound/W in affected.wounds) - if(istype(W, /datum/wound/internal_bleeding)) - affected.wounds -= W - affected.update_damages() - return - - - - - - -/////////////////////////////////////////SEVENTH RUNE - -/obj/effect/rune/proc/seer() - if(usr.loc==src.loc) - if(usr.seer==1) - usr.say("Rash'tla sektath mal[pick("'","`")]zua. Zasan therium viortia.") - to_chat(usr, "The world beyond fades from your vision.") - usr.see_invisible = SEE_INVISIBLE_LIVING - usr.seer = 0 - else if(usr.see_invisible!=SEE_INVISIBLE_LIVING) - to_chat(usr, "The world beyond flashes your eyes but disappears quickly, as if something is disrupting your vision.") - usr.see_invisible = SEE_INVISIBLE_CULT - usr.seer = 0 - else - usr.say("Rash'tla sektath mal[pick("'","`")]zua. Zasan therium vivira. Itonis al'ra matum!") - to_chat(usr, "The world beyond opens to your eyes.") - usr.see_invisible = SEE_INVISIBLE_CULT - usr.seer = 1 - return - return fizzle() - -/////////////////////////////////////////EIGHTH RUNE - -/obj/effect/rune/proc/raise() - var/mob/living/carbon/human/corpse_to_raise - var/mob/living/carbon/human/body_to_sacrifice - - var/is_sacrifice_target = 0 - for(var/mob/living/carbon/human/M in src.loc) - if(M.stat == DEAD) - if(cult && M.mind == cult.sacrifice_target) - is_sacrifice_target = 1 - else - corpse_to_raise = M - break - - if(!corpse_to_raise) - if(is_sacrifice_target) - to_chat(usr, "The Geometer of blood wants this mortal for himself.") - return fizzle() - - - is_sacrifice_target = 0 - find_sacrifice: - for(var/obj/effect/rune/R in rune_list) - if(R.word1==cultwords["blood"] && R.word2==cultwords["join"] && R.word3==cultwords["hell"]) - for(var/mob/living/carbon/human/N in R.loc) - if(cult && N.mind && N.mind == cult.sacrifice_target) - is_sacrifice_target = 1 - else - if(N.stat!= DEAD) - body_to_sacrifice = N - break find_sacrifice - - if(!body_to_sacrifice) - if (is_sacrifice_target) - to_chat(usr, "The Geometer of Blood wants that corpse for himself.") - else - to_chat(usr, "The sacrifical corpse is not dead. You must free it from this world of illusions before it may be used.") - return fizzle() - - if(!cult.can_become_antag(corpse_to_raise.mind) || jobban_isbanned(corpse_to_raise, "cultist")) - to_chat(usr, "The Geometer of Blood refuses to touch this one.") - return fizzle() - else if(!corpse_to_raise.client && corpse_to_raise.mind) //Don't force the dead person to come back if they don't want to. - for(var/mob/observer/dead/ghost in player_list) - if(ghost.mind == corpse_to_raise.mind) - to_chat(ghost, "The cultist [usr.real_name] is trying to \ - revive you. Return to your body if you want to be resurrected into the service of Nar'Sie! \ - (Verbs -> Ghost -> Re-enter corpse)") - break - - sleep(10 SECONDS) - - if(corpse_to_raise.client) - - var/datum/gender/TU = gender_datums[corpse_to_raise.get_visible_gender()] - var/datum/gender/TT = gender_datums[body_to_sacrifice.get_visible_gender()] - - cult.add_antagonist(corpse_to_raise.mind) - corpse_to_raise.revive() - - usr.say("Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!") - corpse_to_raise.visible_message("[corpse_to_raise]'s eyes glow with a faint red as [TU.he] stand[TU.s] up, slowly starting to breathe again.", \ - "Life... I'm alive again...", \ - "You hear a faint, slightly familiar whisper.") - body_to_sacrifice.visible_message("[body_to_sacrifice] is torn apart, a black smoke swiftly dissipating from [TT.his] remains!", \ - "You feel as your blood boils, tearing you apart.", \ - "You hear a thousand voices, all crying in pain.") - body_to_sacrifice.gib() - - to_chat(corpse_to_raise, "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 in the festering wound left behind something sinister takes root.") - to_chat(corpse_to_raise, "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.") - - return - - - - - -/////////////////////////////////////////NINETH RUNE - -/obj/effect/rune/proc/obscure(var/rad) - var/S=0 - for(var/obj/effect/rune/R in orange(rad,src)) - if(R!=src) - R.invisibility=INVISIBILITY_OBSERVER - S=1 - if(S) - if(istype(src,/obj/effect/rune)) - usr.say("Kla[pick("'","`")]atu barada nikt'o!") - for (var/mob/V in viewers(src)) - V.show_message("The rune turns into gray dust, veiling the surrounding runes.", 3) - qdel(src) - else - usr.whisper("Kla[pick("'","`")]atu barada nikt'o!") - to_chat(usr, "Your talisman turns into gray dust, veiling the surrounding runes.") - for (var/mob/V in orange(1,src)) - if(V!=usr) - V.show_message("Dust emanates from [usr]'s hands for a moment.", 3) - - return - if(istype(src,/obj/effect/rune)) - return fizzle() - else - call(/obj/effect/rune/proc/fizzle)() - return - -/////////////////////////////////////////TENTH RUNE - -/obj/effect/rune/proc/ajourney() //some bits copypastaed from admin tools - Urist - if(usr.loc==src.loc) - var/mob/living/carbon/human/L = usr - var/datum/gender/TU = gender_datums[L.get_visible_gender()] - usr.say("Fwe[pick("'","`")]sh mah erl nyag r'ya!") - usr.visible_message("[usr]'s eyes glow blue as [TU.he] freeze[TU.s] in place, absolutely motionless.", \ - "The shadow that is your spirit separates itself from your body. You are now in the realm beyond. While this is a great sight, being here strains your mind and body. Hurry...", \ - "You hear only complete silence for a moment.") - announce_ghost_joinleave(usr.ghostize(1), 1, "You feel that they had to use some [pick("dark", "black", "blood", "forgotten", "forbidden")] magic to [pick("invade","disturb","disrupt","infest","taint","spoil","blight")] this place!") - L.ajourn = 1 - while(L) - if(L.key) - L.ajourn=0 - return - else - L.take_organ_damage(3, 0) - sleep(100) - return fizzle() - - - - -/////////////////////////////////////////ELEVENTH RUNE - -/obj/effect/rune/proc/manifest() - var/obj/effect/rune/this_rune = src - src = null - if(usr.loc!=this_rune.loc) - return this_rune.fizzle() - var/mob/observer/dead/ghost - for(var/mob/observer/dead/O in this_rune.loc) - if(!O.client) continue - if(!O.MayRespawn()) continue - if(O.mind && O.mind.current && O.mind.current.stat != DEAD) continue - if(!(O.client.prefs.be_special & BE_CULTIST)) continue - ghost = O - break - if(!ghost) - return this_rune.fizzle() - if(jobban_isbanned(ghost, "cultist")) - return this_rune.fizzle() - - usr.say("Gal'h'rfikk harfrandid mud[pick("'","`")]gib!") - var/mob/living/carbon/human/dummy/D = new(this_rune.loc) - usr.visible_message("A shape forms in the center of the rune. A shape of... a man.", \ - "A shape forms in the center of the rune. A shape of... a man.", \ - "You hear liquid flowing.") - D.real_name = "Unknown" - var/chose_name = 0 - for(var/obj/item/paper/P in this_rune.loc) - if(P.info) - D.real_name = copytext(P.info, findtext(P.info,">")+1, findtext(P.info,"<",2) ) - chose_name = 1 - break - D.universal_speak = 1 - D.status_flags &= ~GODMODE - D.b_eyes = 200 - D.r_eyes = 200 - D.g_eyes = 200 - D.update_eyes() - D.all_underwear.Cut() - D.key = ghost.key - cult.add_antagonist(D.mind) - - if(!chose_name) - D.real_name = pick("Anguished", "Blasphemous", "Corrupt", "Cruel", "Depraved", "Despicable", "Disturbed", "Exacerbated", "Foul", "Hateful", "Inexorable", "Implacable", "Impure", "Malevolent", "Malignant", "Malicious", "Pained", "Profane", "Profligate", "Relentless", "Resentful", "Restless", "Spiteful", "Tormented", "Unclean", "Unforgiving", "Vengeful", "Vindictive", "Wicked", "Wronged") - D.real_name += " " - D.real_name += pick("Apparition", "Aptrgangr", "Dis", "Draugr", "Dybbuk", "Eidolon", "Fetch", "Fylgja", "Ghast", "Ghost", "Gjenganger", "Haint", "Phantom", "Phantasm", "Poltergeist", "Revenant", "Shade", "Shadow", "Soul", "Spectre", "Spirit", "Spook", "Visitant", "Wraith") - - log_and_message_admins("used a manifest rune.", usr) - var/mob/living/user = usr - while(this_rune && user && user.stat==CONSCIOUS && user.client && user.loc==this_rune.loc) - user.take_organ_damage(1, 0) - sleep(30) - if(D) - D.visible_message("[D] slowly dissipates into dust and bones.", \ - "You feel pain, as bonds formed between your soul and this homunculus break.", \ - "You hear faint rustle.") - D.dust() - return - - - - - -/////////////////////////////////////////TWELFTH RUNE - -/obj/effect/rune/proc/talisman()//only hide, emp, teleport, deafen, blind and tome runes can be imbued atm - var/obj/item/paper/newtalisman - var/unsuitable_newtalisman = 0 - for(var/obj/item/paper/P in src.loc) - if(!P.info) - newtalisman = P - break - else - unsuitable_newtalisman = 1 - if (!newtalisman) - if (unsuitable_newtalisman) - to_chat(usr, "The blank is tainted. It is unsuitable.") - return fizzle() - - var/obj/effect/rune/imbued_from - var/obj/item/paper/talisman/T - for(var/obj/effect/rune/R in orange(1,src)) - if(R==src) - continue - if(R.word1==cultwords["travel"] && R.word2==cultwords["self"]) //teleport - T = new(src.loc) - T.imbue = "[R.word3]" - T.info = "[R.word3]" - imbued_from = R - break - if(R.word1==cultwords["see"] && R.word2==cultwords["blood"] && R.word3==cultwords["hell"]) //tome - T = new(src.loc) - T.imbue = "newtome" - imbued_from = R - break - if(R.word1==cultwords["destroy"] && R.word2==cultwords["see"] && R.word3==cultwords["technology"]) //emp - T = new(src.loc) - T.imbue = "emp" - imbued_from = R - break - if(R.word1==cultwords["blood"] && R.word2==cultwords["see"] && R.word3==cultwords["destroy"]) //conceal - T = new(src.loc) - T.imbue = "conceal" - imbued_from = R - break - if(R.word1==cultwords["hell"] && R.word2==cultwords["destroy"] && R.word3==cultwords["other"]) //armor - T = new(src.loc) - T.imbue = "armor" - imbued_from = R - break - if(R.word1==cultwords["blood"] && R.word2==cultwords["see"] && R.word3==cultwords["hide"]) //reveal - T = new(src.loc) - T.imbue = "revealrunes" - imbued_from = R - break - if(R.word1==cultwords["hide"] && R.word2==cultwords["other"] && R.word3==cultwords["see"]) //deafen - T = new(src.loc) - T.imbue = "deafen" - imbued_from = R - break - if(R.word1==cultwords["destroy"] && R.word2==cultwords["see"] && R.word3==cultwords["other"]) //blind - T = new(src.loc) - T.imbue = "blind" - imbued_from = R - break - if(R.word1==cultwords["self"] && R.word2==cultwords["other"] && R.word3==cultwords["technology"]) //communicat - T = new(src.loc) - T.imbue = "communicate" - imbued_from = R - break - if(R.word1==cultwords["join"] && R.word2==cultwords["hide"] && R.word3==cultwords["technology"]) //communicat - T = new(src.loc) - T.imbue = "runestun" - imbued_from = R - break - if (imbued_from) - for (var/mob/V in viewers(src)) - V.show_message("The runes turn into dust, which then forms into an arcane image on the paper.", 3) - usr.say("H'drak v[pick("'","`")]loso, mir'kanas verbot!") - qdel(imbued_from) - qdel(newtalisman) - else - return fizzle() - -/////////////////////////////////////////THIRTEENTH RUNE - -/obj/effect/rune/proc/mend() - var/mob/living/user = usr - var/datum/gender/TU = gender_datums[usr.get_visible_gender()] - src = null - user.say("Uhrast ka'hfa heldsagen ver[pick("'","`")]lot!") - user.take_overall_damage(200, 0) - runedec+=10 - user.visible_message("\The [user] keels over dead, [TU.his] blood glowing blue as it escapes [TU.his] body and dissipates into thin air.", \ - "In the last moment of your humble life, you feel an immense pain as fabric of reality mends... with your blood.", \ - "You hear faint rustle.") - for(,user.stat==2) - sleep(600) - if (!user) - return - runedec-=10 - return - - -/////////////////////////////////////////FOURTEETH RUNE - -// returns 0 if the rune is not used. returns 1 if the rune is used. -/obj/effect/rune/proc/communicate() - . = 1 // Default output is 1. If the rune is deleted it will return 1 - var/input = input(usr, "Please choose a message to tell to the other acolytes.", "Voice of Blood", "")//sanitize() below, say() and whisper() have their own - if(!input) - if (istype(src)) - fizzle() - return 0 - else - return 0 - - if(istype(src,/obj/effect/rune)) - usr.say("O bidai nabora se[pick("'","`")]sma!") - else - usr.whisper("O bidai nabora se[pick("'","`")]sma!") - - input = sanitize(input) - log_and_message_admins("used a communicate rune to say '[input]'", usr) - for(var/datum/mind/H in cult.current_antagonists) - if (H.current) - to_chat(H.current, "[input]") - for(var/mob/observer/dead/O in player_list) - to_chat(O, "[input]") - qdel(src) - return 1 - -/////////////////////////////////////////FIFTEENTH RUNE - -/obj/effect/rune/proc/sacrifice() - var/list/mob/living/carbon/human/cultsinrange = list() - var/list/mob/living/carbon/human/victims = list() - for(var/mob/living/carbon/human/V in src.loc)//Checks for non-cultist humans to sacrifice - if(ishuman(V)) - if(!(iscultist(V))) - victims += V//Checks for cult status and mob type - for(var/obj/item/I in src.loc)//Checks for MMIs/brains/Intellicards - if(istype(I,/obj/item/organ/internal/brain)) - var/obj/item/organ/internal/brain/B = I - victims += B.brainmob - else if(istype(I,/obj/item/mmi)) - var/obj/item/mmi/B = I - victims += B.brainmob - else if(istype(I,/obj/item/aicard)) - for(var/mob/living/silicon/ai/A in I) - victims += A - for(var/mob/living/carbon/C in orange(1,src)) - if(iscultist(C) && !C.stat) - cultsinrange += C - C.say("Barhah hra zar[pick("'","`")]garis!") - - for(var/mob/H in victims) - - var/worth = 0 - if(istype(H,/mob/living/carbon/human)) - var/mob/living/carbon/human/lamb = H - if(lamb.species.rarity_value > 3) - worth = 1 - - if (ticker.mode.name == "cult") - if(H.mind == cult.sacrifice_target) - if(cultsinrange.len >= 3) - sacrificed += H.mind - if(isrobot(H)) - H.dust()//To prevent the MMI from remaining - else - H.gib() - to_chat(usr, "The Geometer of Blood accepts this sacrifice, your objective is now complete.") - else - to_chat(usr, "Your target's earthly bonds are too strong. You need more cultists to succeed in this ritual.") - else - if(cultsinrange.len >= 3) - if(H.stat !=2) - if(prob(80) || worth) - to_chat(usr, "The Geometer of Blood accepts this [worth ? "exotic " : ""]sacrifice.") - cult.grant_runeword(usr) - else - to_chat(usr, "The Geometer of Blood accepts this sacrifice.") - to_chat(usr, "However, this soul was not enough to gain His favor.") - if(isrobot(H)) - H.dust()//To prevent the MMI from remaining - else - H.gib() - else - if(prob(40) || worth) - to_chat(usr, "The Geometer of Blood accepts this [worth ? "exotic " : ""]sacrifice.") - cult.grant_runeword(usr) - else - to_chat(usr, "The Geometer of Blood accepts this sacrifice.") - to_chat(usr, "However, a mere dead body is not enough to satisfy Him.") - if(isrobot(H)) - H.dust()//To prevent the MMI from remaining - else - H.gib() - else - if(H.stat !=2) - to_chat(usr, "The victim is still alive, you will need more cultists chanting for the sacrifice to succeed.") - else - if(prob(40)) - - to_chat(usr, "The Geometer of Blood accepts this sacrifice.") - cult.grant_runeword(usr) - else - to_chat(usr, "The Geometer of Blood accepts this sacrifice.") - to_chat(usr, "However, a mere dead body is not enough to satisfy Him.") - if(isrobot(H)) - H.dust()//To prevent the MMI from remaining - else - H.gib() - else - if(cultsinrange.len >= 3) - if(H.stat !=2) - if(prob(80)) - to_chat(usr, "The Geometer of Blood accepts this sacrifice.") - cult.grant_runeword(usr) - else - to_chat(usr, "The Geometer of Blood accepts this sacrifice.") - to_chat(usr, "However, this soul was not enough to gain His favor.") - if(isrobot(H)) - H.dust()//To prevent the MMI from remaining - else - H.gib() - else - if(prob(40)) - to_chat(usr, "The Geometer of Blood accepts this sacrifice.") - cult.grant_runeword(usr) - else - to_chat(usr, "The Geometer of Blood accepts this sacrifice.") - to_chat(usr, "However, a mere dead body is not enough to satisfy Him.") - if(isrobot(H)) - H.dust()//To prevent the MMI from remaining - else - H.gib() - else - if(H.stat !=2) - to_chat(usr, "The victim is still alive, you will need more cultists chanting for the sacrifice to succeed.") - else - if(prob(40)) - to_chat(usr, "The Geometer of Blood accepts this sacrifice.") - cult.grant_runeword(usr) - else - to_chat(usr, "The Geometer of Blood accepts this sacrifice.") - to_chat(usr, "However, a mere dead body is not enough to satisfy Him.") - if(isrobot(H)) - H.dust()//To prevent the MMI from remaining - else - H.gib() - -/////////////////////////////////////////SIXTEENTH RUNE - -/obj/effect/rune/proc/revealrunes(var/obj/W as obj) - var/go=0 - var/rad - var/S=0 - if(istype(W,/obj/effect/rune)) - rad = 6 - go = 1 - if (istype(W,/obj/item/paper/talisman)) - rad = 4 - go = 1 - if (istype(W,/obj/item/nullrod)) - rad = 1 - go = 1 - if(go) - for(var/obj/effect/rune/R in orange(rad,src)) - if(R!=src) - R:visibility=15 - S=1 - if(S) - if(istype(W,/obj/item/nullrod)) - to_chat(usr, "Arcane markings suddenly glow from underneath a thin layer of dust!") - return - if(istype(W,/obj/effect/rune)) - usr.say("Nikt[pick("'","`")]o barada kla'atu!") - for (var/mob/V in viewers(src)) - V.show_message("The rune turns into red dust, reveaing the surrounding runes.", 3) - qdel(src) - return - if(istype(W,/obj/item/paper/talisman)) - usr.whisper("Nikt[pick("'","`")]o barada kla'atu!") - to_chat(usr, "Your talisman turns into red dust, revealing the surrounding runes.") - for (var/mob/V in orange(1,usr.loc)) - if(V!=usr) - V.show_message("Red dust emanates from [usr]'s hands for a moment.", 3) - return - return - if(istype(W,/obj/effect/rune)) - return fizzle() - if(istype(W,/obj/item/paper/talisman)) - call(/obj/effect/rune/proc/fizzle)() - return - -/////////////////////////////////////////SEVENTEENTH RUNE - -/obj/effect/rune/proc/wall() - usr.say("Khari[pick("'","`")]d! Eske'te tannin!") - src.density = !src.density - var/mob/living/user = usr - user.take_organ_damage(2, 0) - if(src.density) - to_chat(usr, "Your blood flows into the rune, and you feel that the very space over the rune thickens.") - else - to_chat(usr, "Your blood flows into the rune, and you feel as the rune releases its grasp on space.") - return - -/////////////////////////////////////////EIGHTTEENTH RUNE - -/obj/effect/rune/proc/freedom() - var/mob/living/user = usr - var/list/mob/living/carbon/cultists = new - for(var/datum/mind/H in cult.current_antagonists) - if (istype(H.current,/mob/living/carbon)) - cultists+=H.current - var/list/mob/living/carbon/users = new - for(var/mob/living/carbon/C in orange(1,src)) - if(iscultist(C) && !C.stat) - users+=C - var/dam = round(15 / users.len) - if(users.len>=3) - var/mob/living/carbon/cultist = input("Choose the one who you want to free", "Followers of Geometer") as null|anything in (cultists - users) - if(!cultist) - return fizzle() - if (cultist == user) //just to be sure. - return - if(!(cultist.buckled || \ - cultist.handcuffed || \ - istype(cultist.wear_mask, /obj/item/clothing/mask/muzzle) || \ - (istype(cultist.loc, /obj/structure/closet)&&cultist.loc:welded) || \ - (istype(cultist.loc, /obj/structure/closet/secure_closet)&&cultist.loc:locked) || \ - (istype(cultist.loc, /obj/machinery/dna_scannernew)&&cultist.loc:locked) \ - )) - to_chat(user, "The [cultist] is already free.") - return - cultist.buckled = null - if (cultist.handcuffed) - cultist.drop_from_inventory(cultist.handcuffed) - if (cultist.legcuffed) - cultist.drop_from_inventory(cultist.legcuffed) - if (istype(cultist.wear_mask, /obj/item/clothing/mask/muzzle)) - cultist.drop_from_inventory(cultist.wear_mask) - if(istype(cultist.loc, /obj/structure/closet)&&cultist.loc:welded) - cultist.loc:welded = 0 - if(istype(cultist.loc, /obj/structure/closet/secure_closet)&&cultist.loc:locked) - cultist.loc:locked = 0 - if(istype(cultist.loc, /obj/machinery/dna_scannernew)&&cultist.loc:locked) - cultist.loc:locked = 0 - for(var/mob/living/carbon/C in users) - user.take_overall_damage(dam, 0) - C.say("Khari[pick("'","`")]d! Gual'te nikka!") - qdel(src) - return fizzle() - -/////////////////////////////////////////NINETEENTH RUNE - -/obj/effect/rune/proc/cultsummon() - var/mob/living/user = usr - var/list/mob/living/carbon/cultists = new - for(var/datum/mind/H in cult.current_antagonists) - if (istype(H.current,/mob/living/carbon)) - cultists+=H.current - var/list/mob/living/carbon/users = new - for(var/mob/living/carbon/C in orange(1,src)) - if(iscultist(C) && !C.stat) - users += C - if(users.len>=3) - var/mob/living/carbon/cultist = input("Choose the one who you want to summon", "Followers of Geometer") as null|anything in (cultists - user) - if(!cultist) - return fizzle() - if (cultist == user) //just to be sure. - return - if(cultist.buckled || cultist.handcuffed || (!isturf(cultist.loc) && !istype(cultist.loc, /obj/structure/closet))) - var/datum/gender/TU = gender_datums[cultist.get_visible_gender()] - to_chat(user, "You cannot summon \the [cultist], for [TU.his] shackles of blood are strong.") - return fizzle() - cultist.loc = src.loc - cultist.lying = 1 - cultist.regenerate_icons() - - var/dam = round(25 / (users.len/2)) //More people around the rune less damage everyone takes. Minimum is 3 cultists - - for(var/mob/living/carbon/human/C in users) - if(iscultist(C) && !C.stat) - C.say("N'ath reth sh'yro eth d[pick("'","`")]rekkathnor!") - C.take_overall_damage(dam, 0) - if(users.len <= 4) // You did the minimum, this is going to hurt more and we're going to stun you. - C.apply_effect(rand(3,6), STUN) - C.apply_effect(1, WEAKEN) - user.visible_message("Rune disappears with a flash of red light, and in its place now a body lies.", \ - "You are blinded by the flash of red light! After you're able to see again, you see that now instead of the rune there's a body.", \ - "You hear a pop and smell ozone.") - qdel(src) - return fizzle() - -/////////////////////////////////////////TWENTIETH RUNES - -/obj/effect/rune/proc/deafen() - if(istype(src,/obj/effect/rune)) - var/list/affected = new() - for(var/mob/living/carbon/C in range(7,src)) - if (iscultist(C)) - continue - var/obj/item/nullrod/N = locate() in C - if(N) - continue - C.ear_deaf += 50 - C.show_message("The world around you suddenly becomes quiet.", 3) - affected += C - if(prob(1)) - C.sdisabilities |= DEAF - if(affected.len) - usr.say("Sti[pick("'","`")] kaliedir!") - to_chat(usr, "The world becomes quiet as the deafening rune dissipates into fine dust.") - add_attack_logs(usr,affected,"Deafen rune") - qdel(src) - else - return fizzle() - else - var/list/affected = new() - for(var/mob/living/carbon/C in range(7,usr)) - if (iscultist(C)) - continue - var/obj/item/nullrod/N = locate() in C - if(N) - continue - C.ear_deaf += 30 - //talismans is weaker. - C.show_message("The world around you suddenly becomes quiet.", 3) - affected += C - if(affected.len) - usr.whisper("Sti[pick("'","`")] kaliedir!") - to_chat(usr, "Your talisman turns into gray dust, deafening everyone around.") - add_attack_logs(usr, affected, "Deafen rune") - for (var/mob/V in orange(1,src)) - if(!(iscultist(V))) - V.show_message("Dust flows from [usr]'s hands for a moment, and the world suddenly becomes quiet..", 3) - return - -/obj/effect/rune/proc/blind() - if(istype(src,/obj/effect/rune)) - var/list/affected = new() - for(var/mob/living/carbon/C in viewers(src)) - if (iscultist(C)) - continue - var/obj/item/nullrod/N = locate() in C - if(N) - continue - C.eye_blurry += 50 - C.Blind(20) - if(prob(5)) - C.disabilities |= NEARSIGHTED - if(prob(10)) - C.sdisabilities |= BLIND - C.show_message("Suddenly you see a red flash that blinds you.", 3) - affected += C - if(affected.len) - usr.say("Sti[pick("'","`")] kaliesin!") - to_chat(usr, "The rune flashes, blinding those who not follow the Nar-Sie, and dissipates into fine dust.") - add_attack_logs(usr, affected, "Blindness rune") - qdel(src) - else - return fizzle() - else - var/list/affected = new() - for(var/mob/living/carbon/C in view(2,usr)) - if (iscultist(C)) - continue - var/obj/item/nullrod/N = locate() in C - if(N) - continue - C.eye_blurry += 30 - C.Blind(10) - //talismans is weaker. - affected += C - C.show_message("You feel a sharp pain in your eyes, and the world disappears into darkness..", 3) - if(affected.len) - usr.whisper("Sti[pick("'","`")] kaliesin!") - to_chat(usr, "Your talisman turns into gray dust, blinding those who not follow the Nar-Sie.") - add_attack_logs(usr, affected, "Blindness rune") - return - - -/obj/effect/rune/proc/bloodboil() //cultists need at least one DANGEROUS rune. Even if they're all stealthy. -/* - var/list/mob/living/carbon/cultists = new - for(var/datum/mind/H in ticker.mode.cult) - if (istype(H.current,/mob/living/carbon)) - cultists+=H.current -*/ - var/list/cultists = new //also, wording for it is old wording for obscure rune, which is now hide-see-blood. - var/list/victims = new -// var/list/cultboil = list(cultists-usr) //and for this words are destroy-see-blood. - for(var/mob/living/carbon/C in orange(1,src)) - if(iscultist(C) && !C.stat) - cultists+=C - if(cultists.len>=3) - for(var/mob/living/carbon/M in viewers(usr)) - if(iscultist(M)) - continue - var/obj/item/nullrod/N = locate() in M - if(N) - continue - M.take_overall_damage(51,51) - to_chat(M, "Your blood boils!") - victims += M - if(prob(5)) - spawn(5) - M.gib() - for(var/obj/effect/rune/R in view(src)) - if(prob(10)) - explosion(R.loc, -1, 0, 1, 5) - for(var/mob/living/carbon/human/C in orange(1,src)) - if(iscultist(C) && !C.stat) - C.say("Dedo ol[pick("'","`")]btoh!") - C.take_overall_damage(15, 0) - add_attack_logs(usr, victims, "Blood boil rune") - qdel(src) - else - return fizzle() - return - -// WIP rune, I'll wait for Rastaf0 to add limited blood. - -/obj/effect/rune/proc/burningblood() - var/culcount = 0 - for(var/mob/living/carbon/C in orange(1,src)) - if(iscultist(C) && !C.stat) - culcount++ - if(culcount >= 5) - for(var/obj/effect/rune/R in rune_list) - if(R.blood_DNA == src.blood_DNA) - for(var/mob/living/M in orange(2,R)) - M.take_overall_damage(0,15) - if (R.invisibility>M.see_invisible) - to_chat(M, "Aargh it burns!") - else - to_chat(M, "Rune suddenly ignites, burning you!") - var/turf/T = get_turf(R) - T.hotspot_expose(700,125) - for(var/obj/effect/decal/cleanable/blood/B in world) - if(B.blood_DNA == src.blood_DNA) - for(var/mob/living/M in orange(1,B)) - M.take_overall_damage(0,5) - to_chat(M, "Blood suddenly ignites, burning you!") - var/turf/T = get_turf(B) - T.hotspot_expose(700,125) - qdel(B) - qdel(src) - -////////// Rune 24 (counting burningblood, which kinda doesn't work yet.) - -/obj/effect/rune/proc/runestun(var/mob/living/T as mob) - if(istype(src,/obj/effect/rune)) ///When invoked as rune, flash and stun everyone around. - usr.say("Fuu ma[pick("'","`")]jin!") - for(var/mob/living/L in viewers(src)) - if(iscarbon(L)) - var/mob/living/carbon/C = L - C.flash_eyes() - if(C.stuttering < 1 && (!(HULK in C.mutations))) - C.stuttering = 1 - C.Weaken(1) - C.Stun(1) - C.show_message("The rune explodes in a bright flash.", 3) - add_attack_logs(usr,C,"Stun rune") - - else if(issilicon(L)) - var/mob/living/silicon/S = L - S.Weaken(5) - S.show_message("BZZZT... The rune has exploded in a bright flash.", 3) - add_attack_logs(usr,S,"Stun rune") - qdel(src) - else ///When invoked as talisman, stun and mute the target mob. - usr.say("Dream sign ''Evil sealing talisman'[pick("'","`")]!") - var/obj/item/nullrod/N = locate() in T - if(N) - for(var/mob/O in viewers(T, null)) - O.show_message(text("[] invokes a talisman at [], but they are unaffected!", usr, T), 1) - else - for(var/mob/O in viewers(T, null)) - O.show_message(text("[] invokes a talisman at []", usr, T), 1) - - if(issilicon(T)) - T.Weaken(15) - add_attack_logs(usr,T,"Stun rune") - else if(iscarbon(T)) - var/mob/living/carbon/C = T - C.flash_eyes() - if (!(HULK in C.mutations)) - C.silent += 15 - C.Weaken(25) - C.Stun(25) - add_attack_logs(usr,C,"Stun rune") - return - -/////////////////////////////////////////TWENTY-FIFTH RUNE - -/obj/effect/rune/proc/armor() - var/mob/living/carbon/human/user = usr - if(istype(src,/obj/effect/rune)) - usr.say("N'ath reth sh'yro eth d[pick("'","`")]raggathnor!") - else - usr.whisper("N'ath reth sh'yro eth d[pick("'","`")]raggathnor!") - usr.visible_message("The rune disappears with a flash of red light, and a set of armor appears on [usr]...", \ - "You are blinded by the flash of red light! After you're able to see again, you see that you are now wearing a set of armor.") - - user.equip_to_slot_or_del(new /obj/item/clothing/head/culthood/alt(user), slot_head) - user.equip_to_slot_or_del(new /obj/item/clothing/suit/cultrobes/alt(user), slot_wear_suit) - user.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult(user), slot_shoes) - user.equip_to_slot_or_del(new /obj/item/storage/backpack/cultpack(user), slot_back) - //the above update their overlay icons cache but do not call update_icons() - //the below calls update_icons() at the end, which will update overlay icons by using the (now updated) cache - user.put_in_hands(new /obj/item/melee/cultblade(user)) //put in hands or on floor - - qdel(src) - return diff --git a/code/game/gamemodes/cult/runes/_rune.dm b/code/game/gamemodes/cult/runes/_rune.dm new file mode 100644 index 00000000000..173dd35bebd --- /dev/null +++ b/code/game/gamemodes/cult/runes/_rune.dm @@ -0,0 +1,175 @@ +/// Runes are effectively "spell circles", and are the main source of abilities for cultists. +/obj/effect/rune + name = "rune" + desc = "A strange collection of symbols drawn in what looks to be blood." + icon = 'icons/obj/rune.dmi' + icon_state = "1" + anchored = TRUE + unacidable = TRUE + layer = TURF_LAYER + + /// The actual invocation spoken by each cultist activating this rune. + var/invocation + /// If true, cultists invoking this rune will whisper, instead of speaking normally. + var/whispered + /// A list of "words" used to form this rune. + var/list/circle_words + /// AIs see runes as blood splatters. This variable tracks the image shown in the rune's place. + var/image/blood_image + /// The talisman that this rune will create when used by the Imbue Talisman rune. + var/obj/item/paper/talisman/talisman_path + /// How many cultists need to be adjacent to this rune and able to speak in order to activate it. + var/required_invokers = 1 + /// If non-null, this will be displayed in the rune's tome description instead of the invoker number. + var/invokers_text + /// The actual name of this rune (like "Sacrifice", "Convert", or so on), shown to cultists or ghosts that examine it. + var/rune_name + /// Very short description of the rune's functionality, to be shown as a tooltip in the tome. + var/rune_shorthand + /// As `rune_name`, but for a description of what the rune actually does. + var/rune_desc + /// If false, this rune won't appear on the tome's rune list. + var/can_write = TRUE + +/obj/effect/rune/Initialize() + . = ..() + blood_image = image(loc = src) + blood_image.override = TRUE + for (var/mob/living/silicon/ai/AI in player_list) + AI.client?.images += blood_image + if (cult) + LAZYDISTINCTADD(cult.all_runes, src) + update_icon() + +/obj/effect/rune/Destroy() + for (var/mob/living/silicon/ai/AI in player_list) + AI.client?.images -= blood_image + QDEL_NULL(blood_image) + if (cult) + LAZYREMOVE(cult.all_runes, src) + return ..() + +/obj/effect/rune/get_examine_desc() + if ((isobserver(usr) || iscultist(usr)) && rune_name && rune_desc) + return SPAN_OCCULT("This is a [rune_name] rune.
[rune_desc]") + else + return desc + +/obj/effect/rune/attackby(obj/item/I, mob/user) + if (istype(I, /obj/item/arcane_tome) && iscultist(user)) + to_chat(user, SPAN_NOTICE("You retrace your steps, carefully undoing the lines of \the [src].")) + qdel(src) + return + else if (istype(I, /obj/item/nullrod)) + to_chat(user, SPAN_NOTICE("The scratchings lose coherence and dissolve into puddles of blood that quickly sizzle and disappear.")) + qdel(src) + return + return + +/obj/effect/rune/attack_hand(mob/living/user) + if (can_contribute(user, FALSE)) + do_invocation(user) + +/// Type-specific check for if this rune can be invoked or not. Always returns `TRUE` unless overridden. +/obj/effect/rune/proc/can_invoke(mob/living/invoker) + return TRUE + +/// Type-specific check to get required invokers. This lets runes require different invokers under certain conditions. +/// If it's a rune with dynamic requirements, you might benefit from giving that invoker a message explaining why they need more people. +/obj/effect/rune/proc/get_required_invokers(mob/living/invoker) + return required_invokers + +/** + * Checks if a given mob can participate as an invoker for this rune. + * + * By default, a mob must be a human or construct, a cultist, and able to speak. + * Arguments: + * * `invoker` - The mob being checked as a possible contributor + * * `silent` - If non-true, shows an error message to the mob being checked. Defaults to `TRUE` + */ +/obj/effect/rune/proc/can_contribute(mob/living/invoker, silent = TRUE) + var/fail_message + if (!iscultist(invoker)) + fail_message = "You can't mouth the arcane scratchings without fumbling over them." + if (ishuman(invoker)) + var/mob/living/carbon/human/H = invoker + if (H.is_muzzled() || H.silent || (H.sdisabilities & MUTE)) + fail_message = "You can't speak the words of \the [src]." + else if (!istype(invoker, /mob/living/simple_mob/construct)) + fail_message = "Your mind cannot comprehend the words of \the [src]." + if (fail_message) + if (!silent) + to_chat(invoker, SPAN_WARNING(fail_message)) + return + return TRUE + +/// This proc holds the logic through which invocation is attempted and handled, and shouldn't be overridden. +/// If `can_invoke()` is `TRUE` and the rune has enough possible contributors, the rune's `invoke()` is called. +/obj/effect/rune/proc/do_invocation(mob/living/user) + if (!can_invoke(user)) + fizzle() + return + var/list/invokers = list() + invokers += user + var/req_invokers = get_required_invokers(user) + for (var/mob/living/L in range(1, src) - user) + if (invokers.len >= req_invokers) + break + else if (can_contribute(L)) + invokers.Add(L) + if (invokers.len < req_invokers) + for (var/mob/living/invoker in invokers) + to_chat(invoker, SPAN_WARNING("You need more invokers to use this rune. (Have [invokers.len], need [req_invokers])")) + return + if (invocation) + for (var/mob/living/L in invokers) + !whispered ? L.say(invocation) : L.whisper(invocation) + invoke(invokers) + +/// Short message shown to all observers representing a failed attempt to use the rune. +/// Clarifying messages describing *why* an attempt failed should go in `can_invoke()` or `invoke()` if possible, and not here. +/obj/effect/rune/proc/fizzle() + visible_message(SPAN_WARNING("The markings pulse with a small burst of light, then fall dark.")) + +/// This is what you want to override for each type. The actual effects of a rune (converting someone, teleporting the invoker, etc) should be handled here. +/// To reference the person who triggered the activation of the rune, use `invokers[1]`. +/obj/effect/rune/proc/invoke(list/invokers) + fizzle() + +/// Does something after creating this rune. By default, nothing extra happens. +/obj/effect/rune/proc/after_scribe(mob/living/writer) + return + +/// Applies unique effects to a talisman created by this rune before the rune is destroyed. +/obj/effect/rune/proc/apply_to_talisman(obj/item/paper/talisman/T) + return + +/// "Random" rune with no function, used for generating spooky runes in mapgen. +/obj/effect/rune/mapgen + rune_name = "malformed" + rune_desc = "These meaningless scratchings serve no purpose, save to show one's devotion." + can_write = FALSE + circle_words = list() // Needs to be an empty list to prevent runtimes in Initialize() + +/obj/effect/rune/mapgen/Initialize() + . = ..() + // Doing this pre-roundstart will cause runtimes because Initialize is called before the antagonist subsystem sets up + // For pre-generated mapgen runes, they set up their appearance at roundstart via a proc in `/hook/roundstart`, + // which ensures that the appearance only generates once the word list is populated + if (ticker.HasRoundStarted()) + generate_words() + +/// Composes this rune out of three random cult words. +/obj/effect/rune/mapgen/proc/generate_words() + var/list/words = cult.english_words.Copy() + for (var/i in 1 to 3) + var/word = pick(words) + circle_words.Add(word) + words.Remove(word) + update_icon() + +/// Sets up the appearance of all random runes that were placed before roundstart. Called here instead of `Initialize()` to avoid runtimes. +/hook/roundstart/proc/populate_malformed_runes() + for (var/obj/effect/rune/mapgen/M in cult.all_runes) + M.generate_words() + return TRUE diff --git a/code/game/gamemodes/cult/runes/_rune_icon.dm b/code/game/gamemodes/cult/runes/_rune_icon.dm new file mode 100644 index 00000000000..293f09baacf --- /dev/null +++ b/code/game/gamemodes/cult/runes/_rune_icon.dm @@ -0,0 +1,8 @@ +/obj/effect/rune/update_icon() + assemble_icon() + . = ..() + +/obj/effect/rune/proc/assemble_icon() + var/vocab = cult?.vocabulary + if (circle_words.len == 3 && vocab) + icon = get_uristrune_cult(vocab[circle_words[1]], vocab[circle_words[2]], vocab[circle_words[3]]) diff --git a/code/game/gamemodes/cult/runes/armor.dm b/code/game/gamemodes/cult/runes/armor.dm new file mode 100644 index 00000000000..163abc3f354 --- /dev/null +++ b/code/game/gamemodes/cult/runes/armor.dm @@ -0,0 +1,19 @@ +/obj/effect/rune/armor + rune_name = "Armor" + rune_desc = "When this rune is invoked, either from a rune or a talisman, it will equip its invoker with protective robes. To use this rune to its fullest extent, make sure you are not wearing any form of armor or shoes." + rune_shorthand = "Equips its invoker a set of protective but conspicuous robes. Any articles of clothing that cannot be equipped will not be created." + talisman_path = /obj/item/paper/talisman/armor + circle_words = list(CULT_WORD_HELL, CULT_WORD_DESTROY, CULT_WORD_SELF) + invocation = "Sa tatha najin!" + +/obj/effect/rune/armor/invoke(list/invokers) + var/mob/living/L = invokers[1] + L.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/hooded/cult(L), slot_wear_suit) + L.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult(L), slot_shoes) + L.equip_to_slot_or_del(new /obj/item/storage/backpack/cultpack(L), slot_back) + var/datum/gender/G = gender_datums[L.get_visible_gender()] + L.visible_message( + SPAN_DANGER("The runes crawl onto [L]'s body as they expand to cocoon [G.him], before falling away and revealing [G.his] body once more in gushing spurts of black sludge."), + SPAN_NOTICE("The runes wrap you tightly, and you allow them to shroud you with tainted magmellite before you cast them off as you would a cocoon.") + ) + qdel(src) diff --git a/code/game/gamemodes/cult/runes/astral_journey.dm b/code/game/gamemodes/cult/runes/astral_journey.dm new file mode 100644 index 00000000000..ff6f5eac1e4 --- /dev/null +++ b/code/game/gamemodes/cult/runes/astral_journey.dm @@ -0,0 +1,68 @@ +/obj/effect/rune/astral_journey + rune_name = "Astral Journey" + rune_desc = "Gently rips your life from your body, allowing you to observe your surroundings as a ghost. Your body wll continuously take damage while you remain in this state, so ensure your journey does not remain overlong or you may never return from it." + rune_shorthand = "Explore your surroundings in ghost form while your body remains atop the rune." + circle_words = list(CULT_WORD_HELL, CULT_WORD_TRAVEL, CULT_WORD_SELF) + invocation = "Fwe'sh mah erl nyag r'ya!" + var/mob/living/traveler + var/mob/observer/dead/ghost + +/obj/effect/rune/astral_journey/Destroy() + STOP_PROCESSING(SSprocessing, src) + return ..() + +/obj/effect/rune/astral_journey/can_invoke(mob/living/invoker) + if (traveler) + to_chat(invoker, SPAN_WARNING("\The [traveler] is already using this rune.")) + return + if (get_turf(invoker) != get_turf(src)) + to_chat(invoker, SPAN_WARNING("You must stand on top of this rune to use it.")) + return + return TRUE + +/obj/effect/rune/astral_journey/invoke(list/invokers) + var/mob/living/L = invokers[1] + var/datum/gender/TU = gender_datums[L.get_visible_gender()] + L.visible_message( + SPAN_WARNING("\The [L]'s eyes glow blue as [TU.he] freeze[TU.s] in place, absolutely motionless."), + SPAN_WARNING("The shadow that is your spirit separates itself from your body. You are now in the realm beyond. While this is a great sight, being here strains your mind and body. Hurry..."), + SPAN_WARNING("For a moment, you hear nothing but complete silence.") + ) + set_traveler(L) + +/obj/effect/rune/astral_journey/proc/set_traveler(mob/living/L, return_to_body) + if (!L) + if (traveler) + if (return_to_body && ghost) + if (ghost.reenter_corpse()) + to_chat(ghost, SPAN_DANGER("You are painfully jerked back to reality as the binding sigils force you back into your body.")) + else + to_chat(ghost, SPAN_DANGER(FONT_LARGE("You are unable to return to your body. You are doomed to wander here forever, unless it is returned to life."))) + ghost.mind.current.key = ghost.key + ghost.timeofdeath = world.time + ghost.set_respawn_timer() + announce_ghost_joinleave(ghost, TRUE, "They were trapped here after a failed astral journey.") + traveler = null + STOP_PROCESSING(SSprocessing, src) + else + traveler = L + ghost = traveler.ghostize(TRUE) + ghost.forbid_seeing_deadchat = TRUE + ghost.name = "???" + ghost.color = COLOR_LIGHT_RED + announce_ghost_joinleave(ghost, TRUE, "You feel that they had to use [pick("dark", "black", "blood", "forgotten", "forbidden")] magic to [pick("invade","disturb","disrupt","infest","taint","spoil","blight")] this place!") + START_PROCESSING(SSprocessing, src) + +/obj/effect/rune/astral_journey/process() + if (!traveler || traveler.stat == DEAD) + if (ghost) + to_chat(ghost, SPAN_DANGER(FONT_LARGE("Your body has [!traveler ? "been destroyed. You are doomed to wander here forever" : "died. You are doomed to wander here forever, unless it is returned to life."]"))) + ghost.name = ghost.real_name + ghost.forbid_seeing_deadchat = FALSE + announce_ghost_joinleave(ghost, TRUE, "Their body [traveler ? "died" : "was destroyed"] during an astral journey.") + set_traveler(null) + return + if (traveler.loc != get_turf(src) || !ghost) + set_traveler(null, TRUE) + return + traveler.take_organ_damage(0.3, 0) diff --git a/code/game/gamemodes/cult/runes/blind.dm b/code/game/gamemodes/cult/runes/blind.dm new file mode 100644 index 00000000000..7615cc47bf4 --- /dev/null +++ b/code/game/gamemodes/cult/runes/blind.dm @@ -0,0 +1,19 @@ +/obj/effect/rune/blind + rune_name = "Blind" + rune_desc = "Blinds all non-cultists near the rune." + talisman_path = /obj/item/paper/talisman/blind + circle_words = list(CULT_WORD_DESTROY, CULT_WORD_SEE, CULT_WORD_OTHER) + invocation = "Sti'kaliesin!" + +/obj/effect/rune/blind/invoke(list/invokers) + visible_message(SPAN_DANGER("The runes burst in a red flash.")) + var/list/affected = list() + for (var/mob/living/L in viewers(7, src)) + if (iscultist(L) || findNullRod(L)) + continue + affected += L + L.eye_blurry = max(50, L.eye_blurry) + L.Blind(20) + to_chat(L, SPAN_DANGER("Your vision floods with burning red light!")) + add_attack_logs(invokers[1], affected, "blindness rune") + qdel(src) diff --git a/code/game/gamemodes/cult/runes/communicate.dm b/code/game/gamemodes/cult/runes/communicate.dm new file mode 100644 index 00000000000..f322d4797f6 --- /dev/null +++ b/code/game/gamemodes/cult/runes/communicate.dm @@ -0,0 +1,23 @@ +/obj/effect/rune/communicate + rune_name = "Communicate" + rune_desc = "Allows you to communicate with other cultists by speaking or whispering aloud next to the rune. The rune can be muted or unmuted by invoking it." + talisman_path = /obj/item/paper/talisman/communicate + circle_words = list(CULT_WORD_SELF, CULT_WORD_OTHER, CULT_WORD_TECHNOLOGY) + invocation = "O bidai nabora se'sma!" + whispered = TRUE + var/muted = FALSE + +/obj/effect/rune/communicate/examine(mob/user, infix, suffix) + . = ..() + if (iscultist(user) || isobserver(user)) + . += SPAN_DANGER("This rune [muted ? "is muted, and must be invoked before it will function" : "can be muted by invoking it"].") + +/obj/effect/rune/communicate/hear_talk(mob/M, list/message_pieces, verb) + var/msg = multilingual_to_message(message_pieces, with_capitalization = TRUE) + if (iscultist(M) && get_dist(M, src) <= 1 && !muted) + cult.cult_speak(M, msg) + +/obj/effect/rune/communicate/invoke(list/invokers) + var/mob/living/L = invokers[1] + muted = !muted + to_chat(L, SPAN_NOTICE("This rune will [muted ? "no longer" : "now"] relay your words to the rest of the flock.")) diff --git a/code/game/gamemodes/cult/runes/convert.dm b/code/game/gamemodes/cult/runes/convert.dm new file mode 100644 index 00000000000..5ba8716fc2f --- /dev/null +++ b/code/game/gamemodes/cult/runes/convert.dm @@ -0,0 +1,93 @@ +/obj/effect/rune/convert + rune_name = "Convert" + rune_desc = "A ubiquitous incantation, necessary to educate the innocent. Exposing a nonbeliever's mind to Nar-Sie will typically convert them, but stubborn or resilient individuals may be able to resist Its influence until they succumb or are overwhelmed by the revelation. Some people - typically those possessed of high authority - are able to resist Nar-Sie's influence and will entirely refuse to abandon their old beliefs." + rune_shorthand = "Attempts to convert a nonbeliever to the fold." + circle_words = list(CULT_WORD_JOIN, CULT_WORD_BLOOD, CULT_WORD_SELF) + invocation = "Mah'weyh pleggh at e'ntrath!" + var/mob/living/converting + var/waiting_for_input + var/impudence = 0 + var/impudence_timer = 0 + +/obj/effect/rune/convert/proc/can_convert(mob/living/victim) + return victim && !iscultist(victim) && victim.client && victim.stat != DEAD && victim.mind + +/obj/effect/rune/convert/can_invoke(mob/living/invoker) + for (var/mob/living/L in get_turf(src)) + if (can_convert(L)) + return TRUE + return + +/obj/effect/rune/convert/invoke(list/invokers) + var/mob/living/user = invokers[1] + if (converting) + to_chat(user, SPAN_WARNING("You sense that the Dark One's power is already working away at [converting].")) + return + for (var/mob/living/L in get_turf(src)) + if (can_convert(L)) + converting = L + break + var/datum/gender/G = gender_datums[converting.get_visible_gender()] + converting.visible_message( + SPAN_DANGER("[converting] writhes as the markings below [G.him] glow a sullen, bloody red."), + SPAN_DANGER("AAAAAAHHHH-") + ) + converting.emote("scream") + to_chat(converting, SPAN_OCCULT(FONT_LARGE("Agony invades every corner of your body. Your senses dim, brighten, and dim again. The air swims as if on fire. And as boiling scarlet light bathes your face, you discover that you are not alone in your head."))) + if (!cult.can_become_antag(converting.mind)) // We check this in here instead of can_convert() so that they can be shown to visibly resist the rune's influence + converting = null + converting.visible_message( + SPAN_DANGER("[converting] seems to push away the rune's influence!"), + SPAN_DANGER(FONT_LARGE("...and you're able to force it out of your mind. You need to get away from here as fast as you can!")) + ) + return + else + to_chat(user, SPAN_NOTICE("The ritual is begun. You must keep [converting] atop the rune until [G.he] succumb[G.s] to the Geometer's influence - or die[G.s] from the revelation.")) + impudence = 1 + START_PROCESSING(SSprocessing, src) + process() + +/obj/effect/rune/convert/process() + if (!can_convert(converting) || !cult.can_become_antag(converting.mind) || get_turf(converting) != get_turf(src)) + if (converting) + to_chat(converting, SPAN_DANGER("And then, just like that, it was gone. The blackness slowly recedes, and you are yourself again. Are you still whole?")) + converting = null + waiting_for_input = FALSE + STOP_PROCESSING(SSprocessing, src) + return + if (impudence_timer) + impudence_timer-- + return + if (!waiting_for_input) + spawn() + waiting_for_input = TRUE + var/choice = alert(converting, "Submit to the presence invading your head?", "Submit to Nar-Sie", "Submit!", "Resist!") + waiting_for_input = FALSE + if (choice == "Submit!") + to_chat(converting, SPAN_OCCULT("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 in the festering wound left behind something sinister takes root.")) + to_chat(converting, SPAN_OCCULT("Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.")) + cult.add_antagonist(converting.mind) + converting.hallucination = 0 + converting = null + STOP_PROCESSING(SSprocessing, src) + if (impudence) + converting.take_overall_damage(0, min(5 * impudence, 20)) + converting.apply_effect(min(5 * impudence, 20), AGONY) + switch (converting.getFireLoss()) + if (0 to 25) + to_chat(converting, SPAN_DANGER("You feel like every part of you is on fire as you force yourself to resist the corruption invading every corner of your mind.")) + if (45 to 75) + to_chat(converting, SPAN_DANGER("Flickering images of a vast, vast, dark thing engulf your vision. Everything is so, so hot.")) + converting.apply_effect(rand(1, 10), STUTTER) + if (75 to 100) + to_chat(converting, SPAN_DANGER("You feel like you're being cremated. Images of unspeakable horror flicker through your senses like a slideshow.")) + converting.hallucination = min(converting.hallucination + 100, 500) + converting.apply_effect(10, STUTTER) + converting.adjustBrainLoss(1) + if (100 to INFINITY) + to_chat(converting, SPAN_DANGER("Everything is on fire. You feel yourself coming apart, drawn towards inexorable nothingness.")) + converting.hallucination = min(converting.hallucination + 100, 500) + converting.apply_effect(15, STUTTER) + converting.adjustBrainLoss(1) + impudence++ + impudence_timer = 10 diff --git a/code/game/gamemodes/cult/runes/deafen.dm b/code/game/gamemodes/cult/runes/deafen.dm new file mode 100644 index 00000000000..59c2daf28c1 --- /dev/null +++ b/code/game/gamemodes/cult/runes/deafen.dm @@ -0,0 +1,18 @@ +/obj/effect/rune/deafen + rune_name = "Deafen" + rune_desc = "Deafens all non-cultists near the rune." + talisman_path = /obj/item/paper/talisman/deafen + circle_words = list(CULT_WORD_HIDE, CULT_WORD_OTHER, CULT_WORD_SEE) + invocation = "Dedo ol'btoh!" + +/obj/effect/rune/deafen/invoke(list/invokers) + visible_message(SPAN_DANGER("The runes dissipate into fine dust.")) + var/list/affected = list() + for (var/mob/living/L in hearers(7, src)) + if (iscultist(L) || findNullRod(L)) + continue + affected += L + L.ear_deaf = max(50, L.ear_deaf) + to_chat(L, SPAN_DANGER("Your ears pop and the world goes quiet.")) + add_attack_logs(invokers[1], affected, "deafness rune") + qdel(src) diff --git a/code/game/gamemodes/cult/runes/drain_blood.dm b/code/game/gamemodes/cult/runes/drain_blood.dm new file mode 100644 index 00000000000..ca472703321 --- /dev/null +++ b/code/game/gamemodes/cult/runes/drain_blood.dm @@ -0,0 +1,61 @@ +/obj/effect/rune/drain_blood + rune_name = "Drain Blood" + rune_desc = "Drains the blood of humans on top of all existing runes of this type. The invoker will be healed and regenerate their own blood in the process." + circle_words = list(CULT_WORD_TRAVEL, CULT_WORD_BLOOD, CULT_WORD_SELF) + invocation = "Yu'gular faras desdae. Havas mithum javara. Umathar uf'kal thenar!" + var/remaining_blood = 0 + var/mob/living/carbon/human/cultist + +/obj/effect/rune/drain_blood/Destroy() + STOP_PROCESSING(SSfastprocess, src) + return ..() + +/obj/effect/rune/drain_blood/invoke(list/invokers) + var/mob/living/L = invokers[1] + if (cultist) + to_chat(L, SPAN_WARNING("Another person is already drawing blood from this rune.")) + return + var/total_blood = 0 + for (var/obj/effect/rune/drain_blood/DB in cult.all_runes) + for (var/mob/living/carbon/human/H in get_turf(DB)) + if (H.stat == DEAD) + continue + to_chat(H, SPAN_DANGER("Warm crimson light pulses beneath you. You feel extremely [pick("dizzy", "woozy", "faint", "disoriented", "unsteady")].")) + var/drain = rand(10, 25) + H.remove_blood(drain) + total_blood += drain + if (!total_blood) + return fizzle() + var/datum/gender/G = gender_datums[L.get_visible_gender()] + L.visible_message( + SPAN_WARNING("\The [src] glows a sullen red as \the [L] presses [G.himself] against it. Blood seeps through the scrawlings."), + SPAN_NOTICE("Blood flows from \the [src] into your frail moral body. You feel... empowered.") + ) + L.heal_organ_damage(total_blood % 5) + total_blood -= total_blood % 5 + if (ishuman(L)) + cultist = L + remaining_blood = total_blood / 5 + START_PROCESSING(SSfastprocess, src) + +/obj/effect/rune/drain_blood/process() + if (!remaining_blood || !cultist) + cultist = null + STOP_PROCESSING(SSfastprocess, src) + return + remaining_blood-- + cultist.heal_organ_damage(5, 0) + cultist.add_chemical_effect(CE_BLOODRESTORE, 2) + for (var/obj/item/organ/I in cultist.internal_organs) + if (I.damage > 0) + I.damage = max(I.damage - 5, 0) + if (I.damage <= 5 && I.organ_tag == O_EYES) + cultist.sdisabilities &= ~BLIND + for (var/obj/item/organ/E in cultist.bad_external_organs) + var/obj/item/organ/external/affected = E + if ((affected.damage < affected.min_broken_damage * config.organ_health_multiplier) && (affected.status & ORGAN_BROKEN)) + affected.status &= ~ORGAN_BROKEN + for(var/datum/wound/W in affected.wounds) + if (istype(W, /datum/wound/internal_bleeding)) + affected.wounds -= W + affected.update_damages() diff --git a/code/game/gamemodes/cult/runes/emp.dm b/code/game/gamemodes/cult/runes/emp.dm new file mode 100644 index 00000000000..c9f92ecbfd2 --- /dev/null +++ b/code/game/gamemodes/cult/runes/emp.dm @@ -0,0 +1,16 @@ +/obj/effect/rune/emp + rune_name = "Disable Technology" + rune_desc = "Emits a strong electromagnetic pulse in a medium-sized radius, disabling or harming nearby electronics." + rune_shorthand = "Emits a strong, short-ranged EMP." + talisman_path = /obj/item/paper/talisman/emp + circle_words = list(CULT_WORD_DESTROY, CULT_WORD_SEE, CULT_WORD_TECHNOLOGY) + invocation = "Ta'gh fara'qha fel d'amar det!" + +/obj/effect/rune/emp/invoke(list/invokers) + var/turf/T = get_turf(src) + if (T) + T.hotspot_expose(700, 125) + visible_message(SPAN_DANGER("A wave of heat emanates outwards from the runes as they shimmer and vanish.")) + playsound(src, 'sound/items/Welder.ogg', 50, TRUE) + empulse(T, 2, 3, 4, 5) + qdel(src) diff --git a/code/game/gamemodes/cult/runes/free_cultist.dm b/code/game/gamemodes/cult/runes/free_cultist.dm new file mode 100644 index 00000000000..2df70647102 --- /dev/null +++ b/code/game/gamemodes/cult/runes/free_cultist.dm @@ -0,0 +1,67 @@ +/obj/effect/rune/free_cultist + rune_name = "Free Cultist" + rune_desc = "Reaches out through space and breaks a follower free of their restraints, no matter where they are. This required at least three invokers and causes heavy strain on all involved." + required_invokers = 3 + circle_words = list(CULT_WORD_TRAVEL, CULT_WORD_TECHNOLOGY, CULT_WORD_OTHER) + invocation = "Khari'd! Gual'te nikka!" + var/timeout + var/mob/living/current_invoker + +/obj/effect/rune/free_cultist/can_invoke(mob/living/invoker) + if (timeout > world.time && invoker != current_invoker) + to_chat(invoker, SPAN_WARNING("Allow the current invoker a moment to choose before overriding their will.")) + return + if (current_invoker && invoker != current_invoker) + to_chat(invoker, SPAN_DANGER("You have taken too long to choose a follower to free. [invoker] has overridden your will in the choice.")) + return TRUE + +/obj/effect/rune/free_cultist/invoke(list/invokers) + var/mob/living/L = invokers[1] + var/list/candidates = list() + for (var/mob/living/C in player_list - L) + if (iscultist(C) && can_free(C) && !invokers.Find(C)) + candidates.Add(C) + if (!candidates.len) + to_chat(L, SPAN_WARNING("None of your peers need to be freed in this way.")) + return fizzle() + timeout = world.time + 5 SECONDS + current_invoker = L + var/mob/living/carbon/human/choice = input(L, "Choose a follower to free.", rune_name) as null|anything in candidates + if (!can_free(choice) || L != current_invoker) + return fizzle() + var/datum/gender/G = gender_datums[choice.get_gender()] + for (var/mob/living/C in invokers) + to_chat(C, SPAN_DANGER("You reach out together through space, freeing [choice] of [G.his] bonds.")) + C.take_overall_damage(round(15 / invokers.len), 0) + if (choice.handcuffed) + choice.drop_from_inventory(choice.handcuffed) + if (choice.legcuffed) + choice.drop_from_inventory(choice.legcuffed) + if (istype(choice.wear_suit, /obj/item/clothing/suit/straight_jacket)) + choice.drop_from_inventory(choice.wear_suit) + if (istype(choice.loc, /obj/structure/closet)) + var/obj/structure/closet/amontillado = choice.loc + if (amontillado.sealed) + amontillado.visible_message(SPAN_WARNING("\The [src] flies open!")) + amontillado.break_open() + else if (istype(choice.loc, /obj/machinery/dna_scannernew)) + var/obj/machinery/dna_scannernew/D = choice.loc + if (D.locked) + D.visible_message(SPAN_WARNING("\The [D] grinds open!")) + D.locked = FALSE + D.eject_occupant() + to_chat(choice, SPAN_DANGER("You feel a small surge of vitality as your peers channel their life force to free you of your bonds.")) + qdel(src) + +/obj/effect/rune/free_cultist/proc/can_free(mob/living/L) + if (!L) + return + else if (L.restrained()) + return TRUE + else if (istype(L.loc, /obj/structure/closet)) + var/obj/structure/closet/C = L.loc + return C.req_breakout() + else if (istype(L.loc, /obj/machinery/dna_scannernew)) + var/obj/machinery/dna_scannernew/D = L.loc + return D.locked + return diff --git a/code/game/gamemodes/cult/runes/hide_and_reveal.dm b/code/game/gamemodes/cult/runes/hide_and_reveal.dm new file mode 100644 index 00000000000..21d03847e76 --- /dev/null +++ b/code/game/gamemodes/cult/runes/hide_and_reveal.dm @@ -0,0 +1,31 @@ +/obj/effect/rune/hide_runes + rune_name = "Hide Runes" + rune_desc = "Veils all nearby runes from sight, turning them invisible until they are revealed." + talisman_path = /obj/item/paper/talisman/hide_runes + circle_words = list(CULT_WORD_HIDE, CULT_WORD_SEE, CULT_WORD_BLOOD) + invocation = "Kla'atu barada nikt'o!" + +/obj/effect/rune/hide_runes/invoke(list/invokers) + var/mob/living/L = invokers[1] + for (var/obj/effect/rune/R in range(4, src)) + if (R.invisibility < SEE_INVISIBLE_CULT) + R.invisibility = SEE_INVISIBLE_CULT + R.alpha = 150 // Visual effect for ghosts so they know it's invisible + to_chat(L, SPAN_WARNING("The scratchings turn to dust, veiling the surrounding runes.")) + qdel(src) + +/obj/effect/rune/reveal_runes + rune_name = "Reveal Runes" + rune_desc = "Reverses the effects of Hide Runes, causing all nearby invisible runes to become visible once more." + talisman_path = /obj/item/paper/talisman/reveal_runes + circle_words = list(CULT_WORD_BLOOD, CULT_WORD_SEE, CULT_WORD_HIDE) + invocation = "Nikt'o barada kla'atu!" + +/obj/effect/rune/reveal_runes/invoke(list/invokers) + var/mob/living/L = invokers[1] + for (var/obj/effect/rune/R in range(4, src)) + if (R.invisibility == SEE_INVISIBLE_CULT) + R.invisibility = 0 + R.alpha = 255 + to_chat(L, SPAN_WARNING("The scratchings turn to dust, revealing the surrounding runes.")) + qdel(src) diff --git a/code/game/gamemodes/cult/runes/imbue_talisman.dm b/code/game/gamemodes/cult/runes/imbue_talisman.dm new file mode 100644 index 00000000000..9b888b65a20 --- /dev/null +++ b/code/game/gamemodes/cult/runes/imbue_talisman.dm @@ -0,0 +1,39 @@ +/obj/effect/rune/imbue_talisman + rune_name = "Imbue Talisman" + rune_desc = "Used to create talismans. To use, place a sheet of paper onto this rune, then scribe a different type of rune adjacent to this one. Invoke this one afterwards, and the other rune will be etched onto the paper, creating a talisman out of it. Only some runes can be made into talismans." + rune_shorthand = "Used to create talismans out of sheets of paper and other runes." + circle_words = list(CULT_WORD_HELL, CULT_WORD_TECHNOLOGY, CULT_WORD_JOIN) + invocation = "H'drak v'loso, mir'kanas verbot!" + +/obj/effect/rune/imbue_talisman/can_invoke(mob/living/invoker) + var/valid_runes + for (var/obj/effect/rune/N in orange(1, src)) + if (N.talisman_path) + valid_runes++ + if (!valid_runes) + to_chat(invoker, SPAN_WARNING("There are no nearby runes that can be made into a talisman.")) + return + var/obj/item/paper/P = locate() in get_turf(src) + if (!P) + to_chat(invoker, SPAN_WARNING("A blank piece of paper must be placed on top of the rune to serve as a foundation.")) + return + else if (P.info) + to_chat(invoker, SPAN_WARNING("The blank is tainted with words and cannot be used.")) + return + return TRUE + +/obj/effect/rune/imbue_talisman/invoke(list/invokers) + var/list/valid_runes + for (var/obj/effect/rune/N in orange(1, src)) + if (N.talisman_path) + LAZYADD(valid_runes, N) + if (!LAZYLEN(valid_runes)) + return fizzle() + var/obj/item/paper/P = locate() in get_turf(src) + var/obj/effect/rune/chosen = pick(valid_runes) + var/obj/item/paper/talisman/T = new chosen.talisman_path (get_turf(src)) + chosen.apply_to_talisman(T) + visible_message(SPAN_NOTICE("The words from the runes slither onto \the [P], forming wet red symbols on its surface.")) + qdel(P) + qdel(chosen) + qdel(src) diff --git a/code/game/gamemodes/cult/runes/raise_dead.dm b/code/game/gamemodes/cult/runes/raise_dead.dm new file mode 100644 index 00000000000..998c491244f --- /dev/null +++ b/code/game/gamemodes/cult/runes/raise_dead.dm @@ -0,0 +1,100 @@ +/obj/effect/rune/raise_dead + rune_name = "Raise Dead" + rune_desc = "This rune allows for the resurrection of a dead body. You will need two copies of this rune - place a living human on top of one to use as a sacrifice, and the corpse you wish to resurrect on the other one. If the ritual is successful, the corpse will return to life, while the sacrifice will be torn apart." + rune_shorthand = "Brings a dead body to life using the sacrifice of a living human on another copy of the rune. If the dead body is not a cultist, they will become one." + circle_words = list(CULT_WORD_BLOOD, CULT_WORD_JOIN, CULT_WORD_SELF) + invocation = "Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!" + +/obj/effect/rune/raise_dead/proc/get_targets() + var/to_raise = null + var/to_sacrifice = null + for (var/mob/living/carbon/human/H in get_turf(src)) + if (H.stat == DEAD) + to_raise = H + break + for (var/obj/effect/rune/raise_dead/R in orange(1, src)) + for (var/mob/living/carbon/human/H in get_turf(R)) + if (H.stat != DEAD) + to_sacrifice = H + break + return list(to_raise, to_sacrifice) + +/obj/effect/rune/raise_dead/can_invoke(mob/living/invoker) + var/list/targets = get_targets() + if (!targets[1]) + return + else if (!targets[2]) + to_chat(invoker, SPAN_WARNING("You must position a living human sacrifice on an adjacent copy of the rune.")) + return + var/mob/living/L = targets[1] + if (!L.mind) + to_chat(invoker, SPAN_WARNING("This body is mindless and cannot hold life.")) + return + else if (cult.sacrifice_target == L.mind) + to_chat(invoker, SPAN_WARNING("This body cannot be raised, for the Geometer requires it as sacrifice.")) + return + else if (!cult.can_become_antag(L.mind)) + to_chat(invoker, SPAN_WARNING("The Geometer refuses to touch this body.")) + return + return TRUE + +/obj/effect/rune/raise_dead/invoke(list/invokers) + var/list/targets = get_targets() + var/mob/living/L = invokers[1] + var/mob/living/carbon/human/shears = targets[1] + var/mob/living/carbon/human/lamb = targets[2] + to_chat(L, SPAN_NOTICE("The ritual is begun. Both bodies must remain in place...")) + shears.visible_message(SPAN_WARNING("\The [shears] is yanked upwards by invisible strings, dangling in the air like a puppet.")) + lamb.visible_message( + SPAN_WARNING("\The [lamb] is yanked upwards by invisible strings, dangling in the air like a puppet."), + SPAN_DANGER("An invisible force yanks you in the air and holds you there!") + ) + var/mob/observer/dead/ghost = shears.get_ghost() + if (ghost) + ghost.notify_revive("The cultist [L.real_name] is attempting to raise you from the dead. Return to your body if you wish to be risen into the service of Nar-Sie!", 'sound/effects/genetics.ogg', source = src) + if (do_after(shears, 5 SECONDS, lamb, FALSE, incapacitation_flags = INCAPACITATION_NONE)) + resurrect(shears, lamb, invokers[1]) + return + to_chat(L, SPAN_NOTICE("The ritual's participants must remain stationary!")) + if (shears) + shears.visible_message(SPAN_WARNING("\The [shears] drops unceremoniously to the ground.")) + playsound(shears, "bodyfall", 50, TRUE) + if (lamb) + lamb.visible_message( + SPAN_WARNING("\The [lamb] drops unceremoniously to the ground."), + SPAN_DANGER("The force releases its hold on you, and you fall back to the ground!") + ) + playsound(lamb, "bodyfall", 50, TRUE) + +/obj/effect/rune/raise_dead/proc/resurrect(mob/living/carbon/human/shears, mob/living/carbon/human/lamb, mob/living/invoker) + var/list/targets = get_targets() + if (targets[1] != shears || targets[2] != lamb) + to_chat(invoker, SPAN_WARNING("The ritual's subjects were moved before it could complete.")) + return + if (!shears.client || !shears.mind) + shears.visible_message(SPAN_WARNING("\The [shears] drops unceremoniously to the ground.")) + lamb.visible_message( + SPAN_WARNING("\The [lamb] drops unceremoniously to the ground."), + SPAN_DANGER("The force releases its hold on you, and you fall back to the ground!") + ) + playsound(shears, "bodyfall", 50, TRUE) + playsound(lamb, "bodyfall", 50, TRUE) + to_chat(invoker, SPAN_WARNING("The deceased's spirit did not return to its body. It might work if you try again, or it might not.")) + return + var/datum/gender/GS = gender_datums[shears.get_visible_gender()] + lamb.visible_message( + SPAN_DANGER(FONT_LARGE("[lamb]'s body is violently wrenched apart into bloody pieces!")), + SPAN_DANGER(FONT_LARGE("A vast, dark thing reaches inside you and plucks out something precious. Your body ripped into bloody pieces like wet paper.")) + ) + playsound(lamb, 'sound/effects/splat.ogg', 80, TRUE) + for (var/obj/item/organ/external/E in lamb.organs) + E.droplimb(FALSE, DROPLIMB_EDGE) + shears.revive() + shears.visible_message(SPAN_DANGER("\The [shears] convulses violently as [GS.he] suddenly comes back to life!")) + to_chat(shears, SPAN_DANGER(FONT_LARGE("You are enveloped in a burning red light, plucked from death and forced back into your corpse like a taxidermist might stuff an animal."))) + if (!iscultist(shears)) + to_chat(shears, SPAN_OCCULT("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 in the festering wound left behind something sinister takes root.")) + to_chat(shears, SPAN_OCCULT("Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.")) + cult.add_antagonist(shears.mind) + shears.flash_eyes(override_blindness_check = TRUE) + shears.Paralyse(10) diff --git a/code/game/gamemodes/cult/runes/sacrifice.dm b/code/game/gamemodes/cult/runes/sacrifice.dm new file mode 100644 index 00000000000..61299564b53 --- /dev/null +++ b/code/game/gamemodes/cult/runes/sacrifice.dm @@ -0,0 +1,125 @@ +/obj/effect/rune/sacrifice + rune_name = "Sacrifice" + rune_desc = "Offer a living thing or a body to the Geometer of Blood. Living beings require three invokers to sacrifice, while dead beings require only one." + rune_shorthand = "Offers a creature to the Geometer for consumption. Living beings require three invokers; dead being require only one." + circle_words = list(CULT_WORD_HELL, CULT_WORD_BLOOD, CULT_WORD_JOIN) + invocation = "Barhah hra zar'garis!" + invokers_text = "1 or 3" + var/mob/living/sacrificing + +/// Fetches a sacrifice on top of this rune, aiming for the most "valuable" one (by way of species rarity, role, objective, and so on). +/obj/effect/rune/sacrifice/proc/get_sacrifical_lamb() + var/list/sacrifices + for (var/mob/living/L in get_turf(src)) + var/datum/mind/M = L.mind + if (!iscultist(L) || cult?.sacrifice_target == M) + var/worth = 0 + if (ishuman(L)) + var/mob/living/carbon/human/H = L + if (H.species.rarity_value > 3) + worth++ + if (M) + if (M.assigned_role == "Chaplain") + worth++ + if (cult?.sacrifice_target == M) + worth = 99 // humgry..... + LAZYSET(sacrifices, L, worth) + if (LAZYLEN(sacrifices)) + var/mob/living/worthiest_sacrifice + var/highest_value = -1 + for (var/mob/living/L in shuffle(sacrifices)) + if (sacrifices[L] > highest_value) + worthiest_sacrifice = L + highest_value = sacrifices[L] + return worthiest_sacrifice + return + +/obj/effect/rune/sacrifice/get_required_invokers(mob/living/invoker) + var/mob/living/L = get_sacrifical_lamb() + if (!L) + return required_invokers + if (L.mind && cult.sacrifice_target == L.mind) + to_chat(invoker, SPAN_NOTICE("This sacrifice's earthly bonds necessitates multiple invokers.")) + return 3 + else if (L.stat != DEAD) + to_chat(invoker, SPAN_NOTICE("This sacrifice yet lives, necessitating multiple invokers.")) + return 3 + +/obj/effect/rune/sacrifice/can_invoke(mob/living/invoker) + if (sacrificing) + to_chat(invoker, SPAN_WARNING("The Geometer is already receiving a sacrifice.")) + return sacrificing == null + +/obj/effect/rune/sacrifice/invoke(list/invokers) + var/mob/living/L = get_sacrifical_lamb() + if (!L) + return fizzle() + var/datum/gender/G = gender_datums[L.get_visible_gender()] + L.visible_message( + SPAN_DANGER("The runes beneath [L] widen into a gaping void. Something yanks [G.him] in before they snap shut."), + !iscultist(L) ? SPAN_DANGER("You are dragged into the runes as they widen into a gaping maw!") : SPAN_OCCULT("Yes! Yes! The Geometer accepts you!") + ) + sacrificing = L + if (sacrificing.mind && cult.sacrifice_target == sacrificing.mind) + for (var/mob/living/C in invokers) + to_chat(C, SPAN_OCCULT("The Geometer of Blood is sated. Your objective is now complete.")) + LAZYADD(cult.sacrificed, sacrificing) + else + for (var/mob/living/C in invokers) + to_chat(C, SPAN_OCCULT("The Geometer of Blood feasts on your sacrifice. You have pleased It.")) + if (isrobot(sacrificing)) // Prevent the MMI from surviving + sacrificing.dust() + else + sacrificing.gib() + + + + /*// let's get proper ghoulish + sacrificing.forceMove(src) + for (var/i = 0; i < 10; i++) + if (!sacrificing) + break + sacrificing.take_overall_damage(rand(30, 40), used_weapon = "massive bite marks") + for (var/obj/item/organ/external/E in sacrificing.organs) + if (prob(5)) + E.droplimb(FALSE, pick(DROPLIMB_BLUNT)) + playsound(src, 'sound/effects/squelch1.ogg', 50, TRUE) + sleep (0.5 SECONDS) + if (sacrificing) + var/turf/T = get_turf(src) + if (destroy_body) + visible_message(SPAN_WARNING("Ragged remains and fluids seep from the sigils. There is no sign of a body.")) + sacrificing.gib() + for (var/obj/O in src) + if (isorgan(O)) + var/obj/item/organ/OR = O + if (OR.vital) + qdel(OR) + continue + else + OR.take_damage(round(rand(OR.health / 1.5, OR.health)), TRUE) + O.forceMove(T) + if (prob(75)) + step(O, pick(cardinal)) + else + sacrificing.forceMove(get_turf(src)) + sacrificing.set_dir(NORTH) // facedown + sacrificing.adjustBruteLoss(666) + gibs(T, sacrificing.dna) + visible_message(SPAN_WARNING("[sacrificing] is slowly pushed through the sigils. There is little left of [G.his] body.")) + for (var/obj/item/organ/O in sacrificing.organs) + if (prob(25) || O == sacrificing.get_organ(BP_HEAD)) + if (istype(O, /obj/item/organ/external)) + var/obj/item/organ/external/E = O + E.droplimb(FALSE, DROPLIMB_EDGE) + else + sacrificing.rip_out_internal_organ(O) + if (prob(50)) + qdel(O) + else if (prob(75)) + step(O, pick(cardinal)) + playsound(src, 'sound/effects/splat.ogg', 75, TRUE, frequency = 20000) + for (var/mob/M in view(src)) + shake_camera(M, 3, 1) + sacrificing = null + qdel(src)*/ diff --git a/code/game/gamemodes/cult/runes/see_invisible.dm b/code/game/gamemodes/cult/runes/see_invisible.dm new file mode 100644 index 00000000000..8ea9807352d --- /dev/null +++ b/code/game/gamemodes/cult/runes/see_invisible.dm @@ -0,0 +1,39 @@ +/obj/effect/rune/see_invisible + rune_name = "See Invisible" + rune_desc = "Grants vision into the world beyond as long as one remains on top of the rune, revealing spirits and invisible runes." + circle_words = list(CULT_WORD_SEE, CULT_WORD_HELL, CULT_WORD_JOIN) + invocation = "Rash'tla sektath mal'zua. Zasan therium vivira. Itonis al'ra matum!" + var/mob/living/oracle + +/obj/effect/rune/see_invisible/Destroy() + if (oracle) + oracle.seer = FALSE + oracle.see_invisible = oracle.see_invisible_default + STOP_PROCESSING(SSfastprocess, src) + return ..() + +/obj/effect/rune/see_invisible/can_invoke(mob/living/invoker) + if (oracle) + to_chat(invoker, SPAN_WARNING("[invoker == oracle ? "You are" : "Another is"] already using this rune.")) + return + else if (get_turf(invoker) != get_turf(src)) + to_chat(invoker, SPAN_WARNING("You must stand on top of this rune to use it.")) + return + return TRUE + +/obj/effect/rune/see_invisible/invoke(list/invokers) + var/mob/living/L = invokers[1] + to_chat(L, SPAN_NOTICE("The world beyond opens to your eyes.")) + oracle = L + oracle.seer = TRUE + oracle.see_invisible = SEE_INVISIBLE_OBSERVER + START_PROCESSING(SSfastprocess, src) + +/obj/effect/rune/see_invisible/process() + if (!oracle || !iscultist(oracle) || get_turf(oracle) != get_turf(src)) + if (oracle) + to_chat(oracle, SPAN_WARNING("The world beyond fades from your sight.")) + oracle.seer = FALSE + oracle.see_invisible = oracle.see_invisible_default + oracle = null + STOP_PROCESSING(SSfastprocess, src) diff --git a/code/game/gamemodes/cult/runes/stun.dm b/code/game/gamemodes/cult/runes/stun.dm new file mode 100644 index 00000000000..d6f58505240 --- /dev/null +++ b/code/game/gamemodes/cult/runes/stun.dm @@ -0,0 +1,19 @@ +/obj/effect/rune/stun + rune_name = "Stun" + rune_desc = "This rune is specialized for use in talismans; invoked on its own, its only effect is to slightly disorient nearby beings. Cultists are also affected." + talisman_path = /obj/item/paper/talisman/stun + circle_words = list(CULT_WORD_JOIN, CULT_WORD_HIDE, CULT_WORD_TECHNOLOGY) + invocation = "Fuu ma'jin!" + +/obj/effect/rune/stun/invoke(list/invokers) + visible_message(SPAN_DANGER("The runes explode in a bright flash of light!")) + for (var/mob/living/L in viewers(src)) + if (issilicon(L)) + L.Weaken(5) + else + L.flash_eyes() + L.stuttering = min(1, L.stuttering) + L.Weaken(1) + L.Stun(1) + add_attack_logs(invokers[1], viewers(src), "stun rune") + qdel(src) diff --git a/code/game/gamemodes/cult/runes/summon_cultist.dm b/code/game/gamemodes/cult/runes/summon_cultist.dm new file mode 100644 index 00000000000..a9fdddbc025 --- /dev/null +++ b/code/game/gamemodes/cult/runes/summon_cultist.dm @@ -0,0 +1,69 @@ +/obj/effect/rune/summon_cultist + rune_name = "Summon Cultist" + rune_desc = "Reaches out through space and drags a fellow cultist to the location of the rune. They must not be restrained in any way. The rune requires three invokers, and causes heavy strain on all involved." + required_invokers = 3 + circle_words = list(CULT_WORD_JOIN, CULT_WORD_OTHER, CULT_WORD_SELF) + invocation = "N'ath reth sh'yro eth d'rekkathnor!" + var/timeout + var/mob/living/current_invoker + +/obj/effect/rune/summon_cultist/can_invoke(mob/living/invoker) + if (timeout > world.time && invoker != current_invoker) + to_chat(invoker, SPAN_WARNING("Allow the current invoker a moment to choose before overriding their will.")) + return + if (current_invoker && invoker != current_invoker) + to_chat(invoker, SPAN_DANGER("You have taken too long to choose a follower to free. [invoker] has overridden your will in the choice.")) + return TRUE + +/obj/effect/rune/summon_cultist/invoke(list/invokers) + var/mob/living/L = invokers[1] + var/list/candidates = list() + for (var/mob/living/C in player_list - L) + if (iscultist(C) && !invokers.Find(C)) + candidates.Add(C) + if (!candidates.len) + to_chat(L, SPAN_WARNING("No cultists can be summoned in this way.")) + return fizzle() + timeout = world.time + 5 SECONDS + current_invoker = L + var/mob/living/carbon/human/choice = input(L, "Choose a follower to summon.", rune_name) as null|anything in candidates + if (L != current_invoker) + return + if (choice && !can_summon(choice)) + var/datum/gender/G = gender_datums[choice.get_gender()] + for (var/mob/living/L2 in invokers) + to_chat(L2, SPAN_WARNING("You cannot summon \the [choice], for [G.he] [G.is] bound in place. You must free [G.him] first.")) + for (var/mob/living/C in invokers) + to_chat(C, SPAN_DANGER("You reach out together through space, dragging [choice] to your location.")) + C.take_overall_damage(round(25 / invokers.len), 0) + if (invokers.len <= get_required_invokers(C)) // Minimum invokers causes stuns + C.Weaken(1) + C.Stun(rand(3, 6)) + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(1, 0, choice.loc, 0) + smoke.start() + choice.visible_message( + SPAN_DANGER("\The [choice] is engulfed by black smoke!"), + SPAN_DANGER("You feel your peers calling you forward, and are pulled through space...") + ) + choice.forceMove(get_turf(src)) + choice.lying = TRUE + choice.regenerate_icons() + choice.visible_message( + SPAN_WARNING("The runes bubble, and \the [choice] is thrust through them onto the ground!"), + SPAN_DANGER("...and you emerge on top of the runes they used to bring you forth.") + ) + qdel(src) + +/obj/effect/rune/summon_cultist/proc/can_summon(mob/living/L) + if (!L) + return + else if (L.restrained()) + return + else if (istype(L.loc, /obj/structure/closet)) + var/obj/structure/closet/C = L.loc + return !C.req_breakout() + else if (istype(L.loc, /obj/machinery/dna_scannernew)) + var/obj/machinery/dna_scannernew/D = L.loc + return !D.locked + return TRUE diff --git a/code/game/gamemodes/cult/runes/summon_tome.dm b/code/game/gamemodes/cult/runes/summon_tome.dm new file mode 100644 index 00000000000..ddf20aacbdf --- /dev/null +++ b/code/game/gamemodes/cult/runes/summon_tome.dm @@ -0,0 +1,12 @@ +/obj/effect/rune/summon_tome + rune_name = "Summon Tome" + rune_desc = "Manifests another copy of the Geometer's scripture." + rune_shorthand = "Creates a new arcane tome." + talisman_path = /obj/item/paper/talisman/summon_tome + circle_words = list(CULT_WORD_SEE, CULT_WORD_BLOOD, CULT_WORD_HELL) + invocation = "N'ath reth sh'yro eth d'raggathnor!" + +/obj/effect/rune/summon_tome/invoke(list/invokers) + visible_message(SPAN_WARNING("Space is congealed into a blank book. The runes slither into the pages and drag the cover shut with a hollow thud.")) + new /obj/item/arcane_tome(get_turf(src)) + qdel(src) diff --git a/code/game/gamemodes/cult/runes/tear_reality.dm b/code/game/gamemodes/cult/runes/tear_reality.dm new file mode 100644 index 00000000000..acdddb94939 --- /dev/null +++ b/code/game/gamemodes/cult/runes/tear_reality.dm @@ -0,0 +1,30 @@ +/obj/effect/rune/tear_reality + rune_name = "Tear Reality" + rune_desc = "Sunders the space between this place and the Geometer's, allowing Nar-Sie to grace the world with its influence. Requires nine invokers, and can only be executed if It actually wills it so." + rune_shorthand = "Sunder the space between this place and the Geometer's. Can only be executed if Nar-Sie actually wills it." + required_invokers = 9 + circle_words = list(CULT_WORD_HELL, CULT_WORD_JOIN, CULT_WORD_SELF) + invocation = "TOK-LYR RQA'NAP G'LT-UTOLF!" + +/obj/effect/rune/tear_reality/can_invoke(mob/living/invoker) + if (narsie_cometh) + to_chat(invoker, SPAN_WARNING("The Geometer has already been called forth.")) + return + else if (!cult.allow_narsie) + to_chat(invoker, SPAN_WARNING("The Geometer does not wish the veil destroyed here this day.")) + return + return TRUE + +/obj/effect/rune/tear_reality/invoke(list/invokers) + to_world("IT COMES") + for (var/mob/M in player_list) + M.playsound_local(M.loc, 'sound/effects/weather/wind/wind_5_1.ogg', 50) + SetUniversalState(/datum/universal_state/hell) + narsie_cometh = TRUE + log_and_message_admins_many(invokers, "summoned the end of days.") + addtimer(CALLBACK(src, .proc/call_shuttle), 10 SECONDS) + +/obj/effect/rune/tear_reality/proc/call_shuttle() + if (emergency_shuttle) + emergency_shuttle.call_evac() + emergency_shuttle.launch_time = 0 diff --git a/code/game/gamemodes/cult/runes/teleport.dm b/code/game/gamemodes/cult/runes/teleport.dm new file mode 100644 index 00000000000..34f8e38367f --- /dev/null +++ b/code/game/gamemodes/cult/runes/teleport.dm @@ -0,0 +1,55 @@ +/obj/effect/rune/teleport + rune_name = "Teleport" + rune_desc = "When invoked, teleports anything on top of itself to another Teleport rune sharing the same keyword." + talisman_path = /obj/item/paper/talisman/teleport + circle_words = list(CULT_WORD_TRAVEL, CULT_WORD_SELF, CULT_WORD_OTHER) + invocation = "Sas'so c'arta forbici!" + var/key_word = CULT_WORD_OTHER + +/obj/effect/rune/teleport/examine(mob/user, infix, suffix) + . = ..() + if (iscultist(user) || isobserver(user)) + . += SPAN_DANGER("This rune's key word is \"[key_word]\".") + +/obj/effect/rune/teleport/after_scribe(mob/living/author) + var/word = input(author, "Choose a key word for this rune.", rune_name) as null|anything in cult.english_words + if (QDELETED(src) || QDELETED(author)) + return + if (!word) + to_chat(author, SPAN_WARNING("No key word specified. Using \"[key_word]\" instead.")) + else + key_word = word + circle_words[3] = word + update_icon() + +/obj/effect/rune/teleport/can_invoke(mob/living/invoker) + var/valid_runes = 0 + for (var/obj/effect/rune/teleport/T in cult.all_runes - src) + if (T.key_word == key_word) + valid_runes++ + if (!valid_runes) + to_chat(invoker, SPAN_WARNING("There are no other Teleport runes with a keyword of \"[key_word]\".")) + return + return TRUE + +/obj/effect/rune/teleport/invoke(list/invokers) + var/list/runes + for (var/obj/effect/rune/teleport/T in cult.all_runes - src) + if (T.key_word == key_word) + LAZYADD(runes, T) + if (!LAZYLEN(runes)) + return fizzle() + var/obj/effect/rune/teleport/T = pick(runes) + var/turf/new_loc = get_turf(T) + for (var/mob/living/L in get_turf(src)) + to_chat(L, SPAN_WARNING("You are dragged through space!")) + L.forceMove(new_loc) + for (var/obj/O in get_turf(src)) + if (!O.anchored) + O.forceMove(new_loc) + visible_message(SPAN_DANGER("\The [src] emit\s a burst of red light!")) + T.visible_message(SPAN_DANGER("\The [src] emit\s a burst of red light!")) + +/obj/effect/rune/teleport/apply_to_talisman(obj/item/paper/talisman/T) + var/obj/item/paper/talisman/teleport/TP = T + TP.key_word = key_word diff --git a/code/game/gamemodes/cult/runes/wall.dm b/code/game/gamemodes/cult/runes/wall.dm new file mode 100644 index 00000000000..30ef1c3aa85 --- /dev/null +++ b/code/game/gamemodes/cult/runes/wall.dm @@ -0,0 +1,12 @@ +/obj/effect/rune/wall + rune_name = "Wall" + rune_desc = "Invoking this rune solidifies the air above it, creating an an invisible wall. Invoke the rune again to bring the barrier down." + rune_shorthand = "Forms a reversible solid barrier when invoked." + circle_words = list(CULT_WORD_DESTROY, CULT_WORD_TRAVEL, CULT_WORD_SELF) + invocation = "Khari'd! Eske'te tannin!" + +/obj/effect/rune/wall/invoke(list/invokers) + var/mob/living/L = invokers[1] + density = !density + L.take_organ_damage(2, 0) + to_chat(L, SPAN_DANGER("Your blood flows into the rune, and you feel [density ? "the very space above it thicken" : "it release its grasp on space"].")) diff --git a/code/game/gamemodes/cult/runes/weapon.dm b/code/game/gamemodes/cult/runes/weapon.dm new file mode 100644 index 00000000000..34ca2b3982a --- /dev/null +++ b/code/game/gamemodes/cult/runes/weapon.dm @@ -0,0 +1,15 @@ +/obj/effect/rune/weapon + rune_name = "Weapon" + rune_desc = "Creates a deadly blade, adept at maiming and dismemberment. Use sparingly, for the Geometer disdains bloodshed not executed at Its request." + circle_words = list(CULT_WORD_HELL, CULT_WORD_DESTROY, CULT_WORD_OTHER) + invocation = "Sa tatha rajin!" + +/obj/effect/rune/weapon/invoke(list/invokers) + var/mob/living/L = invokers[1] + var/obj/item/melee/cultblade/C = new (get_turf(src)) + L.put_in_active_hand(C) + L.visible_message( + SPAN_DANGER("The runes coalesce into a long and cruel blade, which [L.get_active_hand() == C ? "\the [L] picks up" : "settles on the floor"]."), + SPAN_DANGER("The runes coalesce into a long and cruel blade[L.get_active_hand() == C ? ", which you pick up" : ""].") + ) + qdel(src) diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm deleted file mode 100644 index 6b57e9d2206..00000000000 --- a/code/game/gamemodes/cult/talisman.dm +++ /dev/null @@ -1,118 +0,0 @@ -/obj/item/paper/talisman - icon_state = "paper_talisman" - var/imbue = null - var/uses = 0 - info = "


" - -/obj/item/paper/talisman/attack_self(mob/living/user as mob) - if(iscultist(user)) - var/delete = 1 - // who the hell thought this was a good idea :( - switch(imbue) - if("newtome") - call(/obj/effect/rune/proc/tomesummon)() - if("armor") - call(/obj/effect/rune/proc/armor)() - if("emp") - call(/obj/effect/rune/proc/emp)(usr.loc,3) - if("conceal") - call(/obj/effect/rune/proc/obscure)(2) - if("revealrunes") - call(/obj/effect/rune/proc/revealrunes)(src) - if("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") - call(/obj/effect/rune/proc/teleport)(imbue) - if("communicate") - //If the user cancels the talisman this var will be set to 0 - delete = call(/obj/effect/rune/proc/communicate)() - if("deafen") - call(/obj/effect/rune/proc/deafen)() - if("blind") - call(/obj/effect/rune/proc/blind)() - if("runestun") - to_chat(user, "To use this talisman, attack your target directly.") - return - if("supply") - supply() - user.take_organ_damage(5, 0) - if(src && src.imbue!="supply" && src.imbue!="runestun") - if(delete) - qdel(src) - return - else - to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") - return - - -/obj/item/paper/talisman/attack(mob/living/carbon/T as mob, mob/living/user as mob) - if(iscultist(user)) - if(imbue == "runestun") - user.take_organ_damage(5, 0) - call(/obj/effect/rune/proc/runestun)(T) - qdel(src) - else - ..() ///If its some other talisman, use the generic attack code, is this supposed to work this way? - else - ..() - - -/obj/item/paper/talisman/proc/supply(var/key) - if (!src.uses) - qdel(src) - return - - var/dat = "There are [src.uses] bloody runes on the parchment.
" - dat += "Please choose the chant to be imbued into the fabric of reality.
" - dat += "
" - dat += "N'ath reth sh'yro eth d'raggathnor! - Allows you to summon a new arcane tome.
" - dat += "Sas'so c'arta forbici! - Allows you to move to a rune with the same last word.
" - dat += "Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range.
" - dat += "Kla'atu barada nikt'o! - Allows you to conceal the runes you placed on the floor.
" - dat += "O bidai nabora se'sma! - Allows you to coordinate with others of your cult.
" - dat += "Fuu ma'jin - Allows you to stun a person by attacking them with the talisman.
" - dat += "Sa tatha najin - Allows you to summon armoured robes and an unholy blade
" - dat += "Kal om neth - Summons a soul stone
" - dat += "Da A'ig Osk - Summons a construct shell for use with captured souls. It is too large to carry on your person.
" - usr << browse(dat, "window=id_com;size=350x200") - return - - -/obj/item/paper/talisman/Topic(href, href_list) - if(!src) return - if (usr.stat || usr.restrained() || !in_range(src, usr)) return - - if (href_list["rune"]) - switch(href_list["rune"]) - if("newtome") - var/obj/item/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) - T.imbue = "newtome" - if("teleport") - var/obj/item/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) - T.imbue = "[pick("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri", "orkan", "allaq")]" - T.info = "[T.imbue]" - if("emp") - var/obj/item/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) - T.imbue = "emp" - if("conceal") - var/obj/item/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) - T.imbue = "conceal" - if("communicate") - var/obj/item/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) - T.imbue = "communicate" - if("runestun") - var/obj/item/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) - T.imbue = "runestun" - if("armor") - var/obj/item/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) - T.imbue = "armor" - if("soulstone") - new /obj/item/soulstone(get_turf(usr)) - if("construct") - new /obj/structure/constructshell/cult(get_turf(usr)) - src.uses-- - supply() - return - - -/obj/item/paper/talisman/supply - imbue = "supply" - uses = 5 diff --git a/code/game/gamemodes/cult/talismans/_talisman.dm b/code/game/gamemodes/cult/talismans/_talisman.dm new file mode 100644 index 00000000000..7d4cf81eea3 --- /dev/null +++ b/code/game/gamemodes/cult/talismans/_talisman.dm @@ -0,0 +1,55 @@ +/// Effectively portable runes, created with the Imbue Talisman rune. Talismans are usually similar to the runes they're created from. +/obj/item/paper/talisman + icon_state = "paper_talisman" + info = "


" + can_write = FALSE + + /// The actual name of this talisman (like "Stun", "Blind", or so on), shown to cultists or ghosts that examine it. + var/talisman_name + /// A description of what this talisman actually does. + var/talisman_desc + /// Specifically shown in the arcane tome, alongside the rune's description. + var/tome_desc + /// The words spoken by a cultist activating this talisman. + var/invocation = "Look at this photograph!" + /// If true, cultists invoking this talisman will whisper, instead of speaking normally. + var/whispered = TRUE + /// If TRUE, the talisman will be deleted after invocation. + var/delete_self = TRUE + +/obj/item/paper/talisman/get_examine_desc() + if ((isobserver(usr) || iscultist(usr)) && talisman_name && talisman_desc) + return SPAN_OCCULT("This is a [talisman_name] talisman.
[talisman_desc]") + else + return desc + +/obj/item/paper/talisman/show_content(mob/user, force_show) + if (iscultist(user)) + return + return ..() + +/obj/item/paper/talisman/attack_self(mob/living/user) + if (iscultist(user)) + invoke(user) + !whispered ? user.say(invocation) : user.whisper(invocation) + if (delete_self) + qdel(src) + return + return ..() + +/// The per-type proc for the talisman actually doing something on activation. This is what you want to override. +/// Some talismans (like Stun) have their own logic and thus ignore this proc. +/obj/item/paper/talisman/proc/invoke(mob/living/user) + return + +/* +Here's a template you can copy-paste to quickly get started on making a new talisman: + +/obj/item/paper/talisman/SUBTYPE + talisman_name = "TYPE" + talisman_desc = "DESC" + invocation = "Bruh" + +/obj/item/paper/talisman/SUBTYPE/invoke(mob/living/user) + return +*/ diff --git a/code/game/gamemodes/cult/talismans/armor.dm b/code/game/gamemodes/cult/talismans/armor.dm new file mode 100644 index 00000000000..16c7ef5e4a2 --- /dev/null +++ b/code/game/gamemodes/cult/talismans/armor.dm @@ -0,0 +1,15 @@ +/obj/item/paper/talisman/armor + talisman_name = "Armor" + talisman_desc = "Equips its invoker with a set of followers' armor, equivalent to the rune from which it was drawn." + tome_desc = "Ditto." + invocation = "Sa tatha najin!" + +/obj/item/paper/talisman/armor/invoke(mob/living/user) + user.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/hooded/cult(user), slot_wear_suit) + user.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult(user), slot_shoes) + user.equip_to_slot_or_del(new /obj/item/storage/backpack/cultpack(user), slot_back) + var/datum/gender/G = gender_datums[user.get_visible_gender()] + user.visible_message( + SPAN_DANGER("\The [src] expands to briefly envelop \the [user]'s body before [G.he] tears through it in a gushing spurt of black sludge."), + SPAN_NOTICE("The talisman expands to wrap you tightly, and you allow it to shroud you with tainted magmellite before you tear through the un-paper.") + ) diff --git a/code/game/gamemodes/cult/talismans/blind.dm b/code/game/gamemodes/cult/talismans/blind.dm new file mode 100644 index 00000000000..bbf449c4bac --- /dev/null +++ b/code/game/gamemodes/cult/talismans/blind.dm @@ -0,0 +1,17 @@ +/obj/item/paper/talisman/blind + talisman_name = "Blind" + talisman_desc = "Induces blindness in nonbelievers within two tiles." + tome_desc = "Shorter range." + invocation = "Sti'kaliesin!" + +/obj/item/paper/talisman/blind/invoke(mob/living/user) + to_chat(user, SPAN_WARNING("The talisman in your hands turns to gray dust, blinding nearby nonbelievers.")) + var/list/affected = list() + for (var/mob/living/L in viewers(2, src)) + if (iscultist(L) || findNullRod(L)) + continue + affected.Add(L) + L.eye_blurry = max(30, L.eye_blurry) + L.Blind(10) + to_chat(L, SPAN_DANGER("Your vision floods with burning red light!")) + add_attack_logs(user, affected, "blindness talisman") diff --git a/code/game/gamemodes/cult/talismans/communicate.dm b/code/game/gamemodes/cult/talismans/communicate.dm new file mode 100644 index 00000000000..82f02b27b6f --- /dev/null +++ b/code/game/gamemodes/cult/talismans/communicate.dm @@ -0,0 +1,17 @@ +/obj/item/paper/talisman/communicate + talisman_name = "Communicate" + talisman_desc = "Allows you to communicate with other cultists." + tome_desc = "Disposable; activate to whisper a single message to the rest of the cult." + invocation = "O bidai nabora se'sma!" + whispered = TRUE + delete_self = FALSE + +/obj/item/paper/talisman/communicate/invoke(mob/living/user) + // This is more or less a verbatim copy of the communicate rune + var/input = input(user, "Please choose a message to tell to the other acolytes.", "Voice of Blood", "") as null|text + if (!input || !CanInteract(user, physical_state)) + return + input = sanitize(input) + log_and_message_admins("used a communicate talisman to say '[input]'", usr) + cult.cult_speak(user, input) + qdel(src) diff --git a/code/game/gamemodes/cult/talismans/deafen.dm b/code/game/gamemodes/cult/talismans/deafen.dm new file mode 100644 index 00000000000..43f0f5c4418 --- /dev/null +++ b/code/game/gamemodes/cult/talismans/deafen.dm @@ -0,0 +1,20 @@ +/obj/item/paper/talisman/deafen + talisman_name = "Deafen" + talisman_desc = "Induces deafness in all nearby nonbelievers." + tome_desc = "Shorter range." + invocation = "Sti'kaliedir!" + +/obj/item/paper/talisman/deafen/invoke(mob/living/user) + user.visible_message( + SPAN_WARNING("Dust flows from \the [user]'s hands, and the world goes quiet..."), + SPAN_WARNING("The talisman in your hands turns to gray dust, deafening nearby nonbelievers."), + range = 1 + ) + var/list/affected = list() + for (var/mob/living/L in hearers(7, src)) + if (iscultist(L) || findNullRod(L)) + continue + affected.Add(L) + L.ear_deaf = max(30, L.ear_deaf) + to_chat(L, SPAN_DANGER("Your ears pop and the world goes quiet.")) + add_attack_logs(user, affected, "deafness talisman") diff --git a/code/game/gamemodes/cult/talismans/emp.dm b/code/game/gamemodes/cult/talismans/emp.dm new file mode 100644 index 00000000000..af106a3b4e1 --- /dev/null +++ b/code/game/gamemodes/cult/talismans/emp.dm @@ -0,0 +1,12 @@ +/obj/item/paper/talisman/emp + talisman_name = "Disable Technology" + talisman_desc = "Emits a strong electromagnetic pulse in a small radius, disabling or harming nearby electronics." + tome_desc = "Lower strength and shorter range." + invocation = "Ta'gh fara'qha fel d'amar det!" + +/obj/item/paper/talisman/emp/invoke(mob/living/user) + var/turf/T = get_turf(user) + to_chat(user, SPAN_WARNING("The talisman shudders in your hand as it swells with searing heat, then burns to dust.")) + playsound(user, 'sound/items/Welder.ogg', 50, TRUE) + empulse(T, 1, 1, 2, 3) + qdel(src) diff --git a/code/game/gamemodes/cult/talismans/hide_and_reveal.dm b/code/game/gamemodes/cult/talismans/hide_and_reveal.dm new file mode 100644 index 00000000000..e122c38912a --- /dev/null +++ b/code/game/gamemodes/cult/talismans/hide_and_reveal.dm @@ -0,0 +1,25 @@ +/obj/item/paper/talisman/hide_runes + talisman_name = "Hide Runes" + talisman_desc = "Veils all nearby runes from sight, turning them invisible until they are revealed." + tome_desc = "Shorter range." + invocation = "Kla'atu barada nikt'o!" + +/obj/item/paper/talisman/hide_runes/invoke(mob/living/user) + for (var/obj/effect/rune/R in range(2, user)) + if (R.invisibility < SEE_INVISIBLE_CULT) + R.invisibility = SEE_INVISIBLE_CULT + R.alpha = 150 + to_chat(user, SPAN_WARNING("Your talisman turns to gray dust, veiling the surrounding runes.")) + +/obj/item/paper/talisman/reveal_runes + talisman_name = "Reveal Runes" + talisman_desc = "Reveal all nearby hidden runes." + tome_desc = "Shorter range." + invocation = "Nikt'o barada kla'atu!" + +/obj/item/paper/talisman/reveal_runes/invoke(mob/living/user) + for (var/obj/effect/rune/R in range(2, user)) + if (R.invisibility == SEE_INVISIBLE_CULT) + R.invisibility = 0 + R.alpha = 255 + to_chat(user, SPAN_WARNING("Your talisman turns to red dust, revealing the surrounding runes.")) diff --git a/code/game/gamemodes/cult/talismans/stun.dm b/code/game/gamemodes/cult/talismans/stun.dm new file mode 100644 index 00000000000..e5ac38eeb61 --- /dev/null +++ b/code/game/gamemodes/cult/talismans/stun.dm @@ -0,0 +1,51 @@ +/obj/item/paper/talisman/stun + talisman_name = "Stun" + talisman_desc = "Forces concentrated energy into a struck target, immediately knocking them to the ground. Humans will be prevented from speaking for a time. This will be obvious to anyone nearby." + tome_desc = "Forces concentrated energy into a struck target, knocking them to the ground and preventing them from speaking. This is obvious to anyone nearby." + invocation = "Dream sign 'Evil Sealing Talisman'!" // I think this is a touhou reference, but I'm not sure - I kept it from the old implementation just in case + whispered = FALSE + +/obj/item/paper/talisman/stun/attack_self(mob/living/user) + if (iscultist(user)) + to_chat(user, SPAN_NOTICE("To use this talisman, attack someone with it while on Harm intent.")) + return + +/obj/item/paper/talisman/stun/attack(mob/living/carbon/T, mob/living/user) + if (iscultist(user) && user.a_intent == I_HURT) + if (invocation) + !whispered ? user.say(invocation) : user.whisper(invocation) + add_attack_logs(user, T, "stun talisman") + stun(user, T) + qdel(src) + return + return ..() + +/obj/item/paper/talisman/stun/proc/stun(mob/living/user, mob/living/target) + target.interact_message(user, + SPAN_DANGER("\The [user] thrusts \the [src] into \the [target]'s face!"), + SPAN_DANGER("\The [user] thrusts \the [src] into your face!"), + SPAN_DANGER("You invoke the talisman at \the [target]!") + ) + if (findNullRod(target)) + target.visible_message( + SPAN_DANGER("\The [target] is unaffected!"), + SPAN_DANGER("You feel a hot flash for a moment, but nothing else happens!") + ) + return + if (ishuman(target)) + var/mob/living/carbon/human/H = target + H.visible_message( + SPAN_DANGER("\The [H] crumples to the ground!"), + SPAN_DANGER("An immense force overwhelms your senses as you fall to the ground!") + ) + H.flash_eyes() + H.Weaken(25) + H.Stun(25) + H.silent = max(15, H.silent) + else + target.visible_message( + SPAN_DANGER("\The [target] freezes up!"), + SPAN_DANGER("An immense force overwhelms your senses as you freeze up!") + ) + target.Weaken(15) + target.Stun(15) diff --git a/code/game/gamemodes/cult/talismans/summon_tome.dm b/code/game/gamemodes/cult/talismans/summon_tome.dm new file mode 100644 index 00000000000..c67126fbd21 --- /dev/null +++ b/code/game/gamemodes/cult/talismans/summon_tome.dm @@ -0,0 +1,10 @@ +/obj/item/paper/talisman/summon_tome + talisman_name = "Summon Tome" + talisman_desc = "Manifests another copy of the Geometer's scripture." + tome_desc = "Ditto." + invocation = "N'ath reth sh'yro eth d'raggathnor!" + +/obj/item/paper/talisman/summon_tome/invoke(mob/living/user) + to_chat(user, SPAN_WARNING("You quietly congeal space into a blank book in your hand as the talisman's etchings slither into its pages.")) + user.drop_item() + user.put_in_active_hand(new /obj/item/arcane_tome(get_turf(src))) diff --git a/code/game/gamemodes/cult/talismans/supply.dm b/code/game/gamemodes/cult/talismans/supply.dm new file mode 100644 index 00000000000..4fc0b6df20b --- /dev/null +++ b/code/game/gamemodes/cult/talismans/supply.dm @@ -0,0 +1,70 @@ +/// This is a unique talisman given to roundstart cultists, and is the only source of soul stones and construct shells for them until they make an artificer. +/obj/item/paper/talisman/supply + talisman_name = "Supply" + talisman_desc = "An irreplaceable talisman densely packed with varying invocations, providing all the tools you need to start a new cult. Make sure you get a tome, if you don't have one already." + delete_self = FALSE + invocation = null + var/uses = 5 + +/obj/item/paper/talisman/supply/examine(mob/user) + . = ..() + if (iscultist(user) || isobserver(user)) + to_chat(user, SPAN_OCCULT("[uses] use\s remain\s.")) + +/obj/item/paper/talisman/supply/invoke(mob/living/user) + var/dat = "There are [uses] bloody runes on the parchment.
" + dat += "Please choose the chant to be imbued into the fabric of reality.
" + dat += "
" + dat += "N'ath reth sh'yro eth d'raggathnor! - Allows you to summon a new arcane tome.
" + dat += "Sas'so c'arta forbici! - Allows you to move to a random rune with a provided key word.
" + dat += "Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range.
" + dat += "Kla'atu barada nikt'o! - Allows you to conceal the runes you placed on the floor.
" + dat += "O bidai nabora se'sma! - Allows you to coordinate with others of your cult.
" + dat += "Fuu ma'jin! - Allows you to stun a person by attacking them with the talisman.
" + dat += "Sa tatha najin! - Allows you to summon armoured robes and an unholy blade
" + dat += "Kal om neth! - Summons a soul stone
" + dat += "Da a'ig osk! - Summons a construct shell for use with captured souls. It is too large to carry on your person.
" + var/datum/browser/popup = new(user, "supply_talisman", "Supply Talisman") + popup.set_content(dat) + popup.open() + +/obj/item/paper/talisman/supply/Topic(href, href_list) + var/mob/living/L = usr + if (QDELETED(src) || !iscultist(L) || L.incapacitated() || !in_range(src, L)) + return + var/atom/movable/atom_type + switch (href_list["rune"]) + if ("newtome") + atom_type = /obj/item/paper/talisman/summon_tome + if ("teleport") + var/word = input(L, "Choose a key word for the talisman. When used, it will teleport you to a random Teleport rune with the same key word.", name) as null|anything in cult.english_words + if (QDELETED(src) || QDELETED(L) || !iscultist(L) || !word) + return + var/obj/item/paper/talisman/teleport/T = new(get_turf(L)) + T.key_word = word + to_chat(L, SPAN_OCCULT("You etch the talisman into the fabric of reality with the key word \"[word]\".")) + if ("emp") + atom_type = /obj/item/paper/talisman/emp + if ("conceal") + atom_type = /obj/item/paper/talisman/hide_runes + if ("communicate") + atom_type = /obj/item/paper/talisman/communicate + if ("runestun") + atom_type = /obj/item/paper/talisman/stun + if ("armor") + atom_type = /obj/item/paper/talisman/armor + if ("soulstone") + atom_type = /obj/item/soulstone + if ("construct") + atom_type = /obj/structure/constructshell/cult + if (atom_type) + var/atom/movable/AM = new atom_type (get_turf(L)) + if (isitem(AM)) + L.put_in_hands(AM) + to_chat(L, SPAN_OCCULT("You etch [istype(AM, /obj/item/paper/talisman) ? "the talisman" : "\the [AM]"] into the fabric of reality.")) + uses-- + if (!uses) + to_chat(L, SPAN_WARNING("\The [src] dissolves into hot ash as the last of its power is used.")) + qdel(src) + return + invoke(L) diff --git a/code/game/gamemodes/cult/talismans/teleport.dm b/code/game/gamemodes/cult/talismans/teleport.dm new file mode 100644 index 00000000000..16f3a9e154a --- /dev/null +++ b/code/game/gamemodes/cult/talismans/teleport.dm @@ -0,0 +1,32 @@ +/obj/item/paper/talisman/teleport + talisman_name = "Teleport" + talisman_desc = "Teleports its invoker to the location of a random Teleport rune with the same keyword." + tome_desc = "Disposable; teleports only the user. The talisman's keyword will reflect that of the rune used to create it." + invocation = "Sas'so c'arta forbici!" + delete_self = FALSE + var/key_word = CULT_WORD_OTHER + +/obj/item/paper/talisman/teleport/examine(mob/user) + . = ..() + if (iscultist(user) || isobserver(user)) + . += SPAN_DANGER("This talisman's key word is \"[key_word]\".") + +/obj/item/paper/talisman/teleport/invoke(mob/living/user) + var/list/runes + for (var/obj/effect/rune/teleport/T in cult.all_runes) + if (T.key_word == key_word) + LAZYADD(runes, T) + if (!LAZYLEN(runes)) + to_chat(user, SPAN_WARNING("There are no existing Teleport runes with a key word of \"[key_word]\".")) + return + var/obj/effect/rune/teleport/T = pick(runes) + user.visible_message( + SPAN_WARNING("\The [user] dissolves into a cloud of black smoke!"), + SPAN_DANGER("You invoke the talisman...") + ) + user.forceMove(get_turf(T)) + user.visible_message( + SPAN_WARNING("\The [user] appears in a cloud of black smoke!"), + SPAN_DANGER("...and appear elsewhere.") + ) + qdel(src) diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index eb57f1d71ad..127f0156db9 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -861,7 +861,7 @@ var/global/list/all_objectives = list() if(target) explanation_text = "Sacrifice [target.name], the [target.assigned_role]. You will need the sacrifice rune (Hell blood join) and three acolytes to do so." /datum/objective/cult/sacrifice/check_completion() - return (target && cult && !cult.sacrificed.Find(target)) + return (target && cult && LAZYFIND(cult.sacrificed, target)) /datum/objective/rev/find_target() ..() @@ -896,4 +896,3 @@ var/global/list/all_objectives = list() rval = 2 return 0 return rval - diff --git a/code/game/magic/Uristrunes.dm b/code/game/magic/Uristrunes.dm index 8efea35a6ed..b8cceac8d2f 100644 --- a/code/game/magic/Uristrunes.dm +++ b/code/game/magic/Uristrunes.dm @@ -23,29 +23,29 @@ var/global/list/word_to_uristrune_table = null /proc/get_uristrune_cult(word1, word2, word3) var/animated - if((word1 == cultwords["travel"] && word2 == cultwords["self"]) \ - || (word1 == cultwords["join"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) \ - || (word1 == cultwords["hell"] && word2 == cultwords["join"] && word3 == cultwords["self"]) \ - || (word1 == cultwords["see"] && word2 == cultwords["blood"] && word3 == cultwords["hell"]) \ - || (word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["technology"]) \ - || (word1 == cultwords["travel"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) \ - || (word1 == cultwords["see"] && word2 == cultwords["hell"] && word3 == cultwords["join"]) \ - || (word1 == cultwords["blood"] && word2 == cultwords["join"] && word3 == cultwords["hell"]) \ - || (word1 == cultwords["hide"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) \ - || (word1 == cultwords["hell"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) \ - || (word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["travel"]) \ - || (word1 == cultwords["hell"] && word2 == cultwords["technology"] && word3 == cultwords["join"]) \ - || (word1 == cultwords["hell"] && word2 == cultwords["blood"] && word3 == cultwords["join"]) \ - || (word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["hide"]) \ - || (word1 == cultwords["destroy"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) \ - || (word1 == cultwords["travel"] && word2 == cultwords["technology"] && word3 == cultwords["other"]) \ - || (word1 == cultwords["join"] && word2 == cultwords["other"] && word3 == cultwords["self"]) \ - || (word1 == cultwords["hide"] && word2 == cultwords["other"] && word3 == cultwords["see"]) \ - || (word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["other"]) \ - || (word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) \ - || (word1 == cultwords["self"] && word2 == cultwords["other"] && word3 == cultwords["technology"]) \ - || (word1 == cultwords["travel"] && word2 == cultwords["other"]) \ - || (word1 == cultwords["join"] && word2 == cultwords["hide"] && word3 == cultwords["technology"]) ) + if((word1 == cult.english_words["travel"] && word2 == cult.english_words["self"]) \ + || (word1 == cult.english_words["join"] && word2 == cult.english_words["blood"] && word3 == cult.english_words["self"]) \ + || (word1 == cult.english_words["hell"] && word2 == cult.english_words["join"] && word3 == cult.english_words["self"]) \ + || (word1 == cult.english_words["see"] && word2 == cult.english_words["blood"] && word3 == cult.english_words["hell"]) \ + || (word1 == cult.english_words["destroy"] && word2 == cult.english_words["see"] && word3 == cult.english_words["technology"]) \ + || (word1 == cult.english_words["travel"] && word2 == cult.english_words["blood"] && word3 == cult.english_words["self"]) \ + || (word1 == cult.english_words["see"] && word2 == cult.english_words["hell"] && word3 == cult.english_words["join"]) \ + || (word1 == cult.english_words["blood"] && word2 == cult.english_words["join"] && word3 == cult.english_words["hell"]) \ + || (word1 == cult.english_words["hide"] && word2 == cult.english_words["see"] && word3 == cult.english_words["blood"]) \ + || (word1 == cult.english_words["hell"] && word2 == cult.english_words["travel"] && word3 == cult.english_words["self"]) \ + || (word1 == cult.english_words["blood"] && word2 == cult.english_words["see"] && word3 == cult.english_words["travel"]) \ + || (word1 == cult.english_words["hell"] && word2 == cult.english_words["technology"] && word3 == cult.english_words["join"]) \ + || (word1 == cult.english_words["hell"] && word2 == cult.english_words["blood"] && word3 == cult.english_words["join"]) \ + || (word1 == cult.english_words["blood"] && word2 == cult.english_words["see"] && word3 == cult.english_words["hide"]) \ + || (word1 == cult.english_words["destroy"] && word2 == cult.english_words["travel"] && word3 == cult.english_words["self"]) \ + || (word1 == cult.english_words["travel"] && word2 == cult.english_words["technology"] && word3 == cult.english_words["other"]) \ + || (word1 == cult.english_words["join"] && word2 == cult.english_words["other"] && word3 == cult.english_words["self"]) \ + || (word1 == cult.english_words["hide"] && word2 == cult.english_words["other"] && word3 == cult.english_words["see"]) \ + || (word1 == cult.english_words["destroy"] && word2 == cult.english_words["see"] && word3 == cult.english_words["other"]) \ + || (word1 == cult.english_words["destroy"] && word2 == cult.english_words["see"] && word3 == cult.english_words["blood"]) \ + || (word1 == cult.english_words["self"] && word2 == cult.english_words["other"] && word3 == cult.english_words["technology"]) \ + || (word1 == cult.english_words["travel"] && word2 == cult.english_words["other"]) \ + || (word1 == cult.english_words["join"] && word2 == cult.english_words["hide"] && word3 == cult.english_words["technology"]) ) animated = 1 else animated = 0 diff --git a/code/game/objects/items/weapons/mop_deploy.dm b/code/game/objects/items/weapons/mop_deploy.dm index cd66dc7654a..bbbaf36a164 100644 --- a/code/game/objects/items/weapons/mop_deploy.dm +++ b/code/game/objects/items/weapons/mop_deploy.dm @@ -73,4 +73,4 @@ host.pinned -= src host.embedded -= src host.drop_from_inventory(src) - spawn(1) if(!QDELETED(src)) qdel(src) \ No newline at end of file + spawn(1) if(!QDELETED(src)) qdel(src) diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm index 0977ef73c19..e40b390c910 100644 --- a/code/game/objects/items/weapons/weaponry.dm +++ b/code/game/objects/items/weapons/weaponry.dm @@ -44,12 +44,20 @@ M.visible_message("\The [user] waves \the [src] over \the [M]'s head.") return -/obj/item/nullrod/afterattack(atom/A, mob/user as mob, proximity) - if(!proximity) +/obj/item/nullrod/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + if (!proximity_flag) return - if (istype(A, /turf/simulated/floor)) - to_chat(user, "You hit the floor with the [src].") - call(/obj/effect/rune/proc/revealrunes)(src) + if (isfloor(target)) + user.visible_message( + SPAN_NOTICE("\The [user] taps \the [src] against \the [target]."), + SPAN_NOTICE("You tap \the [src] against \the [target].") + ) + for (var/obj/effect/rune/R in range(4, get_turf(target))) + if (R.invisibility == SEE_INVISIBLE_CULT) + R.invisibility = 0 + R.alpha = 255 + R.visible_message(SPAN_WARNING("\A [R] appears to the [dir2text(get_dir(R, target))]!")) + user.setClickCooldown(2 SECONDS) /obj/item/energy_net name = "energy net" diff --git a/code/modules/admin/admin_verb_lists.dm b/code/modules/admin/admin_verb_lists.dm index 90a33b5c2c2..6a3c30312b9 100644 --- a/code/modules/admin/admin_verb_lists.dm +++ b/code/modules/admin/admin_verb_lists.dm @@ -56,7 +56,6 @@ var/global/list/admin_verbs_admin = list( /client/proc/cmd_admin_direct_narrate, //send text directly to a player with no padding. Useful for narratives and fluff-text, /client/proc/cmd_admin_world_narrate, //sends text to all players with no padding, /client/proc/cmd_admin_create_centcom_report, - /client/proc/check_words, //displays cult-words, /client/proc/check_ai_laws, //shows AI and borg laws, /client/proc/rename_silicon, //properly renames silicons, /client/proc/manage_silicon_laws, // Allows viewing and editing silicon laws. , @@ -283,7 +282,6 @@ var/global/list/admin_verbs_hideable = list( /client/proc/admin_cancel_shuttle, /client/proc/cmd_admin_direct_narrate, /client/proc/cmd_admin_world_narrate, - /client/proc/check_words, /client/proc/play_local_sound, /client/proc/play_sound, /client/proc/play_server_sound, @@ -451,7 +449,6 @@ var/global/list/admin_verbs_event_manager = list( /client/proc/cmd_admin_direct_narrate, //send text directly to a player with no padding. Useful for narratives and fluff-text, /client/proc/cmd_admin_world_narrate, //sends text to all players with no padding, /client/proc/cmd_admin_create_centcom_report, - /client/proc/check_words, //displays cult-words, /client/proc/check_ai_laws, //shows AI and borg laws, /client/proc/rename_silicon, //properly renames silicons, /client/proc/manage_silicon_laws, // Allows viewing and editing silicon laws. , diff --git a/code/modules/antagonist/antagonist_create.dm b/code/modules/antagonist/antagonist_create.dm index ae4dc1a671c..df83c8ed4fb 100644 --- a/code/modules/antagonist/antagonist_create.dm +++ b/code/modules/antagonist/antagonist_create.dm @@ -101,7 +101,7 @@ /datum/antagonist/proc/greet(var/datum/mind/player) // Makes it harder to miss if you're alt-tabbed or not paying attention. if(antag_sound) - sound_to(player.current, sound(antag_sound)) + player.current.playsound_local(player.current.loc, antag_sound, 50, is_global = TRUE) window_flash(player.current.client) // Basic intro text. diff --git a/code/modules/antagonist/station/cultist.dm b/code/modules/antagonist/station/cultist.dm index e111513023f..279cfaf3b22 100644 --- a/code/modules/antagonist/station/cultist.dm +++ b/code/modules/antagonist/station/cultist.dm @@ -31,16 +31,38 @@ var/global/datum/antagonist/cultist/cult initial_spawn_target = 6 antaghud_indicator = "hudcultist" - var/allow_narsie = 1 + /// Whether or not the Tear Reality rune can be used. + var/allow_narsie = TRUE + /// The mind datum of the mob that this cult must sacrifice to fulfill their objective. var/datum/mind/sacrifice_target - var/list/startwords = list("blood","join","self","hell") - var/list/allwords = list("travel","self","see","hell","blood","join","tech","destroy", "other", "hide") - var/list/sacrificed = list() - var/list/harvested = list() + /// A list of all mobs that this cult has sacrificed. Uses lazylist macros. + var/list/sacrificed + /// A list of all non-cultists that have been killed by Nar-Sie (in the rare event that an admin spawns it or something.) Uses lazylist macros. + var/list/harvested + /// A list of all runes in the game world. Uses lazylist macros. + var/list/all_runes + + /** + * So here's how the cult vocabulary works: + * * There are two lists of words: one contains English words representing concepts ("blood", "other", "technology", etc) while the other are culty gibberish. Both of these lists have the same amount of total words in them. + * * At runtime, each cult word is correlated to a random English word representing its meaning. On one round the word "ego" might mean "technology", but on another it might mean "hell", and so on. + * * This list is populated as an associative list with each English word associated with its cult word counterpart. + * + * The word lists are found in `english_words` and `cult_words` on `/datum/antagonist/cultist`, and are populated from defines to avoid string copy-paste. + */ + var/list/vocabulary + var/list/english_words = list(CULT_WORD_BLOOD, CULT_WORD_DESTROY, CULT_WORD_HELL, CULT_WORD_HIDE, CULT_WORD_JOIN, CULT_WORD_OTHER, CULT_WORD_SELF, CULT_WORD_SEE, CULT_WORD_TECHNOLOGY, CULT_WORD_TRAVEL) + var/list/cult_words = list(CULT_WORD_BALAQ, CULT_WORD_CERTUM, CULT_WORD_EGO, CULT_WORD_GEERI, CULT_WORD_IRE, CULT_WORD_KARAZET, CULT_WORD_JATKAA, CULT_WORD_MGAR, CULT_WORD_NAHLIZET, CULT_WORD_VERI) /datum/antagonist/cultist/New() ..() cult = src + if (!LAZYLEN(vocabulary)) + var/list/gibberish = cult_words + for (var/eng in english_words) + var/culty = pick(gibberish) + LAZYSET(vocabulary, eng, culty) + gibberish -= culty /datum/antagonist/cultist/create_global_objectives() @@ -79,28 +101,6 @@ var/global/datum/antagonist/cultist/cult if(S && istype(S)) T.loc = S -/datum/antagonist/cultist/greet(var/datum/mind/player) - if(!..()) - return 0 - grant_runeword(player.current) - -/datum/antagonist/cultist/proc/grant_runeword(mob/living/carbon/human/cult_mob, var/word) - - if (!word) - if(startwords.len > 0) - word=pick(startwords) - startwords -= word - else - word = pick(allwords) - - // Ensure runes are randomized. - if(!cultwords["travel"]) - runerandom() - - var/wordexp = "[cultwords[word]] is [word]..." - to_chat(cult_mob, "You remember one thing from the dark teachings of your master... [wordexp]") - cult_mob.mind.store_memory("You remember that [wordexp]", 0, 0) - /datum/antagonist/cultist/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted) if(!..()) return 0 @@ -128,3 +128,8 @@ var/global/datum/antagonist/cultist/cult if(L && (L.imp_in == player.current)) return 0 return 1 + +/datum/antagonist/cultist/proc/cult_speak(mob/speaker, message) + for (var/mob/M in player_list) + if (iscultist(M) || isobserver(M)) + to_chat(M, SPAN_OCCULT("[speaker.GetVoice()][speaker.GetAltName()] intones, \"[message]\"")) diff --git a/code/modules/clothing/head/hood.dm b/code/modules/clothing/head/hood.dm index 61533fbbc2f..a6febc9207a 100644 --- a/code/modules/clothing/head/hood.dm +++ b/code/modules/clothing/head/hood.dm @@ -183,6 +183,18 @@ desc = "An old-fashioned fur-lined hood." icon_state = "vintagepark_hood" +/obj/item/clothing/head/hood/cult + name = "cult hood" + icon_state = "cult_hoodalt" + desc = "Chips of wet magmellite and strips of leathery textile are melded together into stiff cloth. Its surface is warm and shudders at the touch." + flags_inv = HIDEEARS | HIDEFACE | BLOCKHAIR + min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0 + armor = list(melee = 50, bullet = 30, laser = 50, energy = 80, bomb = 25, bio = 10, rad = 0) + +/obj/item/clothing/head/hood/cult/cultify() + return + // Explorer gear /obj/item/clothing/head/hood/explorer name = "explorer hood" diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index 99961f06f63..0f7a9a622b6 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -87,8 +87,8 @@ playsound(src, "clownstep", 20, 1) /obj/item/clothing/shoes/cult - name = "boots" - desc = "A pair of boots worn by the followers of an unknown god." + name = "cult boots" + desc = "Chips of wet magmellite and strips of leathery textile are melded together into stiff cloth. Its surface is warm and shudders at the touch." icon_state = "cult" item_state_slots = list(slot_r_hand_str = "cult", slot_l_hand_str = "cult") force = 2 diff --git a/code/modules/clothing/suits/hooded.dm b/code/modules/clothing/suits/hooded.dm index 89b00933655..c0d15a9f3f0 100644 --- a/code/modules/clothing/suits/hooded.dm +++ b/code/modules/clothing/suits/hooded.dm @@ -427,3 +427,18 @@ /obj/item/radio, /obj/item/pickaxe ) + +/obj/item/clothing/suit/storage/hooded/cult + name = "cult robes" + desc = "Chips of wet magmellite and strips of leathery textile are melded together into stiff cloth. Its surface is warm and shudders at the touch." + icon_state = "cultrobesalt" + origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEJUMPSUIT + hoodtype = /obj/item/clothing/head/hood/cult + siemens_coefficient = 0 + armor = list(melee = 50, bullet = 30, laser = 50, energy = 80, bomb = 25, bio = 10, rad = 0) + allowed = list(/obj/item/arcane_tome, /obj/item/melee/cultblade) + +/obj/item/clothing/suit/storage/hooded/cult/cultify() + return diff --git a/code/modules/examine/descriptions/weapons.dm b/code/modules/examine/descriptions/weapons.dm index f6f8a3b4ed6..d4bf13c3f69 100644 --- a/code/modules/examine/descriptions/weapons.dm +++ b/code/modules/examine/descriptions/weapons.dm @@ -87,6 +87,3 @@ description_antag = "The energy sword is a very strong melee weapon, capable of severing limbs easily, if they are targeted. It can also has a chance \ to block projectiles and melee attacks while it is on and being held. The sword can be toggled on or off by using it in your hand. While it is off, \ it can be concealed in your pocket or bag." - -/obj/item/melee/cultblade - description_antag = "This sword is a powerful weapon, capable of severing limbs easily, if they are targeted. Nonbelievers are unable to use this weapon." diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index 1aaff675688..2bf06fe432f 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -183,7 +183,7 @@ if(src.emagged) dat += "7. Access the Forbidden Lore Vault
" if(src.arcanecheckout) - new /obj/item/book/tome(src.loc) + new /obj/item/arcane_tome(src.loc) var/datum/gender/T = gender_datums[user.get_visible_gender()] to_chat(user, "Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a dusty old tome sitting on the desk. You don't really remember printing it.") user.visible_message("\The [user] stares at the blank screen for a few moments, [T.his] expression frozen in fear. When [T.he] finally awakens from it, [T.he] looks a lot older.", 2) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 71a6d2b623a..4a2120fd51c 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -151,7 +151,7 @@ return /mob/observer/dead/attackby(obj/item/W, mob/user) - if(istype(W,/obj/item/book/tome)) + if(istype(W,/obj/item/arcane_tome)) var/mob/observer/dead/M = src M.manifest(user) @@ -268,16 +268,6 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if(mind.current.key && copytext(mind.current.key,1,2)!="@") //makes sure we don't accidentally kick any clients to_chat(usr, "Another consciousness is in your body... it is resisting you.") return - if(mind.current.ajourn && mind.current.stat != DEAD) //check if the corpse is astral-journeying (it's client ghosted using a cultist rune). - var/found_rune - for(var/obj/effect/rune/R in mind.current.loc) //whilst corpse is alive, we can only reenter the body if it's on the rune - if(R && R.word1 == cultwords["hell"] && R.word2 == cultwords["travel"] && R.word3 == cultwords["self"]) // Found an astral journey rune. - found_rune = 1 - break - if(!found_rune) - to_chat(usr, "The astral cord that ties your body and your spirit has been severed. You are likely to wander the realm beyond until your body is finally dead and thus reunited with you.") - return - mind.current.ajourn=0 mind.current.key = key mind.current.teleop = null if(istype(mind.current.loc, /obj/structure/morgue)) diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 3a6ac91730e..acf43d528d6 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -1420,14 +1420,6 @@ see_in_dark = 8 if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO - if(seer==1) - var/obj/effect/rune/R = locate() in loc - if(R && R.word1 == cultwords["see"] && R.word2 == cultwords["hell"] && R.word3 == cultwords["join"]) - see_invisible = SEE_INVISIBLE_CULT - else - see_invisible = see_invisible_default - seer = 0 - if(!seedarkness) sight = species.get_vision_flags(src) see_in_dark = 8 @@ -1454,6 +1446,9 @@ see_in_dark = 8 if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO + if (seer) + see_invisible = SEE_INVISIBLE_OBSERVER + for(var/datum/modifier/M in modifiers) if(!isnull(M.vision_flags)) sight |= M.vision_flags diff --git a/code/modules/mob/living/silicon/ai/login.dm b/code/modules/mob/living/silicon/ai/login.dm index 57856a861f8..78de7d85f3b 100644 --- a/code/modules/mob/living/silicon/ai/login.dm +++ b/code/modules/mob/living/silicon/ai/login.dm @@ -9,4 +9,4 @@ if(multicam_on) end_multicam() src.view_core() - return \ No newline at end of file + return diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/harvester.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/harvester.dm index 93eef282283..b71baa0e0eb 100644 --- a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/harvester.dm +++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/harvester.dm @@ -36,9 +36,8 @@ construct_spells = list( /spell/aoe_turf/knock/harvester, /spell/targeted/construct_advanced/inversion_beam, - /spell/targeted/construct_advanced/agonizing_sphere, - /spell/rune_write + /spell/targeted/construct_advanced/agonizing_sphere ) /decl/mob_organ_names/harvester - hit_zones = list("cephalothorax", "eye", "carapace", "energy crystal", "mandible") \ No newline at end of file + hit_zones = list("cephalothorax", "eye", "carapace", "energy crystal", "mandible") diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 163be51b738..a82ec8ff0c7 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -73,8 +73,6 @@ var/exploit_record = "" var/exploit_addons = list() //Assorted things that show up at the end of the exploit_record list var/blinded = null - var/bhunger = 0 //Carbon - var/ajourn = 0 var/druggy = 0 //Carbon var/confused = 0 //Carbon var/antitoxs = null diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 36f313dfb87..b5f5376b4fa 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -34,6 +34,7 @@ var/rigged = 0 var/spam_flag = 0 var/age = 0 + var/can_write = TRUE var/last_modified_ckey var/was_maploaded = FALSE // This tracks if the paper was created on mapload. @@ -576,6 +577,9 @@ if(icon_state == "scrap") to_chat(usr, "\The [src] is too crumpled to write on.") return + if (!can_write) + to_chat(usr, SPAN_WARNING("Nothing you write on \the [src] seems to stick.")) + return var/obj/item/pen/robopen/RP = P if ( istype(RP) && RP.mode == 2 ) diff --git a/code/modules/reagents/reagents/dispenser.dm b/code/modules/reagents/reagents/dispenser.dm index c54360072b2..eb6f6be14ac 100644 --- a/code/modules/reagents/reagents/dispenser.dm +++ b/code/modules/reagents/reagents/dispenser.dm @@ -194,7 +194,7 @@ if(istype(O, /obj/item/book)) if(volume < 5) return - if(istype(O, /obj/item/book/tome)) + if(istype(O, /obj/item/arcane_tome)) to_chat(usr, "The solution does nothing. Whatever this is, it isn't normal ink.") return var/obj/item/book/affectedbook = O diff --git a/code/modules/spells/general/rune_write.dm b/code/modules/spells/general/rune_write.dm deleted file mode 100644 index 28710333954..00000000000 --- a/code/modules/spells/general/rune_write.dm +++ /dev/null @@ -1,175 +0,0 @@ -/spell/rune_write - name = "Scribe a Rune" - desc = "Let's you instantly manifest a working rune." - - school = "evocation" - charge_max = 100 - charge_type = Sp_RECHARGE - invocation_type = SpI_NONE - - spell_flags = CONSTRUCT_CHECK - - hud_state = "const_rune" - - smoke_amt = 1 - -/spell/rune_write/choose_targets(mob/user = usr) - return list(user) - -/spell/rune_write/cast(null, mob/user = usr) - if(!cultwords["travel"]) - runerandom() - var/list/runes = list("Teleport", "Teleport Other", "Spawn a Tome", "Change Construct Type", "Convert", "EMP", "Drain Blood", "See Invisible", "Resurrect", "Hide Runes", "Reveal Runes", "Astral Journey", "Manifest a Ghost", "Imbue Talisman", "Sacrifice", "Wall", "Free Cultist", "Summon Cultist", "Deafen", "Blind", "BloodBoil", "Communicate", "Stun") - var/r = input(user, "Choose a rune to scribe", "Rune Scribing") in runes //not cancellable. - var/obj/effect/rune/R = new /obj/effect/rune(user.loc) - if(istype(user.loc,/turf)) - var/area/A = get_area(user) - log_and_message_admins("created \an [r] rune at \the [A.name] - [user.loc.x]-[user.loc.y]-[user.loc.z].", user) - switch(r) - if("Teleport") - if(cast_check(1)) - var/beacon - if(user) - beacon = input(user, "Select the last rune", "Rune Scribing") in rnwords - R.word1=cultwords["travel"] - R.word2=cultwords["self"] - R.word3=beacon - R.check_icon() - if("Teleport Other") - if(cast_check(1)) - var/beacon - if(user) - beacon = input(user, "Select the last rune", "Rune Scribing") in rnwords - R.word1=cultwords["travel"] - R.word2=cultwords["other"] - R.word3=beacon - R.check_icon() - if("Spawn a Tome") - if(cast_check(1)) - R.word1=cultwords["see"] - R.word2=cultwords["blood"] - R.word3=cultwords["hell"] - R.check_icon() - if("Change Construct Type") - if(cast_check(1)) - R.word1=cultwords["hell"] - R.word2=cultwords["destroy"] - R.word3=cultwords["other"] - R.check_icon() - if("Convert") - if(cast_check(1)) - R.word1=cultwords["join"] - R.word2=cultwords["blood"] - R.word3=cultwords["self"] - R.check_icon() - if("EMP") - if(cast_check(1)) - R.word1=cultwords["destroy"] - R.word2=cultwords["see"] - R.word3=cultwords["technology"] - R.check_icon() - if("Drain Blood") - if(cast_check(1)) - R.word1=cultwords["travel"] - R.word2=cultwords["blood"] - R.word3=cultwords["self"] - R.check_icon() - if("See Invisible") - if(cast_check(1)) - R.word1=cultwords["see"] - R.word2=cultwords["hell"] - R.word3=cultwords["join"] - R.check_icon() - if("Resurrect") - if(cast_check(1)) - R.word1=cultwords["blood"] - R.word2=cultwords["join"] - R.word3=cultwords["hell"] - R.check_icon() - if("Hide Runes") - if(cast_check(1)) - R.word1=cultwords["hide"] - R.word2=cultwords["see"] - R.word3=cultwords["blood"] - R.check_icon() - if("Astral Journey") - if(cast_check(1)) - R.word1=cultwords["hell"] - R.word2=cultwords["travel"] - R.word3=cultwords["self"] - R.check_icon() - if("Manifest a Ghost") - if(cast_check(1)) - R.word1=cultwords["blood"] - R.word2=cultwords["see"] - R.word3=cultwords["travel"] - R.check_icon() - if("Imbue Talisman") - if(cast_check(1)) - R.word1=cultwords["hell"] - R.word2=cultwords["technology"] - R.word3=cultwords["join"] - R.check_icon() - if("Sacrifice") - if(cast_check(1)) - R.word1=cultwords["hell"] - R.word2=cultwords["blood"] - R.word3=cultwords["join"] - R.check_icon() - if("Reveal Runes") - if(cast_check(1)) - R.word1=cultwords["blood"] - R.word2=cultwords["see"] - R.word3=cultwords["hide"] - R.check_icon() - if("Wall") - if(cast_check(1)) - R.word1=cultwords["destroy"] - R.word2=cultwords["travel"] - R.word3=cultwords["self"] - R.check_icon() - if("Freedom") - if(cast_check(1)) - R.word1=cultwords["travel"] - R.word2=cultwords["technology"] - R.word3=cultwords["other"] - R.check_icon() - if("Cultsummon") - if(cast_check(1)) - R.word1=cultwords["join"] - R.word2=cultwords["other"] - R.word3=cultwords["self"] - R.check_icon() - if("Deafen") - if(cast_check(1)) - R.word1=cultwords["hide"] - R.word2=cultwords["other"] - R.word3=cultwords["see"] - R.check_icon() - if("Blind") - if(cast_check(1)) - R.word1=cultwords["destroy"] - R.word2=cultwords["see"] - R.word3=cultwords["other"] - R.check_icon() - if("BloodBoil") - if(cast_check(1)) - R.word1=cultwords["destroy"] - R.word2=cultwords["see"] - R.word3=cultwords["blood"] - R.check_icon() - if("Communicate") - if(cast_check(1)) - R.word1=cultwords["self"] - R.word2=cultwords["other"] - R.word3=cultwords["technology"] - R.check_icon() - if("Stun") - if(cast_check(1)) - R.word1=cultwords["join"] - R.word2=cultwords["hide"] - R.word3=cultwords["technology"] - R.check_icon() - else - to_chat(user, " You do not have enough space to write a proper rune.") - return diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi index cafc60896f7..7f70433f1b1 100644 Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi index ac398d21665..e203996bc1a 100644 Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ diff --git a/maps/submaps/surface_submaps/mountains/temple.dmm b/maps/submaps/surface_submaps/mountains/temple.dmm index f640496aaf8..3aac694a1a6 100644 --- a/maps/submaps/surface_submaps/mountains/temple.dmm +++ b/maps/submaps/surface_submaps/mountains/temple.dmm @@ -15,7 +15,7 @@ "o" = (/obj/structure/loot_pile/maint/technical,/obj/effect/decal/cleanable/cobweb2,/turf/simulated/floor/tiled/asteroid_steel,/area/submap/AbandonedTemple) "p" = (/turf/simulated/floor/carpet/turcarpet,/area/submap/AbandonedTemple) "q" = (/obj/structure/table/woodentable,/obj/item/flame/lighter/zippo/royal,/obj/item/clothing/mask/smokable/pipe,/turf/simulated/floor/carpet/turcarpet,/area/submap/AbandonedTemple) -"r" = (/obj/effect/rune,/obj/effect/decal/remains/human,/turf/simulated/floor/tiled/asteroid_steel,/area/submap/AbandonedTemple) +"r" = (/obj/effect/rune/mapgen,/obj/effect/decal/remains/human,/turf/simulated/floor/tiled/asteroid_steel,/area/submap/AbandonedTemple) "s" = (/obj/effect/decal/cleanable/blood,/turf/simulated/floor/tiled/asteroid_steel,/area/submap/AbandonedTemple) "t" = (/obj/machinery/artifact,/turf/simulated/floor/tiled/asteroid_steel,/area/submap/AbandonedTemple) "u" = (/obj/structure/bed/chair/comfy/brown{dir = 4},/obj/effect/decal/cleanable/blood,/turf/simulated/floor/carpet/turcarpet,/area/submap/AbandonedTemple) diff --git a/maps/submaps/surface_submaps/plains/lonehome.dmm b/maps/submaps/surface_submaps/plains/lonehome.dmm index a6074d7808f..8cc1de1f739 100644 --- a/maps/submaps/surface_submaps/plains/lonehome.dmm +++ b/maps/submaps/surface_submaps/plains/lonehome.dmm @@ -63,7 +63,7 @@ "tp" = (/obj/structure/flora/pottedplant/bamboo{pixel_y = 12},/obj/structure/table/bench/wooden,/turf/simulated/floor/wood,/area/submap/lonehome) "uc" = (/obj/structure/girder,/turf/simulated/floor,/area/submap/lonehome) "uh" = (/obj/structure/fence/cut/medium{dir = 4},/turf/simulated/floor/outdoors/grass/heavy,/area/submap/lonehome) -"um" = (/obj/structure/window/reinforced/tinted/frosted{dir = 8},/obj/structure/table/wooden_reinforced,/obj/item/book/tome,/turf/simulated/floor/wood,/area/submap/lonehome) +"um" = (/obj/structure/window/reinforced/tinted/frosted{dir = 8},/obj/structure/table/wooden_reinforced,/obj/item/arcane_tome,/turf/simulated/floor/wood,/area/submap/lonehome) "uo" = (/obj/item/material/shard,/turf/simulated/floor/outdoors/dirt,/area/submap/lonehome) "ur" = (/obj/structure/table/marble,/obj/item/material/knife/hook,/turf/simulated/floor,/area/submap/lonehome) "uw" = (/obj/item/seeds/random,/obj/item/reagent_containers/spray/cleaner,/obj/structure/table/wooden_reinforced,/turf/simulated/floor/wood,/area/submap/lonehome) @@ -104,7 +104,7 @@ "Gr" = (/obj/structure/table/bench/marble,/obj/structure/window/reinforced/polarized{id = "h_master"},/obj/structure/flora/pottedplant/bamboo{pixel_y = 7},/turf/simulated/floor/wood,/area/submap/lonehome) "GA" = (/obj/item/reagent_containers/food/snacks/ghostmuffin/poison,/obj/structure/table/sifwooden_reinforced,/turf/simulated/floor/wood,/area/submap/lonehome) "GS" = (/obj/structure/table/steel,/obj/item/stack/material/phoron{amount = 5},/obj/item/stack/material/phoron{amount = 5},/obj/fiftyspawner/wood,/obj/fiftyspawner/steel,/obj/random/gun/random/anomalous,/turf/simulated/floor,/area/submap/lonehome) -"Hc" = (/obj/effect/decal/cleanable/blood/gibs,/obj/structure/kitchenspike,/obj/effect/rune,/turf/simulated/floor,/area/submap/lonehome) +"Hc" = (/obj/effect/decal/cleanable/blood/gibs,/obj/structure/kitchenspike,/obj/effect/rune/mapgen,/turf/simulated/floor,/area/submap/lonehome) "Hu" = (/obj/item/frame/apc,/obj/machinery/light_switch{pixel_x = 11; pixel_y = 22},/obj/random/junk,/turf/simulated/floor,/area/submap/lonehome) "Im" = (/obj/structure/window/reinforced/tinted/frosted{dir = 8},/turf/simulated/floor/wood,/area/submap/lonehome) "Ji" = (/obj/structure/fence/cut/medium,/turf/simulated/floor/outdoors/grass/heavy,/area/submap/lonehome) diff --git a/maps/submaps/surface_submaps/wilderness/Chapel.dmm b/maps/submaps/surface_submaps/wilderness/Chapel.dmm index 254124fc5dc..1853bf06bf9 100644 --- a/maps/submaps/surface_submaps/wilderness/Chapel.dmm +++ b/maps/submaps/surface_submaps/wilderness/Chapel.dmm @@ -2,7 +2,7 @@ "ab" = (/turf/simulated/floor/outdoors/rocks,/area/template_noop) "ac" = (/turf/simulated/wall/log_sif,/area/submap/Chapel1) "ad" = (/obj/structure/lightpost,/turf/simulated/floor/outdoors/grass/sif/forest,/area/submap/Chapel1) -"ae" = (/obj/structure/closet/hydrant{pixel_y = 32},/obj/item/book/tome,/turf/simulated/floor/wood/sif,/area/submap/Chapel1) +"ae" = (/obj/structure/closet/hydrant{pixel_y = 32},/obj/item/arcane_tome,/turf/simulated/floor/wood/sif,/area/submap/Chapel1) "af" = (/obj/item/flame/candle/everburn,/turf/simulated/floor/outdoors/rocks,/area/template_noop) "ag" = (/obj/structure/bookcase,/turf/simulated/floor/wood/sif,/area/submap/Chapel1) "ah" = (/turf/simulated/floor/outdoors/grass/sif,/area/template_noop) diff --git a/maps/submaps/surface_submaps/wilderness/demonpool.dmm b/maps/submaps/surface_submaps/wilderness/demonpool.dmm index 75531ee1e9c..6babf1f16aa 100644 --- a/maps/submaps/surface_submaps/wilderness/demonpool.dmm +++ b/maps/submaps/surface_submaps/wilderness/demonpool.dmm @@ -146,7 +146,7 @@ /area/template_noop) "jG" = ( /obj/effect/spawner/gibs/human, -/obj/effect/rune, +/obj/effect/rune/mapgen, /turf/simulated/floor/cult, /area/submap/DemonPool) "jK" = ( @@ -556,7 +556,7 @@ /turf/simulated/floor/cult, /area/submap/DemonPool) "Qv" = ( -/obj/effect/rune, +/obj/effect/rune/mapgen, /turf/simulated/floor/cult, /area/submap/DemonPool) "Qz" = ( diff --git a/nano/images/uiBackground-Cult.png b/nano/images/uiBackground-Cult.png new file mode 100644 index 00000000000..e2caf8e41ac Binary files /dev/null and b/nano/images/uiBackground-Cult.png differ diff --git a/nano/js/nano_base_helpers.js b/nano/js/nano_base_helpers.js index e3258dd0656..44e14c5afd2 100644 --- a/nano/js/nano_base_helpers.js +++ b/nano/js/nano_base_helpers.js @@ -15,6 +15,21 @@ NanoBaseHelpers = function () return ''; }, + + // As above, but for culty stuff + spookyMode: function() { + $('body').css("color", "#9A9A9A"); + $('body').css("background-color", "#0D0C0C"); + $('body').css("background-image", "url('uiBackground-Cult.png')"); + $('body').css("background-position", "50% 0"); + $('body').css("background-repeat", "repeat-x"); + + $('#uiTitleText').css("color", "#777777"); + // Hide the NT icon completely + $('#uiTitleFluff').css("width", "0px"); + $('#uiTitleFluff').css("height", "0px"); + return ''; + }, // Generate a Byond link link: function( text, icon, parameters, status, elementClass, elementId) { @@ -129,12 +144,12 @@ NanoBaseHelpers = function () { showText = ''; } - + if (typeof difClass == 'undefined' || !difClass) { difClass = '' } - + if(typeof direction == 'undefined' || !direction) { direction = 'width' @@ -143,9 +158,9 @@ NanoBaseHelpers = function () { direction = 'height' } - + var percentage = Math.round((value - rangeMin) / (rangeMax - rangeMin) * 100); - + return '
' + showText + '
'; }, // Display DNA Blocks (for the DNA Modifier UI) @@ -203,7 +218,7 @@ NanoBaseHelpers = function () return html; } }; - + return { addHelpers: function () { @@ -217,14 +232,7 @@ NanoBaseHelpers = function () { NanoTemplate.removeHelper(helperKey); } - } + } } }; } (); - - - - - - - diff --git a/nano/templates/arcane_tome.tmpl b/nano/templates/arcane_tome.tmpl new file mode 100644 index 00000000000..424b372fc95 --- /dev/null +++ b/nano/templates/arcane_tome.tmpl @@ -0,0 +1,70 @@ +{{:helper.spookyMode()}} +{{:helper.link("Archives", null, { 'switch_tab': 1 }, data.tab == 1 ? 'selected' : null)}} +{{:helper.link("Runes", null, { 'switch_tab': 2 }, data.tab == 2 ? 'selected' : null)}} +

+{{if data.tab == 1}} + You are a cultist of Nar-Sie, the One who Sees, the Geometer of Blood. It has revealed to you the fact of Its existence by necessity, and graciously inducted you into Its followers so that you may better serve Its will. +

+ These archives serve as an out-of-character knowledge base for information about the cult, how your abilities work, and your ultimate goals. Refer back to it any time you are lost!
+

+ {{:helper.link("What is this book?", null, { 'switch_archive' : 1 }, data.archive == 1 ? 'selected' : null)}} + {{:helper.link("What are runes?", null, { 'switch_archive' : 2 }, data.archive == 2 ? 'selected' : null)}} + {{:helper.link("What are talismans?", null, { 'switch_archive' : 3 }, data.archive == 3 ? 'selected' : null)}} + {{:helper.link("What do I do?", null, { 'switch_archive' : 4 }, data.archive == 4 ? 'selected' : null)}} +

+ {{if data.archive == 1}} + You are holding an arcane tome, a book that contains the rites and scriptures of the Geometer. In addition to this informational section (the Archives), you can browse Runes within a dedicated section of the tome, allowing you to see their functions and +

+ The tome is an essential tool, and every cultist should have one unless they have a good reason not to. It's small enough to fit into pockets, webbing, and so on, so it should be easy to find a place to squeeze it. That being said, arcane tomes are strange things to nonbelievers, and security may become concerned if they see one, especially near runes; try and keep it concealed. +

+ If need be, your tome can serve as a weapon as well. Simply switch to Harm intent and attack your foes with it to deal burn damage. Naturally, doing so will be abundantly obvious to your target and everyone around. + {{else data.archive == 2}} + Runes are eldritch scrawlings, etched with precise shapes and words to invoke the power of Nar-Sie. They are the primary source of your power as a cultist, and they allow you to perform anomalous feats akin to magic. Some runes can be made into talismans that offer something akin to their abilities in a more portable form. +

+ There are many different types of rune, each with their own function and method of use. Runes are drawn in blood (or a close equivalent), meaning that you'll take a small amount of damage each time you create one. To invoke a rune, simply click on it with an empty hand. +

+ Runes are naturally very conspicuous, and you should be careful to keep them well-hidden to avoid drawing unnecessary suspicion. You can erase a rune by clicking on it with your tome - leaving no trace of its existence - or just clean it up like any regular spill. +

+ Notably, AIs cannot perceive runes. Instead, they just see them as a regular pool of blood. That's still conspicuous in its own right, mind you, so it's no excuse to cut corners on concealment! + {{else data.archive == 3}} + Runes are essential for success, but they can be difficult to lay down quickly and conveniently. Talismans alleviate this problem, serving essentially as portable runes. They're created by invoking the Imbue Talisman rune with a blank sheet of paper on top and a valid rune type nearby, at which point that rune will be etched onto the paper, turning it into a talisman that can be carried and activated at any time. +

+ The effects of talismans are usually equivalent to that of their respective rune, but weaker. Some, however, can be vastly different; the Stun talisman, for instance, causes a heavy stun to a single target struck with the talisman, instead of disorienting everyone nearby. Cultists starting aboard the station are equipped with a special Supply talisman that allows them to make certain talismans for free or to create soul shards and construct shells. +

+ Like runes, talismans are inherently conspicuous. In addition to appearing visibly different - with bloody markings on the surface - any attempts to write on the paper or alter its contents will invariably fail. Keep them hidden too! + {{else data.archive == 4}} + Depends! Stealth and infiltration are your best ally, so unless you've already been ousted, it's never a bad idea to simply go back to what you were doing as if nothing had happened. You can keep in touch with other cultists across distances and z-levels by using a Communicate rune, and coordinating in this manner is very important. +

+ When you feel inclined to introduce more people to the fold, you should go about it carefully. Conversion through the Convert rune is opt-in - the convertee can simply keep refusing to join the cult and will take increasing damage instead - so above all else, remember to roleplay and make it an interesting experience. Avoid just stunning someone and dragging them to the rune - lead up to it. Talk to them, manipulate them, be evil and weird. Make their conversion mean something, and it'll be more fun for everyone. + {{/if}} +{{else data.tab == 2}} + {{:helper.link(data.compact_mode ? "Switch to standard display" : "Switch to compact display", null, { "compact_mode" : 1 })}} +

+ {{if !data.compact_mode}} + {{for data.runes}} +
+
+ {{:helper.link(value.name, null, { "write_rune" : value.typepath })}} +
+
+ {{:value.shorthand}} +
+ {{if value.invoker_data}} +
Required invokers - {{:value.invoker_data}} + {{/if}} + {{if value.talisman}} +
Talisman effect - {{:value.talisman}} + {{/if}} +
+
+
+ {{/for}} + {{else}} +
+ Entries marked with an asterisk (*) can be made into a talisman.

+ {{for data.runes}} + {{:helper.link(value.name + (value.talisman ? " *" : "") + (value.invoker_data ? " (" + value.invoker_data + " invokers)" : ""), null, { "write_rune" : value.typepath })}} + {{/for}} +
+ {{/if}} +{{/if}} diff --git a/polaris.dme b/polaris.dme index be8eeacc525..d42768a78da 100644 --- a/polaris.dme +++ b/polaris.dme @@ -524,19 +524,51 @@ #include "code\game\gamemodes\changeling\powers\silence_sting.dm" #include "code\game\gamemodes\changeling\powers\transform.dm" #include "code\game\gamemodes\changeling\powers\visible_camouflage.dm" +#include "code\game\gamemodes\cult\arcane_tome.dm" #include "code\game\gamemodes\cult\construct_spells.dm" #include "code\game\gamemodes\cult\cult.dm" #include "code\game\gamemodes\cult\cult_items.dm" #include "code\game\gamemodes\cult\cult_structures.dm" #include "code\game\gamemodes\cult\hell_universe.dm" #include "code\game\gamemodes\cult\narsie.dm" -#include "code\game\gamemodes\cult\ritual.dm" -#include "code\game\gamemodes\cult\runes.dm" #include "code\game\gamemodes\cult\soulstone.dm" -#include "code\game\gamemodes\cult\talisman.dm" #include "code\game\gamemodes\cult\cultify\mob.dm" #include "code\game\gamemodes\cult\cultify\obj.dm" #include "code\game\gamemodes\cult\cultify\turf.dm" +#include "code\game\gamemodes\cult\runes\_rune.dm" +#include "code\game\gamemodes\cult\runes\_rune_icon.dm" +#include "code\game\gamemodes\cult\runes\armor.dm" +#include "code\game\gamemodes\cult\runes\astral_journey.dm" +#include "code\game\gamemodes\cult\runes\blind.dm" +#include "code\game\gamemodes\cult\runes\communicate.dm" +#include "code\game\gamemodes\cult\runes\convert.dm" +#include "code\game\gamemodes\cult\runes\deafen.dm" +#include "code\game\gamemodes\cult\runes\drain_blood.dm" +#include "code\game\gamemodes\cult\runes\emp.dm" +#include "code\game\gamemodes\cult\runes\free_cultist.dm" +#include "code\game\gamemodes\cult\runes\hide_and_reveal.dm" +#include "code\game\gamemodes\cult\runes\imbue_talisman.dm" +#include "code\game\gamemodes\cult\runes\raise_dead.dm" +#include "code\game\gamemodes\cult\runes\sacrifice.dm" +#include "code\game\gamemodes\cult\runes\see_invisible.dm" +#include "code\game\gamemodes\cult\runes\stun.dm" +#include "code\game\gamemodes\cult\runes\summon_cultist.dm" +#include "code\game\gamemodes\cult\runes\summon_tome.dm" +#include "code\game\gamemodes\cult\runes\tear_reality.dm" +#include "code\game\gamemodes\cult\runes\teleport.dm" +#include "code\game\gamemodes\cult\runes\wall.dm" +#include "code\game\gamemodes\cult\runes\weapon.dm" +#include "code\game\gamemodes\cult\talismans\_talisman.dm" +#include "code\game\gamemodes\cult\talismans\armor.dm" +#include "code\game\gamemodes\cult\talismans\blind.dm" +#include "code\game\gamemodes\cult\talismans\communicate.dm" +#include "code\game\gamemodes\cult\talismans\deafen.dm" +#include "code\game\gamemodes\cult\talismans\emp.dm" +#include "code\game\gamemodes\cult\talismans\hide_and_reveal.dm" +#include "code\game\gamemodes\cult\talismans\stun.dm" +#include "code\game\gamemodes\cult\talismans\summon_tome.dm" +#include "code\game\gamemodes\cult\talismans\supply.dm" +#include "code\game\gamemodes\cult\talismans\teleport.dm" #include "code\game\gamemodes\endgame\endgame.dm" #include "code\game\gamemodes\endgame\supermatter_cascade\blob.dm" #include "code\game\gamemodes\endgame\supermatter_cascade\portal.dm" @@ -3109,7 +3141,6 @@ #include "code\modules\spells\aoe_turf\conjure\construct.dm" #include "code\modules\spells\aoe_turf\conjure\forcewall.dm" #include "code\modules\spells\general\area_teleport.dm" -#include "code\modules\spells\general\rune_write.dm" #include "code\modules\spells\targeted\ethereal_jaunt.dm" #include "code\modules\spells\targeted\genetic.dm" #include "code\modules\spells\targeted\harvest.dm"