Cultists: [cult[1]]")
+ to_chat(M, "Constructs: [cult[2]]")
+
+
+/datum/cult_objectives/proc/current_sac_objective() //Return the current sacrifice objective datum, if any
+ if(cult_status == NARSIE_DEMANDS_SACRIFICE && length(presummon_objs))
+ var/datum/objective/sacrifice/current_obj = presummon_objs[length(presummon_objs)]
+ return current_obj
+ return FALSE
+
+/datum/cult_objectives/proc/is_sac_target(datum/mind/mind)
+ if(cult_status != NARSIE_DEMANDS_SACRIFICE || !length(presummon_objs))
+ return FALSE
+ var/datum/objective/sacrifice/current_obj = presummon_objs[length(presummon_objs)]
+ if(current_obj.target == mind)
+ return TRUE
+ return FALSE
+
+/datum/cult_objectives/proc/find_new_sacrifice_target(datum/mind/mind)
+ var/datum/objective/sacrifice/current_obj = presummon_objs[length(presummon_objs)]
+ if(current_obj.find_target())
+ for(var/datum/mind/cult_mind in SSticker.mode.cult)
+ if(cult_mind && cult_mind.current)
+ to_chat(cult_mind.current, "[SSticker.cultdat.entity_name] murmurs, Our goal is beyond your reach. Sacrifice [current_obj.target] instead...")
+ return TRUE
+ return FALSE
+
+/datum/cult_objectives/proc/succesful_sacrifice()
+ var/datum/objective/sacrifice/current_obj = presummon_objs[length(presummon_objs)]
+ current_obj.sacced = TRUE
+ sacrifices_done++
+ if(sacrifices_done >= sacrifices_required)
+ ready_to_summon()
else
- objectives += new_objective
-
- var/explanation
-
- switch(new_objective)
- if("convert")
- explanation = "We must increase our influence before we can summon [SSticker.cultdat.entity_name]. Convert [convert_target] crew members. Take it slowly to avoid raising suspicions."
- if("bloodspill")
- spilltarget = 100 + rand(0,GLOB.player_list.len * 3)
- explanation = "We must prepare this place for [SSticker.cultdat.entity_title1]'s coming. Spread blood and gibs over [spilltarget] of the Station's floor tiles."
- if("sacrifice")
- explanation = "We need to sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role], for [sacrifice_target.p_their()] blood is the key that will lead our master to this realm. You will need 3 cultists around a Sacrifice rune to perform the ritual."
-
- for(var/datum/mind/cult_mind in cult)
- if(cult_mind)
- to_chat(cult_mind.current, "You and your acolytes have completed your task, but this place requires yet more preparation!")
- to_chat(cult_mind.current, "Objective #[current_objective]: [explanation]")
- cult_mind.memory += "Objective #[current_objective]: [explanation]
"
-
- message_admins("New Cult Objective: [new_objective]")
- log_admin("New Cult Objective: [new_objective]")
-
- blood_check()//in case there are already enough blood covered tiles when the objective is given.
-
-/datum/game_mode/cult/proc/gtfo_phase()//YOU HAD ONE JOB
- var/explanation
- objectives += "survive"
- explanation = "Our knowledge must live on. Make sure at least [acolytes_needed] acolytes escape on the shuttle to spread their work on an another station."
- for(var/datum/mind/cult_mind in cult)
- if(cult_mind)
- to_chat(cult_mind.current, "You and your acolytes suddenly feel the urge to do your best, but survive!")
- to_chat(cult_mind.current, "Objective Survive: [explanation]")
- cult_mind.memory += "Objective Survive: [explanation]
"
-
-
-/datum/game_mode/cult/proc/second_phase()
- narsie_condition_cleared = 1
- var/explanation
-
- if(prob(40))//split the chance of this
- objectives += "eldergod"
- explanation = "Summon [SSticker.cultdat.entity_name] on the Station via the use of the Tear Reality rune. The veil is weak enough in [english_list(GLOB.summon_spots)] for the ritual to begin."
- else
- objectives += "slaughter"
- explanation = "Bring the Slaughter via the rune 'Call Forth The Slaughter'. The veil is weak enough in [english_list(GLOB.summon_spots)] for the ritual to begin."
+ var/datum/objective/sacrifice/obj_sac = new
+ if(obj_sac.find_target())
+ presummon_objs += obj_sac
+ for(var/datum/mind/cult_mind in SSticker.mode.cult)
+ if(cult_mind && cult_mind.current)
+ to_chat(cult_mind.current, "You and your acolytes have made progress, but there is more to do still before [SSticker.cultdat ? SSticker.cultdat.entity_title1 : "The Dark One"] can be summoned!")
+ to_chat(cult_mind.current, "Current goal: [obj_sac.explanation_text]")
+ else
+ ready_to_summon()
- for(var/datum/mind/cult_mind in cult)
- if(cult_mind)
+/datum/cult_objectives/proc/ready_to_summon()
+ cult_status = NARSIE_NEEDS_SUMMONING
+ for(var/datum/mind/cult_mind in SSticker.mode.cult)
+ if(cult_mind && cult_mind.current)
to_chat(cult_mind.current, "You and your acolytes have succeeded in preparing the station for the ultimate ritual!")
- to_chat(cult_mind.current, "Objective #[current_objective]: [explanation]")
- cult_mind.memory += "Objective #[current_objective]: [explanation]
"
-
-/datum/game_mode/cult/proc/third_phase()
- current_objective++
-
- sleep(10)
-
- var/last_objective = pick_bonus_objective()
-
- objectives += last_objective
-
- var/explanation
-
- switch(last_objective)
- if("harvest")
- explanation = "[SSticker.cultdat.entity_title1] hungers for their first meal of this never-ending day. Offer them [harvest_target] humanoids in sacrifice."
- if("hijack")
- explanation = "[SSticker.cultdat.entity_name] wishes for their troops to start the assault on CentCom immediately. Hijack the escape shuttle and don't let a single non-cultist board it."
- if("massacre")
- explanation = "[SSticker.cultdat.entity_name] wants to watch you as you massacre the remaining crew on the station (until less than [massacre_target] humans are left alive)."
-
- for(var/datum/mind/cult_mind in cult)
- if(cult_mind)
- to_chat(cult_mind.current, "Objective #[current_objective]: [explanation]")
- cult_mind.memory += "Objective #[current_objective]: [explanation]
"
-
- message_admins("Last Cult Objective: [last_objective]")
- log_admin("Last Cult Objective: [last_objective]")
-
-/datum/game_mode/cult/proc/get_possible_sac_targets()
- var/list/possible_sac_targets = list()
- for(var/mob/living/carbon/human/player in GLOB.player_list)
- if(player.mind && !is_convertable_to_cult(player.mind) && (player.stat != DEAD) && (!player.mind.offstation_role) )
- possible_sac_targets += player.mind
- if(!possible_sac_targets.len)
- //There are no living Unconvertables on the station. Looking for a Sacrifice Target among the ordinary crewmembers
- for(var/mob/living/carbon/human/player in GLOB.player_list)
- if(is_secure_level(player.z) || player.mind.offstation_role) //We can't sacrifice people that are on the centcom z-level or offstation roles
+ to_chat(cult_mind.current, "Current goal: [obj_summon.explanation_text]")
+
+/datum/cult_objectives/proc/succesful_summon()
+ cult_status = NARSIE_HAS_RISEN
+ obj_summon.summoned = TRUE
+
+/datum/cult_objectives/proc/narsie_death()
+ cult_status = NARSIE_HAS_FALLEN
+ obj_summon.killed = TRUE
+
+//Objectives
+
+/datum/objective/servecult //Given to cultists on conversion/roundstart
+ explanation_text = "Assist your fellow cultists and Tear the Veil! (Use the Study Veil action to check your progress.)"
+ completed = TRUE
+
+/datum/objective/sacrifice
+ var/sacced = FALSE
+ explanation_text = "Sacrifice a crewmember in order to prepare the summoning."
+
+/datum/objective/sacrifice/check_completion()
+ return sacced || completed
+
+/datum/objective/sacrifice/find_target()
+ var/list/target_candidates = list()
+ for(var/mob/living/carbon/human/H in GLOB.player_list)
+ if(is_admin_level(H.z)) //We can't sacrifice people that are on the centcom z-level
+ continue
+ if(H.mind && !is_convertable_to_cult(H.mind) && (H.stat != DEAD) && (H.mind.offstation_role != TRUE))
+ target_candidates += H.mind
+ if(!length(target_candidates)) //There are no living unconvertables on the station. Looking for a Sacrifice Target among the ordinary crewmembers
+ for(var/mob/living/carbon/human/H in GLOB.player_list)
+ if(is_admin_level(H.z)) //We can't sacrifice people that are on the centcom z-level
continue
- if(player.mind && !(player.mind in cult) && (player.stat != DEAD))//make DAMN sure they are not dead
- possible_sac_targets += player.mind
- return possible_sac_targets
-
-// Handles the updating of sacrifice objectives after the sacrifice target goes to cryo and ghosts
-/datum/game_mode/cult/proc/update_sac_objective(previous_target, previous_role)
- for(var/datum/mind/cult_mind in cult)
- if(cult_mind)
- var/updated_memory = cult_mind.memory
- updated_memory = replacetext("[cult_mind.memory]", "[previous_target]", "[sacrifice_target]")
- updated_memory = replacetext("[updated_memory]", "[previous_role]", "[sacrifice_target.assigned_role]")
- cult_mind.memory = updated_memory
-
-
-/datum/game_mode/cult/proc/pick_objective()
- var/list/possible_objectives = list()
-
- if(!spilled_blood && (bloody_floors.len < spilltarget))
- possible_objectives |= "bloodspill"
-
- if(!sacrificed.len)
- var/list/possible_targets = get_possible_sac_targets()
- if(possible_targets.len > 0)
- sacrifice_target = pick(possible_targets)
- possible_objectives |= "sacrifice"
- else
- log_runtime(EXCEPTION("Didn't find a suitable sacrifice target...what the hell? Shout at a coder."))
-
- if(!mass_convert)
- var/living_crew = 0
- var/living_cultists = 0
- for(var/mob/living/L in GLOB.player_list)
- if(L.stat != DEAD)
- if(L.mind in cult)
- living_cultists++
- else
- if(istype(L, /mob/living/carbon/human))
- living_crew++
-
- var/total = living_crew + living_cultists
-
- if((living_cultists * 2) < total)
- if(total < 15)
- message_admins("There are [total] players, too little for the mass convert objective!")
- log_admin("There are [total] players, too little for the mass convert objective!")
- else
- possible_objectives |= "convert"
- convert_target = rand(9,15)
-
- if(!possible_objectives.len)//No more possible objectives, time to summon Nar-Sie
- message_admins("No suitable objectives left! Nar-Sie objective unlocked.")
- log_admin("No suitable objectives left! Nar-Sie objective unlocked.")
- var/lastbit
- if(prob(50))
- lastbit = "eldergod"
- else
- lastbit = "slaughter"
- return lastbit
- else
- return pick(possible_objectives)
-
-/datum/game_mode/cult/proc/pick_bonus_objective()//did we summon demons? TIME FOR THE BONUS STAGE
- var/list/possible_objectives = list()
-
- var/living_crew = 0
- for(var/mob/living/carbon/C in GLOB.player_list)
- if(C.stat != DEAD)
- if(!(C.mind in cult))
- var/turf/T = get_turf(C)
- if(is_station_level(T.z)) //we're only interested in the remaining humans on the station
- living_crew++
-
- if(living_crew > 5)
- possible_objectives |= "massacre"
-
- if(living_crew > 10)
- possible_objectives |= "harvest"
-
- possible_objectives |= "hijack" //we need at least one objective guarranted to fire
-
- return pick(possible_objectives)
-
-/datum/game_mode/cult/proc/bonus_check()
- switch(objectives[current_objective])
- if("harvest")
- if(harvested >= harvest_target)
- bonus = 1
-
- if("hijack")
- for(var/mob/living/L in GLOB.player_list)
- if(L.stat != DEAD && !(L.mind in cult))
- var/area/A = get_area(L)
- if(is_type_in_list(A.loc, GLOB.centcom_areas))
- escaped_shuttle++
- if(!escaped_shuttle)
- bonus = 1
-
- if("massacre")
- for(var/mob/living/carbon/C in GLOB.player_list)
- if(C.stat != DEAD && !(C.mind in cult))
- var/turf/T = get_turf(C)
- if(is_station_level(T.z)) //we're only interested in the remaining humans on the station
- survivors++
- if(survivors < massacre_target)
- bonus = 1
+ if(H.mind && !iscultist(H) && (H.stat != DEAD) && (H.mind.offstation_role != TRUE))
+ target_candidates += H.mind
+ if(length(target_candidates))
+ target = pick(target_candidates)
+ explanation_text = "Sacrifice [target], the [target.assigned_role] via invoking an Offer rune with [target.p_their()] body or brain on it and three acolytes around it."
+ return TRUE
+ message_admins("Cult Sacrifice: Could not find unconvertible or convertible target. Nar'Sie summoning unlocked!")
+ return FALSE
+
+
+/datum/objective/eldergod
+ var/summoned = FALSE
+ var/killed = FALSE
+ var/list/summon_spots = list()
+
+/datum/objective/eldergod/New()
+ ..()
+ find_summon_locations()
+
+/datum/objective/eldergod/proc/find_summon_locations(reroll = FALSE)
+ if(reroll)
+ summon_spots = new()
+ var/sanity = 0
+ while(length(summon_spots) < SUMMON_POSSIBILITIES && sanity < 100)
+ var/area/summon = pick(return_sorted_areas() - summon_spots)
+ var/valid_spot = FALSE
+ if(summon && is_station_level(summon.z) && summon.valid_territory) // Check if there's a turf that you can walk on, if not it's not valid
+ for(var/turf/T in get_area_turfs(summon))
+ if(!T.density)
+ var/clear = TRUE
+ for(var/obj/O in T)
+ if(O.density)
+ clear = FALSE
+ break
+ if(clear)
+ valid_spot = TRUE
+ break
+ if(valid_spot)
+ summon_spots += summon
+ sanity++
+ explanation_text = "Summon [SSticker.cultdat ? SSticker.cultdat.entity_name : "your god"] by invoking the rune 'Tear Veil' with 9 cultists, constructs, or summoned ghosts on it.\
+ \nThe summoning can only be accomplished in [english_list(summon_spots)] - where the veil is weak enough for the ritual to begin."
+
+
+/datum/objective/eldergod/check_completion()
+ if(killed)
+ return NARSIE_HAS_FALLEN // You failed so hard that even the code went backwards.
+ return summoned || completed
diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm
index 17ae54591aaaf..2127a89738543 100644
--- a/code/game/gamemodes/cult/cult_structures.dm
+++ b/code/game/gamemodes/cult/cult_structures.dm
@@ -1,37 +1,42 @@
-//Noncult
/obj/structure/cult
- density = 1
- anchored = 1
+ density = TRUE
+ anchored = TRUE
+ layer = BELOW_OBJ_LAYER
icon = 'icons/obj/cult.dmi'
+ light_power = 2
//Noncult As we may have this on maps
/obj/structure/cult/altar
name = "Altar"
- desc = "A bloodstained altar dedicated to Nar-Sie"
+ desc = "A bloodstained altar."
icon_state = "altar"
/obj/structure/cult/forge
name = "Daemon forge"
- desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie"
+ desc = "A forge used in crafting unholy armors and weapons."
icon_state = "forge"
+ light_range = 2
+ light_color = LIGHT_COLOR_LAVA
/obj/structure/cult/pylon
name = "Pylon"
- desc = "A floating crystal that hums with an unearthly energy"
+ desc = "A floating crystal that hums with an unearthly energy."
icon_state = "pylon"
- light_range = 5
- light_color = "#3e0000"
+ light_range = 1.5
+ light_color = LIGHT_COLOR_RED
/obj/structure/cult/archives
name = "Desk"
- desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl"
+ desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl."
icon_state = "archives"
+ light_range = 1.5
+ light_color = LIGHT_COLOR_FIRE
//Cult versions cuase fuck map conflicts
/obj/structure/cult/functional
max_integrity = 100
var/cooldowntime = 0
- var/death_message = "The structure falls apart." //The message shown when the structure is destroyed
+ var/death_message = "The structure falls apart." //The message shown when the structure is destroyed
var/death_sound = 'sound/items/bikehorn.ogg'
var/heathen_message = "You're a huge nerd, go away. Also, a coder forgot to put a message here."
var/selection_title = "Oops"
@@ -42,20 +47,19 @@
/obj/structure/cult/functional/obj_destruction()
visible_message(death_message)
- playsound(src, death_sound, 50, 1)
+ playsound(src, death_sound, 50, TRUE)
..()
/obj/structure/cult/functional/examine(mob/user)
. = ..()
if(iscultist(user) && cooldowntime > world.time)
- . += "The magic in [src] is weak, it will be ready to use again in [getETA()]."
- . += "\The [src] is [anchored ? "":"not "]secured to the floor."
+ . += "The magic in [src] is weak, it will be ready to use again in [get_ETA()]."
+ . += "[src] is [anchored ? "":"not "]secured to the floor."
-/obj/structure/cult/functional/attackby(obj/I, mob/user, params)
- if(istype(I, /obj/item/tome) && iscultist(user))
+/obj/structure/cult/functional/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/melee/cultblade/dagger) && iscultist(user))
anchored = !anchored
- to_chat(user, "You [anchored ? "":"un"]secure \the [src] [anchored ? "to":"from"] the floor.")
- playsound(loc, 'sound/hallucinations/wail.ogg', 75, 1)
+ to_chat(user, "You [anchored ? "":"un"]secure [src] [anchored ? "to":"from"] the floor.")
if(!anchored)
icon_state = SSticker.cultdat?.get_icon("[initial(icon_state)]_off")
else
@@ -71,39 +75,64 @@
to_chat(user, "You cannot seem to manipulate this structure with your bulky hands!")
return
if(!anchored)
- to_chat(user, "You need to anchor [src] to the floor with a tome first.")
+ to_chat(user, "You need to anchor [src] to the floor with a dagger first.")
return
if(cooldowntime > world.time)
- to_chat(user, "The magic in [src] is weak, it will be ready to use again in [getETA()].")
+ to_chat(user, "The magic in [src] is weak, it will be ready to use again in [get_ETA()].")
return
var/choice = input(user, selection_prompt, selection_title) as null|anything in choosable_items
var/pickedtype = choosable_items[choice]
if(pickedtype && Adjacent(user) && src && !QDELETED(src) && !user.incapacitated() && cooldowntime <= world.time)
cooldowntime = world.time + creation_delay
var/obj/item/N = new pickedtype(get_turf(src))
- to_chat(user, replacetext("[creation_message]", "%ITEM%", "[N]"))
-
-/obj/structure/cult/functional/proc/getETA()
- var/time = (cooldowntime - world.time)/600
- var/eta = "[round(time, 1)] minutes"
- if(time <= 1)
- time = (cooldowntime - world.time)*0.1
- eta = "[round(time, 1)] seconds"
- return eta
+ to_chat(user, replacetext("[creation_message]", "%ITEM%", "[N.name]"))
+
+/**
+ * Returns the cooldown time in minutes and seconds
+ */
+/obj/structure/cult/functional/proc/get_ETA()
+ var/time = cooldowntime - world.time
+ var/minutes = round(time / 600)
+ var/seconds = round(time * 0.1, 1)
+ var/message
+ if(minutes)
+ message = "[minutes] minute\s"
+ seconds = seconds - (60 * minutes)
+ if(seconds) // To avoid '2 minutes, 0 seconds.'
+ message += "[minutes ? ", " : ""][seconds] second\s"
+ return message
+
+/obj/structure/cult/functional/cult_conceal()
+ density = FALSE
+ visible_message("[src] fades away.")
+ invisibility = INVISIBILITY_OBSERVER
+ alpha = 100 //To help ghosts distinguish hidden objs
+ light_range = 0
+ light_power = 0
+ update_light()
+
+/obj/structure/cult/functional/cult_reveal()
+ density = initial(density)
+ invisibility = 0
+ visible_message("[src] suddenly appears!")
+ alpha = initial(alpha)
+ light_range = initial(light_range)
+ light_power = initial(light_power)
+ update_light()
/obj/structure/cult/functional/altar
name = "altar"
desc = "A bloodstained altar dedicated to a cult."
icon_state = "altar"
max_integrity = 150 //Sturdy
- death_message = "The altar breaks into splinters, releasing a cascade of spirits into the air!"
+ death_message = "The altar breaks into splinters, releasing a cascade of spirits into the air!"
death_sound = 'sound/effects/altar_break.ogg'
heathen_message = "There is a foreboding aura to the altar and you want nothing to do with it."
selection_prompt = "You study the rituals on the altar..."
selection_title = "Altar"
- creation_message = "You kneel before the altar and your faith is rewarded with an %ITEM%!"
- choosable_items = list("Eldritch Whetstone"= /obj/item/whetstone/cult, "Zealot's Blindfold" = /obj/item/clothing/glasses/night/cultblind, \
- "Flask of Unholy Water" = /obj/item/reagent_containers/food/drinks/bottle/unholywater, "Cultist Dagger" = /obj/item/melee/cultblade/dagger)
+ creation_message = "You kneel before the altar and your faith is rewarded with a %ITEM%!"
+ choosable_items = list("Eldritch Whetstone" = /obj/item/whetstone/cult, "Flask of Unholy Water" = /obj/item/reagent_containers/food/drinks/bottle/unholywater,
+ "Construct Shell" = /obj/structure/constructshell)
/obj/structure/cult/functional/altar/New()
. = ..()
@@ -113,15 +142,17 @@
name = "daemon forge"
desc = "A forge used in crafting the unholy weapons used by the armies of a cult."
icon_state = "forge"
+ light_range = 2
+ light_color = LIGHT_COLOR_LAVA
max_integrity = 300 //Made of metal
- death_message = "The forge falls apart, its lava cooling and winking away!"
+ death_message = "The forge falls apart, its lava cooling and winking away!"
death_sound = 'sound/effects/forge_destroy.ogg'
heathen_message = "Your hand feels like it's melting off as you try to touch the forge."
selection_prompt = "You study the schematics etched on the forge..."
selection_title = "Forge"
- creation_message = "You work the forge as dark knowledge guides your hands, creating %ITEM%!"
- choosable_items = list("Shielded Robe" = /obj/item/clothing/suit/hooded/cultrobes/cult_shield, "Flagellant's Robe" = /obj/item/clothing/suit/hooded/cultrobes/flagellant_robe, \
- "Cultist Hardsuit" = /obj/item/storage/box/cult)
+ creation_message = "You work the forge as dark knowledge guides your hands, creating a %ITEM%!"
+ choosable_items = list("Shielded Robe" = /obj/item/clothing/suit/hooded/cultrobes/cult_shield, "Flagellant's Robe" = /obj/item/clothing/suit/hooded/cultrobes/flagellant_robe,
+ "Mirror Shield" = /obj/item/shield/mirror)
/obj/structure/cult/functional/forge/New()
. = ..()
@@ -131,45 +162,48 @@
if(istype(I, /obj/item/grab))
var/obj/item/grab/G = I
if(!iscarbon(G.affecting))
- to_chat(user, "You may only dunk carbon-based creatures!")
- return 0
+ return FALSE
if(G.affecting == LAVA_PROOF)
to_chat(user, "[G.affecting] is immune to lava!")
- return 0
+ return FALSE
if(G.affecting.stat == DEAD)
to_chat(user, "[G.affecting] is dead!")
- return 0
+ return FALSE
var/mob/living/carbon/human/C = G.affecting
- C.visible_message("[user] dunks [C]'s face into [src]'s lava!", \
+ var/obj/item/organ/external/head/head = C.get_organ("head")
+ if(!head)
+ to_chat(user, "[C] has no head!")
+ return FALSE
+
+ C.visible_message("[user] dunks [C]'s face into [src]'s lava!",
"[user] dunks your face into [src]'s lava!")
- if(!C.stat)
- C.emote("scream")
+ C.emote("scream")
+ C.apply_damage(30, BURN, "head") // 30 fire damage because it's FUCKING LAVA
+ head.disfigure() // Your face is unrecognizable because it's FUCKING LAVA
+ C.UpdateDamageIcon()
+ add_attack_logs(user, C, "Lava-dunked into [src]")
user.changeNext_move(CLICK_CD_MELEE)
- var/obj/item/organ/external/head/head = C.get_organ("head")
- if(head)
- C.apply_damage(30, BURN, "head") //30 fire damage because it's FUCKING LAVA
- head.disfigure() //Your face is unrecognizable because it's FUCKING LAVA
- return 1
+ return TRUE
return ..()
GLOBAL_LIST_INIT(blacklisted_pylon_turfs, typecacheof(list(
- /turf/simulated/floor/engine/cult,
- /turf/space,
- /turf/simulated/floor/plating/lava,
- /turf/simulated/floor/chasm,
- /turf/simulated/wall/cult,
- /turf/simulated/wall/cult/artificer,
- /turf/unsimulated/wall
+ /turf/simulated/floor/engine/cult,
+ /turf/space,
+ /turf/simulated/floor/plating/lava,
+ /turf/simulated/floor/chasm,
+ /turf/simulated/wall/cult,
+ /turf/simulated/wall/cult/artificer,
+ /turf/unsimulated/wall
)))
/obj/structure/cult/functional/pylon
name = "pylon"
desc = "A floating crystal that slowly heals those faithful to a cult."
icon_state = "pylon"
- light_range = 5
- light_color = "#3e0000"
+ light_range = 1.5
+ light_color = LIGHT_COLOR_RED
max_integrity = 50 //Very fragile
- death_message = "The pylon's crystal vibrates and glows fiercely before violently shattering!"
+ death_message = "The pylon's crystal vibrates and glows fiercely before violently shattering!"
death_sound = 'sound/effects/pylon_shatter.ogg'
var/heal_delay = 30
@@ -190,24 +224,39 @@ GLOBAL_LIST_INIT(blacklisted_pylon_turfs, typecacheof(list(
/obj/structure/cult/functional/pylon/Destroy()
STOP_PROCESSING(SSobj, src)
- return ..()
+ ..()
+
+/obj/structure/cult/functional/pylon/cult_conceal()
+ STOP_PROCESSING(SSobj, src)
+ ..()
+
+/obj/structure/cult/functional/pylon/cult_reveal()
+ START_PROCESSING(SSobj, src)
+ ..()
/obj/structure/cult/functional/pylon/process()
if(!anchored)
return
+
if(last_heal <= world.time)
last_heal = world.time + heal_delay
for(var/mob/living/L in range(5, src))
- if(iscultist(L) || iswizard(L) || istype(L, /mob/living/simple_animal/shade) || istype(L, /mob/living/simple_animal/hostile/construct))
+ if(iscultist(L) || iswizard(L) || isshade(L) || isconstruct(L))
if(L.health != L.maxHealth)
new /obj/effect/temp_visual/heal(get_turf(src), "#960000")
+
if(ishuman(L))
L.heal_overall_damage(1, 1, TRUE, FALSE, TRUE)
- if(istype(L, /mob/living/simple_animal/shade) || istype(L, /mob/living/simple_animal/hostile/construct))
+
+ else if(isshade(L) || isconstruct(L))
var/mob/living/simple_animal/M = L
if(M.health < M.maxHealth)
M.adjustHealth(-1)
- if(last_corrupt <= world.time)
+
+ if(ishuman(L) && L.blood_volume < BLOOD_VOLUME_NORMAL)
+ L.blood_volume += 1
+
+ if(!is_station_level(z) && last_corrupt <= world.time) //Pylons only convert tiles on offstation bases to help hide onstation cults from meson users
var/list/validturfs = list()
var/list/cultturfs = list()
for(var/T in circleviewturfs(src, 5))
@@ -234,23 +283,23 @@ GLOBAL_LIST_INIT(blacklisted_pylon_turfs, typecacheof(list(
else
// Are we in space or something? No cult turfs or
// convertable turfs?
- last_corrupt = world.time + corrupt_delay*2
-
-
+ last_corrupt = world.time + corrupt_delay * 2
/obj/structure/cult/functional/archives
name = "archives"
desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl."
icon_state = "archives"
+ light_range = 1.5
+ light_color = LIGHT_COLOR_FIRE
max_integrity = 125 //Slightly sturdy
- death_message = "The desk breaks apart, its books falling to the floor."
+ death_message = "The desk breaks apart, its books falling to the floor."
death_sound = 'sound/effects/wood_break.ogg'
heathen_message = "What do you hope to seek?"
selection_prompt = "You flip through the black pages of the archives..."
selection_title = "Archives"
- creation_message = "You invoke the dark magic of the tomes creating %ITEM%!"
- choosable_items = list("Supply Talisman" = /obj/item/paper/talisman/supply/weak, "Shuttle Curse" = /obj/item/shuttle_curse, \
- "Veil Shifter" = /obj/item/cult_shift)
+ creation_message = "You invoke the dark magic of the tomes creating a %ITEM%!"
+ choosable_items = list("Shuttle Curse" = /obj/item/shuttle_curse, "Zealot's Blindfold" = /obj/item/clothing/glasses/hud/health/night/cultblind,
+ "Veil Shifter" = /obj/item/cult_shift) //Add void torch to veil shifter spawn
/obj/structure/cult/functional/archives/New()
. = ..()
@@ -258,11 +307,11 @@ GLOBAL_LIST_INIT(blacklisted_pylon_turfs, typecacheof(list(
/obj/effect/gateway
name = "gateway"
- desc = "You're pretty sure that abyss is staring back"
+ desc = "You're pretty sure that the abyss is staring back"
icon = 'icons/obj/cult.dmi'
icon_state = "hole"
- density = 1
- anchored = 1.0
+ density = TRUE
+ anchored = TRUE
/obj/effect/gateway/singularity_act()
return
@@ -275,17 +324,3 @@ GLOBAL_LIST_INIT(blacklisted_pylon_turfs, typecacheof(list(
/obj/effect/gateway/Crossed(atom/movable/AM, oldloc)
return
-
-
-//Armor kit
-
-/obj/item/storage/box/cult
- name = "Dark Forge Cache"
- can_hold = list("/obj/item/clothing/suit/space/cult", "/obj/item/clothing/head/helmet/space/cult")
- max_w_class = WEIGHT_CLASS_NORMAL
-
-/obj/item/storage/box/cult/New()
- ..()
- new /obj/item/clothing/suit/space/cult(src)
- new /obj/item/clothing/head/helmet/space/cult(src)
- return
diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm
index 823a9a8980ebe..254914aabc3a6 100644
--- a/code/game/gamemodes/cult/ritual.dm
+++ b/code/game/gamemodes/cult/ritual.dm
@@ -1,320 +1,175 @@
#define CULT_ELDERGOD "eldergod"
#define CULT_SLAUGHTER "slaughter"
-/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()
- if(!SSticker.mode)//work around for maps with runes and cultdat is not loaded all the way
- var/bits = make_bit_triplet()
- icon = get_rune(bits)
- else
- icon = get_rune_cult(invocation)
-
-/obj/item/tome
- name = "arcane tome"
- desc = "An old, dusty tome with frayed edges and a sinister-looking cover."
- icon_state ="tome"
- throw_speed = 2
- throw_range = 5
+/obj/item/melee/cultblade/dagger
+ name = "ritual dagger"
+ desc = "A strange dagger said to be used by sinister groups for \"preparing\" a corpse before sacrificing it to their dark gods."
+ icon_state = "blood_dagger"
+ item_state = "blood_dagger"
w_class = WEIGHT_CLASS_SMALL
- var/scribereduct = 0
- var/canbypass = 0 //ADMINBUS
-
-/obj/item/tome/accursed
- name = "accursed tome"
- desc = "An arcane tome still empowered with a shadow of its former consecration."
- scribereduct = 30 //faster because it's made by corrupting a bible
-
-/obj/item/tome/imbued //Admin-only tome, allows instant drawing of runes
- name = "imbued arcane tome"
- desc = "An arcane tome granted by the Geometer itself."
- scribereduct = 50
- canbypass = 1
-
-/obj/item/tome/New()
- if(!SSticker.mode)
- icon_state = "tome"
- else
- icon_state = SSticker.cultdat.tome_icon
+ force = 15
+ throwforce = 25
+ armour_penetration = 35
+ sprite_sheets_inhand = null // Override parent
+ var/drawing_rune = FALSE
+ var/scribe_multiplier = 1 // Lower is faster
+
+/obj/item/melee/cultblade/dagger/adminbus
+ name = "ritual dagger of scribing, +1"
+ desc = "VERY fast culto scribing at incredible hihg speed"
+ force = 16
+ scribe_multiplier = 0.1
+
+/obj/item/melee/cultblade/dagger/New()
..()
+ if(SSticker.mode)
+ icon_state = SSticker.cultdat.dagger_icon
+ item_state = SSticker.cultdat.dagger_icon
-/obj/item/tome/examine(mob/user)
+/obj/item/melee/cultblade/dagger/examine(mob/user)
. = ..()
if(iscultist(user) || user.stat == DEAD)
- . += "The scriptures of [SSticker.cultdat.entity_title3]. Allows the scribing of runes and access to the knowledge archives of the cult of [SSticker.cultdat.entity_name]."
- . += "Striking another cultist with it will purge holy water from them."
- . += "Striking a non-cultist, however, will sear their flesh."
+ . += "A dagger gifted by [SSticker.cultdat.entity_title3]. Allows the scribing of runes and access to the knowledge archives of the cult of [SSticker.cultdat.entity_name]."
+ . += "Striking another cultist with it will purge holy water from them."
+ . += "Striking a noncultist will tear their flesh."
-/obj/item/tome/attack(mob/living/M, mob/living/user)
- if(!istype(M))
- return
- if(!iscultist(user))
- return ..()
+/obj/item/melee/cultblade/dagger/attack(mob/living/M, mob/living/user)
if(iscultist(M))
if(M.reagents && M.reagents.has_reagent("holywater")) //allows cultists to be rescued from the clutches of ordained religion
to_chat(user, "You remove the taint from [M].")
- var/holy2unholy = M.reagents.get_reagent_amount("holywater")
+ var/amount = M.reagents.get_reagent_amount("holywater")
M.reagents.del_reagent("holywater")
- M.reagents.add_reagent("unholywater",holy2unholy)
+ M.reagents.add_reagent("unholywater", amount)
add_attack_logs(user, M, "Hit with [src], removing the holy water from them")
- return
- M.take_organ_damage(0, 15) //Used to be a random between 5 and 20
- playsound(M, 'sound/weapons/sear.ogg', 50, 1)
- M.visible_message("[user] strikes [M] with [src]!", \
- "[user] strikes you with [src], searing your flesh!")
- flick("tome_attack", src)
- user.do_attack_animation(M)
- add_attack_logs(user, M, "Hit with [src]")
+ return FALSE
+ . = ..()
-/obj/item/tome/attack_self(mob/user)
+/obj/item/melee/cultblade/dagger/attack_self(mob/user)
if(!iscultist(user))
- to_chat(user, "[src] seems full of unintelligible shapes, scribbles, and notes. Is this some sort of joke?")
+ to_chat(user, "[src] is covered in unintelligible shapes and markings.")
return
- open_tome(user)
-
-/obj/item/tome/proc/open_tome(mob/user)
- var/choice = alert(user,"You open the tome...",,"Scribe Rune","More Information","Cancel")
- switch(choice)
- if("More Information")
- read_tome(user)
- if("Scribe Rune")
- scribe_rune(user)
- if("Cancel")
- return
-
-/obj/item/tome/proc/read_tome(mob/user)
- var/text = list()
- text += "Archives of [SSticker.cultdat.entity_title1]
"
- text += "A rune's name and effects can be revealed by examining the rune.<
"
-
- text += "Rite of Binding
This rune is one of the most important runes the cult has, being the only way to create new talismans. A blank sheet of paper must be on top of the rune. After \
- invoking it and choosing which talisman you desire, the paper will be converted, after some delay into a talisman.
"
-
- text += "Teleport
This rune is unique in that it requires a keyword before the scribing can begin. When invoked, it will find any other Teleport runes; \
- If any are found, the user can choose which rune to send to. Upon activation, the rune teleports everything above it to the selected rune.
"
-
- text += "Rite of Enlightenment
This rune is critical to the success of the cult. It will allow you to convert normal crew members into cultists. \
- To do this, simply place the crew member upon the rune and invoke it. This rune requires two invokers to use. If the target to be converted is mindshielded or a certain assignment, they will \
- be unable to be converted. People [SSticker.cultdat.entity_title3] wishes sacrificed will also be ineligible for conversion, and anyone with a shielding presence like the null rod will not be converted.
\
- Successful conversions will produce a tome for the new cultist.
"
-
- text += "Rite of Tribute
This rune is necessary to achieve your goals. Simply place any dead creature upon the rune and invoke it (this will not \
- target cultists!). If this creature has a mind, a soulstone will be created and the creature's soul transported to it. Sacrificing the dead can be done alone, but sacrificing living crew or your cult's target will require 3 cultists. \
- Soulstones used on construct shells will move that soul into a powerful construct of your choice.
"
-
-
- text += "Rite of Resurrection
This rune requires two corpses. To perform the ritual, place the corpse you wish to revive onto \
- the rune and the offering body adjacent to it. When the rune is invoked, the body to be sacrificed will turn to dust, the life force flowing into the revival target. Assuming the target is not moved \
- within a few seconds, they will be brought back to life, healed of all ailments.
"
-
- text += "Rite of Disruption
Robotic lifeforms have time and time again been the downfall of fledgling cults. This rune may allow you to gain the upper \
- hand against these pests. By using the rune, a large electromagnetic pulse will be emitted from the rune's location. The size of the EMP will grow significantly for each additional adjacent cultist when the \
- rune is activated.
"
-
- text += "Astral Communion
This rune is perhaps the most ingenious rune that is usable by a single person. Upon invoking the rune, the \
- user's spirit will be ripped from their body. In this state, the user's physical body will be locked in place to the rune itself - any attempts to move it will result in the rune pulling it back. \
- The body will also take constant damage while in this form, and may even die. The user's spirit will contain their consciousness, and will allow them to freely wander the station as a ghost. This may \
- also be used to commune with the dead.
"
-
- text += "Rite of the Corporeal Shield
While simple, this rune serves an important purpose in defense and hindering passage. When invoked, the \
- rune will draw a small amount of life force from the user and make the space above the rune completely dense, rendering it impassable to all but the most complex means. The rune may be invoked again to \
- undo this effect and allow passage again.
"
-
- text += "Rite of Joined Souls
This rune allows the cult to free other cultists with ease. When invoked, it will allow the user to summon a single cultist to the rune from \
- any location. It requires two invokers, and will damage each invoker slightly.
"
-
- text += "Blood Boil
When invoked, this rune will do a massive amount of damage to all non-cultist viewers, but it will also emit a small explosion upon invocation. \
- It requires three invokers.
"
-
- text += "Leeching
When invoked, this rune will transfer life force from the victim to the invoker.
"
-
- text += "Rite of Spectral Manifestation
This rune allows you to summon spirits as humanoid fighters. When invoked, a spirit above the rune will be brought to life as a human, wearing nothing, that seeks only to serve you and [SSticker.cultdat.entity_title3]. \
- However, the spirit's link to reality is fragile - you must remain on top of the rune, and you will slowly take damage. Upon stepping off the rune, all summoned spirits will dissipate, dropping their items to the ground. You may manifest \
- multiple spirits with one rune, but you will rapidly take damage in doing so.
"
-
- text += "Ritual of Dimensional Rending
This rune is necessary to achieve your goals. On attempting to scribe it, it will produce shields around you and alert everyone you are attempting to scribe it; it takes a very long time to scribe, \
- and does massive damage to the one attempting to scribe it.
Invoking it requires 9 invokers and the sacrifice of a specific crewmember, and once invoked, will summon [SSticker.cultdat.entity_title3], [SSticker.cultdat.entity_name]. \
- This will complete your objectives.
"
-
- text += "Talisman of Teleportation
The talisman form of the Teleport rune will transport the invoker to a selected Teleport rune once.
"
-
- text += "Talisman of Fabrication
This talisman is the main way of creating construct shells. To use it, one must strike 30 sheets of metal with the talisman. The sheets will then be twisted into a construct shell, ready to receive a soul to occupy it.
"
-
- text += "Talisman of Tome Summoning
This talisman will produce a single tome at your feet.
"
-
- text += "Talisman of Veiling/Revealing
This talisman will hide runes on its first use, and on the second, will reveal runes.
"
-
- text += "Talisman of Disguising
This talisman will permanently disguise all nearby runes as crayon runes.
"
-
- text += "Talisman of Electromagnetic Pulse
This talisman will EMP anything else nearby. It disappears after one use.
"
-
- text += "Talisman of Stunning
Attacking a target will knock them down for a long duration in addition to inhibiting their speech. \
- Robotic lifeforms will suffer the effects of a heavy electromagnetic pulse instead.
"
-
- text += "Talisman of Armaments
The Talisman of Arming will equip the user with armored robes, a backpack, shoes, an eldritch longsword, and an empowered bola. Any equipment that cannot \
- be equipped will not be summoned, weaponry will be put on the floor below the user. Attacking a fellow cultist with it will instead equip them.
"
-
- text += "Talisman of Horrors
The Talisman of Horror must be applied directly to the victim, it will shatter your victim's mind with visions of the end-times that may incapacitate them.
"
-
- text += "Talisman of Shackling
The Talisman of Shackling must be applied directly to the victim, it has 4 uses and cuffs victims with magic shackles that disappear when removed.
"
-
- text += "In addition to these runes, the cult has a small selection of equipment and constructs.
"
-
- text += "Equipment:
"
-
- text += "Cult Blade
Cult blades are sharp weapons that, notably, cannot be used by non-cultists. These blades are produced by the Talisman of Arming.
"
-
- text += "Cult Bola
Cult bolas are strong bolas, useful for snaring targets. These bolas are produced by the Talisman of Arming.
"
-
- text += "Cult Robes
Cult robes are heavily armored robes. These robes are produced by the Talisman of Arming.
"
-
- text += "Soulstone
A soulstone is a simple piece of magic, produced either via the starter talisman or by sacrificing humans. Using it on an unconscious or dead humanoid, or on a Shade, will trap their soul in the stone, allowing its use in construct shells. \
-
The soul within can also be released as a Shade by using it in-hand.
"
-
- text += "Construct Shell
A construct shell is useless on its own, but placing a filled soulstone within it allows you to produce your choice of a Wraith, a Juggernaut, or an Artificer. \
-
Each construct has uses, detailed below in Constructs. Construct shells can be produced via the starter talisman or the Rite of Fabrication.
"
-
- text += "Constructs:
"
-
- text += "Shade
While technically not a construct, the Shade is produced when released from a soulstone. It is quite fragile and has weak melee attacks, but is fully healed when recaptured by a soulstone.
"
-
- text += "Wraith
The Wraith is a fast, lethal melee attacker which can jaunt through walls. However, it is only slightly more durable than a shade.
"
-
- text += "Juggernaut
The Juggernaut is a slow, but durable, melee attacker which can produce temporary forcewalls. It will also reflect most lethal energy weapons.
"
-
- text += "Artificer
The Artificer is a weak and fragile construct, able to heal other constructs, produce more soulstones and construct shells, \
- construct fortifying cult walls and flooring, and finally, it can release a few indiscriminate stunning missiles.
"
-
- text += "Harvester
If you see one, know that you have done all you can and your life is void.
"
-
- var/text_string = jointext(text, null)
- var/datum/browser/popup = new(user, "tome", "", 800, 600)
- popup.set_content(text_string)
- popup.open()
- return 1
-
-/obj/item/tome/proc/finale_runes_ok(mob/living/user, obj/effect/rune/rune_to_scribe)
- var/datum/game_mode/cult/cult_mode = SSticker.mode
- var/area/A = get_area(src)
- if(GAMEMODE_IS_CULT)
- if(!canbypass)//not an admin-tome, check things
- if(!cult_mode.narsie_condition_cleared)
- to_chat(user, "There is still more to do before unleashing [SSticker.cultdat.entity_name]'s' power!")
- return 0
- if(!cult_mode.eldergod)
- to_chat(user, "\"I am already here. There is no need to try to summon me now.\"")
- return 0
- if(cult_mode.demons_summoned)
- to_chat(user, "\"We are already here. There is no need to try to summon us now.\"")
- return 0
- if(!((CULT_ELDERGOD in cult_mode.objectives) || (CULT_SLAUGHTER in cult_mode.objectives)))
- to_chat(user, "[SSticker.cultdat.entity_name]'s power does not wish to be unleashed!")
- return 0
- if(!(A in GLOB.summon_spots))
- to_chat(user, "[SSticker.cultdat.entity_name] can only be summoned where the veil is weak - in [english_list(GLOB.summon_spots)]!")
- return 0
- var/confirm_final = alert(user, "This is the FINAL step to summon your deity's power. It is a long, painful ritual and the crew will be alerted to your presence.", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No")
+ scribe_rune(user)
+
+/obj/item/melee/cultblade/dagger/proc/narsie_rune_check(mob/living/user, area/A)
+ var/datum/game_mode/gamemode = SSticker.mode
+
+ if(gamemode.cult_objs.cult_status < NARSIE_NEEDS_SUMMONING)
+ to_chat(user, "[SSticker.cultdat.entity_name] is not ready to be summoned yet!")
+ return FALSE
+ if(gamemode.cult_objs.cult_status == NARSIE_HAS_RISEN)
+ to_chat(user, "\"I am already here. There is no need to try to summon me now.\"")
+ return FALSE
+
+ var/list/summon_areas = gamemode.cult_objs.obj_summon.summon_spots
+ if(!(A in summon_areas))
+ to_chat(user, "[SSticker.cultdat.entity_name] can only be summoned where the veil is weak - in [english_list(summon_areas)]!")
+ return FALSE
+ var/confirm_final = alert(user, "This is the FINAL step to summon your deities power, it is a long, painful ritual and the crew will be alerted to your presence AND your location!",
+ "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No")
+ if(user)
if(confirm_final == "No" || confirm_final == null)
- to_chat(user, "You decide to prepare further before scribing the rune.")
- return 0
+ to_chat(user, "You decide to prepare further before scribing the rune.")
+ return FALSE
else
- return 1
- else//the game mode is not cult..but we ARE a cultist...ALL ON THE ADMINBUS
- var/confirm_final = alert(user, "This is the FINAL step to summon your deity's power. It is a long, painful ritual and the crew will be alerted to your presence.", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No")
- if(confirm_final == "No" || confirm_final == null)
- to_chat(user, "You decide to prepare further before scribing the rune.")
- return 0
- else
- return 1
-
-/obj/item/tome/proc/scribe_rune(mob/living/user)
- var/turf/runeturf = get_turf(user)
- if(isspaceturf(runeturf))
- return
- var/chosen_keyword
- var/obj/effect/rune/rune_to_scribe
- var/entered_rune_name
- var/list/possible_runes = list()
+ if(locate(/obj/effect/rune) in range(1, user))
+ to_chat(user, "You need a space cleared of runes before you can summon [SSticker.cultdat.entity_title1]!")
+ return FALSE
+ else
+ return TRUE
+
+/obj/item/melee/cultblade/dagger/proc/can_scribe(mob/living/user)
+ if(!src || !user || loc != user || user.incapacitated())
+ return FALSE
+ var/turf/T = get_turf(user)
+ if(isspaceturf(T))
+ return FALSE
+ if((locate(/obj/effect/rune) in T) || (locate(/obj/effect/rune/narsie) in range(1, T)))
+ to_chat(user, "There's already a rune here!")
+ return FALSE
+ return TRUE
+
+
+/obj/item/melee/cultblade/dagger/proc/scribe_rune(mob/living/user)
var/list/shields = list()
- var/area/A = get_area(src)
- if(locate(/obj/effect/rune) in runeturf)
- to_chat(user, "There is already a rune here.")
- return
- for(var/T in subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed)
- var/obj/effect/rune/R = T
- if(initial(R.cultist_name))
- possible_runes.Add(initial(R.cultist_name)) //This is to allow the menu to let cultists select runes by name rather than by object path. I don't know a better way to do this
- if(!possible_runes.len)
- return
- entered_rune_name = input(user, "Choose a rite to scribe.", "Sigils of Power") as null|anything in possible_runes
- if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated())
- return
- for(var/T in typesof(/obj/effect/rune))
- var/obj/effect/rune/R = T
- if(initial(R.cultist_name) == entered_rune_name)
- rune_to_scribe = R
- if(initial(R.req_keyword))
- var/the_keyword = stripped_input(usr, "Please enter a keyword for the rune.", "Enter Keyword", "")
- if(!the_keyword)
- return
- chosen_keyword = the_keyword
- break
- if(!rune_to_scribe)
+ var/list/possible_runes = list()
+ var/keyword
+
+ if(!can_scribe(user)) // Check this before anything else
return
- runeturf = get_turf(user) //we may have moved. adjust as needed...
- A = get_area(src)
- if(locate(/obj/effect/rune) in runeturf)
- to_chat(user, "There is already a rune here.")
+
+ // Choosing a rune
+ for(var/I in (subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed))
+ var/obj/effect/rune/R = I
+ var/rune_name = initial(R.cultist_name)
+ if(rune_name)
+ possible_runes[rune_name] = R
+ if(!length(possible_runes))
return
- if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated())
+
+ var/chosen_rune = input(user, "Choose a rite to scribe.", "Sigils of Power") as null|anything in possible_runes
+ if(!chosen_rune)
return
- if(ispath(rune_to_scribe, /obj/effect/rune/narsie) || ispath(rune_to_scribe, /obj/effect/rune/slaughter))//may need to change this - Fethas
- if(finale_runes_ok(user,rune_to_scribe))
- A = get_area(src)
- if(!(A in GLOB.summon_spots)) // Check again to make sure they didn't move
- to_chat(user, "The ritual can only begin where the veil is weak - in [english_list(GLOB.summon_spots)]!")
+ var/obj/effect/rune/rune = possible_runes[chosen_rune]
+ var/narsie_rune = FALSE
+ if(rune == /obj/effect/rune/narsie)
+ narsie_rune = TRUE
+ if(initial(rune.req_keyword))
+ keyword = stripped_input(user, "Please enter a keyword for the rune.", "Enter Keyword")
+ if(!keyword)
+ return
+
+ // Check if the rune is allowed
+ var/area/A = get_area(src)
+ var/turf/runeturf = get_turf(user)
+ var/datum/game_mode/gamemode = SSticker.mode
+ if(ispath(rune, /obj/effect/rune/summon))
+ if(!is_station_level(runeturf.z) || istype(A, /area/space))
+ to_chat(user, "The veil is not weak enough here to summon a cultist, you must be on station!")
+ return
+
+ if(narsie_rune)
+ if(!narsie_rune_check(user, A))
+ return // don't do shit
+ else
+ var/list/summon_areas = gamemode.cult_objs.obj_summon.summon_spots
+ if(!(A in summon_areas)) // Check again to make sure they didn't move
+ to_chat(user, "The ritual can only begin where the veil is weak - in [english_list(summon_areas)]!")
return
- GLOB.command_announcement.Announce("Figments from an eldritch god are being summoned somewhere on the station from an unknown dimension. Disrupt the ritual at all costs!","Central Command Higher Dimensional Affairs", 'sound/AI/spanomalies.ogg')
- for(var/B in spiral_range_turfs(1, user, 1))
- var/turf/T = B
- var/obj/machinery/shield/N = new(T)
- N.name = "Rune-Scriber's Shield"
- N.desc = "A potent shield summoned by cultists to protect them while they prepare the final ritual"
- N.icon_state = "shield-cult"
- N.health = 60
+ GLOB.command_announcement.Announce("Figments from an eldritch god are being summoned into the [A.map_name] from an unknown dimension. Disrupt the ritual at all costs!", "Central Command Higher Dimensional Affairs", 'sound/AI/spanomalies.ogg')
+ for(var/I in spiral_range_turfs(1, user, 1))
+ var/turf/T = I
+ var/obj/machinery/shield/cult/narsie/N = new(T)
shields |= N
- else
- return//don't do shit
+ // Check everything again, in case they moved
+ if(!can_scribe(user))
+ return
+
+ // Draw the rune
var/mob/living/carbon/human/H = user
- var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot")
- var/obj/item/organ/external/affecting = H.get_organ(ran_zone(dam_zone))
- user.visible_message("[user] cuts open [user.p_their()] [affecting] and begins writing in [user.p_their()] own blood!", "You slice open your [affecting] and begin drawing a sigil of [SSticker.cultdat.entity_title3].")
- user.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE , affecting)
- if(!do_after(user, initial(rune_to_scribe.scribe_delay)-scribereduct, target = get_turf(user)))
- for(var/V in shields)
+ H.cult_self_harm(initial(rune.scribe_damage), TRUE)
+ if(!do_after(user, initial(rune.scribe_delay) * scribe_multiplier, target = runeturf))
+ for(var/V in shields) // Only used for the 'Tear Veil' rune
var/obj/machinery/shield/S = V
if(S && !QDELETED(S))
qdel(S)
return
- if(locate(/obj/effect/rune) in runeturf)
- to_chat(user, "There is already a rune here.")
- return
- user.visible_message("[user] creates a strange circle in [user.p_their()] own blood.", \
- "You finish drawing the arcane markings of [SSticker.cultdat.entity_title3].")
- for(var/V in shields)
+
+ user.visible_message("[user] creates a strange circle in [user.p_their()] own blood.",
+ "You finish drawing the arcane markings of [SSticker.cultdat.entity_title3].")
+ for(var/V in shields) // Only for the 'Tear Veil' rune
var/obj/machinery/shield/S = V
if(S && !QDELETED(S))
qdel(S)
- var/obj/effect/rune/R = new rune_to_scribe(runeturf, chosen_keyword)
+
+ var/obj/effect/rune/R = new rune(runeturf, keyword)
+ if(narsie_rune)
+ for(var/obj/effect/rune/I in orange(1, R))
+ qdel(I)
+
R.blood_DNA = list()
R.blood_DNA[H.dna.unique_enzymes] = H.dna.blood_type
R.add_hiddenprint(H)
- to_chat(user, "The [lowertext(initial(rune_to_scribe.cultist_name))] rune [initial(rune_to_scribe.cultist_desc)]")
+ R.color = H.dna.species.blood_color
+ R.rune_blood_color = H.dna.species.blood_color
+ to_chat(user, "The [lowertext(initial(rune.cultist_name))] rune [initial(rune.cultist_desc)]")
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index 02cd7b6db6570..32b6614c061e6 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -1,49 +1,66 @@
-GLOBAL_LIST_EMPTY(sacrificed)
-GLOBAL_LIST_INIT(non_revealed_runes, (subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed))
+GLOBAL_LIST_EMPTY(sacrificed) // A mixed list of minds and mobs
+GLOBAL_LIST_EMPTY(wall_runes) // A list of all cult shield walls
+GLOBAL_LIST_EMPTY(teleport_runes) // I'll give you two guesses
/*
This file contains runes.
Runes are used by the cult to cause many different effects and are paramount to their success.
-They are drawn with an arcane tome in blood, and are distinguishable to cultists and normal crew by examining.
+They are drawn with a ritual dagger in blood, and are distinguishable to cultists and normal crew by examining.
Fake runes can be drawn in crayon to fool people.
Runes can either be invoked by one's self or with many different cultists. Each rune has a specific incantation that the cultists will say when invoking it.
-To draw a rune, use an arcane tome.
+To draw a rune, use a ritual dagger.
*/
/obj/effect/rune
+ /// Name non-cultists see
name = "rune"
+ /// Name that cultists see
var/cultist_name = "basic rune"
+ /// Description that non-cultists see
desc = "An odd collection of symbols drawn in what seems to be blood."
+ /// Description that cultists see
var/cultist_desc = "a basic rune with no function." //This is shown to cultists who examine the rune in order to determine its true purpose.
- anchored = 1
+ anchored = TRUE
icon = 'icons/obj/rune.dmi'
icon_state = "1"
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ mouse_opacity = MOUSE_OPACITY_OPAQUE // So that runes aren't so hard to click
var/visibility = 0
var/view_range = 7
- layer = TURF_LAYER
-
- var/invocation = "Aiy ele-mayo!" //This is said by cultists when the rune is invoked.
- var/req_cultists = 1 //The amount of cultists required around the rune to invoke it. If only 1, any cultist can invoke it.
- var/rune_in_use = 0 // Used for some runes, this is for when you want a rune to not be usable when in use.
-
- var/scribe_delay = 50 //how long the rune takes to create
- var/scribe_damage = 0.1 //how much damage you take doing it
-
- var/allow_excess_invokers = 0 //if we allow excess invokers when being invoked
- var/construct_invoke = 1 //if constructs can invoke it
-
- var/req_keyword = 0 //If the rune requires a keyword - go figure amirite
- var/keyword //The actual keyword for the rune
-
- var/invoke_damage = 0 //how much damage invokers take when invoking it
-
+ layer = SIGIL_LAYER
+ color = COLOR_BLOOD_BASE
+
+ /// What is said by cultists when the rune is invoked
+ var/invocation = "Aiy ele-mayo!"
+ ///The amount of cultists required around the rune to invoke it. If only 1, any cultist can invoke it.
+ var/req_cultists = 1
+ /// Used for some runes, this is for when you want a rune to not be usable when in use.
+ var/rune_in_use = FALSE
+
+ /// How long the rune takes to create (Currently only different for the Nar'Sie rune)
+ var/scribe_delay = 5 SECONDS
+ /// How much damage you take from drawing the rune
+ var/scribe_damage = 1
+
+ /// If nearby cultists will also chant when invoked
+ var/allow_excess_invokers = 0
+ /// If constructs can invoke it
+ var/construct_invoke = TRUE
+
+ /// If the rune requires a keyword (e.g. Teleport runes)
+ var/req_keyword = FALSE
+ /// The actual keyword for the rune
+ var/keyword
+
+ /// How much damage cultists take when invoking it (This includes constructs)
+ var/invoke_damage = 0
+ /// The color of the rune. (Based on species blood color)
+ var/rune_blood_color = COLOR_BLOOD_BASE
/obj/effect/rune/New(loc, set_keyword)
..()
if(set_keyword)
keyword = set_keyword
- check_icon()
var/image/blood = image(loc = src)
blood.override = 1
for(var/mob/living/silicon/ai/AI in GLOB.player_list)
@@ -56,18 +73,29 @@ To draw a rune, use an arcane tome.
. += "Effects: [capitalize(cultist_desc)]"
. += "Required Acolytes: [req_cultists]"
if(req_keyword && keyword)
- . += "Keyword: [keyword]"
+ . += "Keyword: [keyword]"
/obj/effect/rune/attackby(obj/I, mob/user, params)
- if(istype(I, /obj/item/tome) && iscultist(user))
- to_chat(user, "You carefully erase the [lowertext(cultist_name)] rune.")
- qdel(src)
+ if(istype(I, /obj/item/melee/cultblade/dagger) && iscultist(user))
+ // Telerunes with portals open
+ if(istype(src, /obj/effect/rune/teleport))
+ var/obj/effect/rune/teleport/T = src // Can't erase telerunes if they have a portal open
+ if(T.inner_portal || T.outer_portal)
+ to_chat(user, "The portal needs to close first!")
+ return
+
+ // Everything else
+ var/obj/item/melee/cultblade/dagger/D = I
+ user.visible_message("[user] begins to erase [src] with [I].")
+ if(do_after(user, initial(scribe_delay) * D.scribe_multiplier, target = src))
+ to_chat(user, "You carefully erase the [lowertext(cultist_name)] rune.")
+ qdel(src)
return
if(istype(I, /obj/item/nullrod))
if(iscultist(user))//cultist..what are doing..cultist..staph...
user.drop_item()
- user.visible_message("[I] suddenly glows with white light, forcing [user] to drop it in pain!", \
- "[I] suddenly glows with a white light that sears your hand, forcing you to drop it!")
+ user.visible_message("[I] suddenly glows with a white light, forcing [user] to drop it in pain!", \
+ "[I] suddenly glows with a white light that sears your hand, forcing you to drop it!") // TODO: Make this actually burn your hand
return
to_chat(user,"You disrupt the magic of [src] with [I].")
qdel(src)
@@ -75,39 +103,33 @@ To draw a rune, use an arcane tome.
return ..()
/obj/effect/rune/attack_hand(mob/living/user)
+ user.Move_Pulled(src) // So that you can still drag things onto runes
if(!iscultist(user))
to_chat(user, "You aren't able to understand the words of [src].")
return
var/list/invokers = can_invoke(user)
- if(invokers.len >= req_cultists)
+ if(length(invokers) >= req_cultists)
invoke(invokers)
else
- fail_invoke(user)
-
+ fail_invoke()
/obj/effect/rune/attack_animal(mob/living/simple_animal/M)
- if(istype(M, /mob/living/simple_animal/shade) || istype(M, /mob/living/simple_animal/hostile/construct))
+ if(isshade(M) || isconstruct(M))
if(construct_invoke || !iscultist(M)) //if you're not a cult construct we want the normal fail message
attack_hand(M)
else
to_chat(M, "You are unable to invoke the rune!")
-/obj/effect/rune/proc/talismanhide() //for talisman of revealing/hiding
+/obj/effect/rune/cult_conceal() //for concealing spell
visible_message("[src] fades away.")
invisibility = INVISIBILITY_OBSERVER
alpha = 100 //To help ghosts distinguish hidden runes
-/obj/effect/rune/proc/talismanreveal() //for talisman of revealing/hiding
+/obj/effect/rune/cult_reveal() //for revealing spell
invisibility = 0
visible_message("[src] suddenly appears!")
alpha = initial(alpha)
-/obj/effect/rune/proc/talismanfake() //for rune disguising
- var/runenum = pick(1,2,3,4,5,6)
- visible_message("[src] takes on a waxy apperance!")
- icon = 'icons/effects/crayondecal.dmi'
- icon_state = "rune[runenum]"
- color = rgb(255, 0, 0)
/*
There are a few different procs each rune runs through when a cultist activates it.
@@ -116,31 +138,29 @@ invoke() is the rune's actual effects.
fail_invoke() is called when the rune fails, via not enough people around or otherwise. Typically this just has a generic 'fizzle' effect.
structure_check() searches for nearby cultist structures required for the invocation. Proper structures are pylons, forges, archives, and altars.
*/
-
-/obj/effect/rune/proc/can_invoke(var/mob/living/user)
+/obj/effect/rune/proc/can_invoke(mob/living/user)
//This proc determines if the rune can be invoked at the time. If there are multiple required cultists, it will find all nearby cultists.
var/list/invokers = list() //people eligible to invoke the rune
var/list/chanters = list() //people who will actually chant the rune when passed to invoke()
if(invisibility == INVISIBILITY_OBSERVER)//hidden rune
return
+ // Get the user
if(user)
chanters |= user
invokers |= user
+ // Get anyone nearby
if(req_cultists > 1 || allow_excess_invokers)
for(var/mob/living/L in range(1, src))
if(iscultist(L))
if(L == user)
continue
- if(ishuman(L))
- var/mob/living/carbon/human/H = L
- if(!H.can_speak())
- continue
if(L.stat)
continue
invokers |= L
- if(invokers.len >= req_cultists)
+
+ if(length(invokers) >= req_cultists) // If there's enough invokers
if(allow_excess_invokers)
- chanters |= invokers
+ chanters |= invokers // Let the others join in too
else
invokers -= user
shuffle(invokers)
@@ -149,10 +169,12 @@ structure_check() searches for nearby cultist structures required for the invoca
chanters |= L
return chanters
-/obj/effect/rune/proc/invoke(var/list/invokers)
+/obj/effect/rune/proc/invoke(list/invokers)
//This proc contains the effects of the rune as well as things that happen afterwards. If you want it to spawn an object and then delete itself, have both here.
for(var/M in invokers)
var/mob/living/L = M
+ if(!L)
+ return
if(invocation)
if(!L.IsVocal())
L.emote("gestures ominously.")
@@ -164,37 +186,52 @@ structure_check() searches for nearby cultist structures required for the invoca
to_chat(L, "[src] saps your strength!")
do_invoke_glow()
-/obj/effect/rune/proc/burn_invokers(var/list/mobstoburn)
- for(var/M in mobstoburn)
- var/mob/living/L = M
- to_chat(L, "\"YOUR SOUL BURNS WITH YOUR ARROGANCE!!!\"")
- if(L.reagents)
- L.reagents.add_reagent("hell_water", 10)
- L.Weaken(7)
- L.Stun(7)
- fail_invoke()
+/**
+ * Spawns the phase in/out effects for a cult teleport.
+ *
+ * Arguments:
+ * * user - Mob to teleport
+ * * location - Location to teleport from
+ * * target - Location to teleport to
+ */
+/obj/effect/rune/proc/teleport_effect(mob/living/user, turf/location, target)
+ new /obj/effect/temp_visual/dir_setting/cult/phase/out(location, user.dir)
+ new /obj/effect/temp_visual/dir_setting/cult/phase(target, user.dir)
+ // So that the mob only appears after the effect is finished
+ user.notransform = TRUE
+ user.invisibility = INVISIBILITY_MAXIMUM
+ sleep(12)
+ user.notransform = FALSE
+ user.invisibility = 0
/obj/effect/rune/proc/do_invoke_glow()
- var/oldtransform = transform
- spawn(0) //animate is a delay, we want to avoid being delayed
- animate(src, transform = matrix()*2, alpha = 0, time = 5) //fade out
- animate(transform = oldtransform, alpha = 255, time = 0)
+ var/oldtransform = transform
+ animate(src, transform = matrix() * 2, alpha = 0, time = 5) // Fade out
+ animate(transform = oldtransform, alpha = 255, time = 0)
-/obj/effect/rune/proc/fail_invoke(var/mob/living/user)
+/obj/effect/rune/proc/fail_invoke()
//This proc contains the effects of a rune if it is not invoked correctly, through either invalid wording or not enough cultists. By default, it's just a basic fizzle.
visible_message("The markings pulse with a small flash of red light, then fall dark.")
- spawn(0) //animate is a delay, we want to avoid being delayed
- animate(src, color = rgb(255, 0, 0), time = 0)
- animate(src, color = initial(color), time = 5)
+ animate(src, color = rgb(255, 0, 0), time = 0)
+ animate(src, color = rune_blood_color, time = 5)
+
+
+/obj/effect/rune/proc/check_icon()
+ if(!SSticker.mode)//work around for maps with runes and cultdat is not loaded all the way
+ var/bits = make_bit_triplet()
+ icon = get_rune(bits)
+ else
+ icon = get_rune_cult(invocation)
+
//Malformed Rune: This forms if a rune is not drawn correctly. Invoking it does nothing but hurt the user.
/obj/effect/rune/malformed
- cultist_name = "malformed rune"
+ cultist_name = "Malformed"
cultist_desc = "a senseless rune written in gibberish. No good can come from invoking this."
invocation = "Ra'sha yoka!"
invoke_damage = 30
-/obj/effect/rune/malformed/invoke(var/list/invokers)
+/obj/effect/rune/malformed/invoke(list/invokers)
..()
for(var/M in invokers)
var/mob/living/L = M
@@ -205,204 +242,120 @@ structure_check() searches for nearby cultist structures required for the invoca
var/obj/item/nullrod/N = locate() in src
if(N)
return N
- return 0
-
-//Rite of Binding: A paper on top of the rune to a talisman.
-/obj/effect/rune/imbue
- cultist_name = "Rite of Binding"
- cultist_desc = "transforms paper into powerful magic talismans."
- invocation = "H'drak v'loso, mir'kanas verbot!"
- icon_state = "3"
+ return FALSE
-/obj/effect/rune/imbue/invoke(var/list/invokers)
- var/mob/living/user = invokers[1] //the first invoker is always the user
- var/turf/T = get_turf(src)
- var/list/papers_on_rune = list()
- var/entered_talisman_name
- var/obj/item/paper/talisman/talisman_type
- var/list/possible_talismans = list()
- for(var/obj/item/paper/P in T)
- if(!P.info)
- papers_on_rune.Add(P)
- if(!papers_on_rune.len)
- to_chat(user, "There must be a blank paper on top of [src]!")
- fail_invoke()
- log_game("Talisman Creation rune failed - no blank papers on rune")
- return
- if(rune_in_use)
- to_chat(user, "[src] can only support one ritual at a time!")
- fail_invoke()
- log_game("Talisman Creation rune failed - already in use")
- return
- var/obj/item/paper/paper_to_imbue = pick(papers_on_rune)
- for(var/I in subtypesof(/obj/item/paper/talisman) - /obj/item/paper/talisman/malformed - /obj/item/paper/talisman/supply - /obj/item/paper/talisman/supply/weak)
- var/obj/item/paper/talisman/J = I
- var/talisman_cult_name = initial(J.cultist_name)
- if(talisman_cult_name)
- possible_talismans[talisman_cult_name] = J //This is to allow the menu to let cultists select talismans by name
- entered_talisman_name = input(user, "Choose a talisman to imbue.", "Talisman Choices") as null|anything in possible_talismans
- talisman_type = possible_talismans[entered_talisman_name]
- if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated() || rune_in_use || !talisman_type)
- return
- ..()
- visible_message("Dark power begins to channel into the paper!.")
- rune_in_use = 1
- if(!do_after(user, 100, target = get_turf(user)))
- rune_in_use = 0
- return
- new talisman_type(get_turf(src))
- visible_message("[src] glows with power, and bloody images form themselves on [paper_to_imbue].")
- qdel(paper_to_imbue)
- rune_in_use = 0
-
-GLOBAL_LIST_EMPTY(teleport_runes)
-/obj/effect/rune/teleport
- cultist_name = "Teleport"
- cultist_desc = "warps everything above it to another chosen teleport rune."
- invocation = "Sas'so c'arta forbici!"
- icon_state = "2"
- req_keyword = 1
- var/listkey
- invoke_damage = 6 //but theres some checks for z-level
-
-/obj/effect/rune/teleport/New(loc, set_keyword)
- ..()
- var/area/A = get_area(src)
- var/locname = initial(A.name)
- listkey = set_keyword ? "[set_keyword] [locname]":"[locname]"
- GLOB.teleport_runes += src
-
-/obj/effect/rune/teleport/Destroy()
- GLOB.teleport_runes -= src
- return ..()
-
-/obj/effect/rune/teleport/invoke(var/list/invokers)
- var/mob/living/user = invokers[1] //the first invoker is always the user
- var/list/potential_runes = list()
- var/list/teleportnames = list()
- var/list/duplicaterunecount = list()
- for(var/R in GLOB.teleport_runes)
- var/obj/effect/rune/teleport/T = R
- var/resultkey = T.listkey
- if(resultkey in teleportnames)
- duplicaterunecount[resultkey]++
- resultkey = "[resultkey] ([duplicaterunecount[resultkey]])"
- else
- teleportnames.Add(resultkey)
- duplicaterunecount[resultkey] = 1
- if(T != src && is_level_reachable(T.z))
- potential_runes[resultkey] = T
-
- if(!potential_runes.len)
- to_chat(user, "There are no valid runes to teleport to!")
- log_game("Teleport rune failed - no other teleport runes")
- fail_invoke()
- return
-
- if(!is_level_reachable(user.z))
- to_chat(user, "You are not in the right dimension!")
- log_game("Teleport rune failed - user in away mission")
- fail_invoke()
- return
-
- var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in potential_runes //we know what key they picked
- var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to?
- if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated() || !actual_selected_rune)
- fail_invoke()
- return
-
- var/turf/T = get_turf(src)
- var/movedsomething = 0
- var/moveuserlater = 0
- for(var/atom/movable/A in T)
- if(A.move_resist == INFINITY)
- continue //object cant move, shouldnt teleport
- if(A == user)
- moveuserlater = 1
- movedsomething = 1
- continue
- if(!A.anchored)
- movedsomething = 1
- A.forceMove(get_turf(actual_selected_rune))
- if(movedsomething)
- ..()
- visible_message("There is a sharp crack of inrushing air, and everything above the rune disappears!")
- to_chat(user, "You[moveuserlater ? "r vision blurs, and you suddenly appear somewhere else":" send everything above the rune away"].")
- if(moveuserlater)
- user.forceMove(get_turf(actual_selected_rune))
- var/mob/living/carbon/human/H = user
- if(user.z != T.z)
- if(istype(H))
- H.bleed(5)
- user.apply_damage(5, BRUTE)
- else
- if(istype(H))
- H.bleed(rand(5,10))
- else
- fail_invoke()
-
-
-//Rite of Offering: Converts a normal crewmember to the cult or sacrifices mindshielded and sacrifice targets.
+//Rite of Enlightenment: Converts a normal crewmember to the cult, or offer them as sacrifice if cant be converted.
/obj/effect/rune/convert
- cultist_name = "Rite of Offering"
- cultist_desc = "Offers a non-cultists on top of it to your deity, either converting or sacrificing them."
+ cultist_name = "Offer"
+ cultist_desc = "offers non-cultists on top of it to the Dark One, either converting or sacrificing them. Sacrifices with a soul will result in a captured soulshard. This can be done with brains as well."
invocation = "Mah'weyh pleggh at e'ntrath!"
- icon_state = "3"
+ icon_state = "offering"
req_cultists = 1
allow_excess_invokers = TRUE
rune_in_use = FALSE
-/obj/effect/rune/convert/do_invoke_glow()
- return
-
-/obj/effect/rune/convert/invoke(var/list/invokers)
+/obj/effect/rune/convert/invoke(list/invokers)
if(rune_in_use)
return
- var/list/myriad_targets = list()
+
+ var/list/offer_targets = list()
var/turf/T = get_turf(src)
for(var/mob/living/M in T)
- if(!iscultist(M))
- myriad_targets |= M
- if(!myriad_targets.len)
+ if(!iscultist(M) || (M.mind && is_sacrifice_target(M.mind)))
+ offer_targets += M
+
+ // Offering a head/brain
+ for(var/obj/item/organ/O in T)
+ var/mob/living/carbon/brain/b_mob
+ if(istype(O, /obj/item/organ/external/head)) // Offering a head
+ var/obj/item/organ/external/head/H = O
+ for(var/obj/item/organ/internal/brain/brain in H.contents)
+ b_mob = brain.brainmob
+ brain.forceMove(T)
+ O = brain // Convoluted way of making the brain disappear
+
+ else if(istype(O, /obj/item/organ/internal/brain)) // Offering a brain
+ var/obj/item/organ/internal/brain/brain = O
+ b_mob = brain.brainmob
+
+ if(b_mob && b_mob.mind && (!iscultist(b_mob) || is_sacrifice_target(b_mob.mind)))
+ offer_targets += b_mob
+ O.invisibility = INVISIBILITY_MAXIMUM // So that it can't be moved around. This gets qdeleted later
+
+ if(!length(offer_targets))
fail_invoke()
log_game("Offer rune failed - no eligible targets")
rune_in_use = FALSE
return
rune_in_use = TRUE
- var/mob/living/new_cultist = pick(myriad_targets)
- if(is_convertable_to_cult(new_cultist.mind) && !new_cultist.null_rod_check() && !is_sacrifice_target(new_cultist.mind) && new_cultist.stat != DEAD && new_cultist.client != null)
- invocation = "Mah'weyh pleggh at e'ntrath!"
+ var/mob/living/L = pick(offer_targets)
+ if(L.mind in GLOB.sacrificed)
+ fail_invoke()
+ rune_in_use = FALSE
+ return
+
+ if(L.stat != DEAD && is_convertable_to_cult(L.mind))
..()
- do_convert(new_cultist, invokers)
+ do_convert(L, invokers)
else
invocation = "Barhah hra zar'garis!"
..()
- do_sacrifice(new_cultist, invokers)
+ do_sacrifice(L, invokers)
+ if(isbrain(L))
+ qdel(L.loc) // Don't need this anymore!
rune_in_use = FALSE
/obj/effect/rune/convert/proc/do_convert(mob/living/convertee, list/invokers)
- if(invokers.len < 2)
+ if(length(invokers) < 2)
fail_invoke()
for(var/I in invokers)
to_chat(I, "You need at least two invokers to convert!")
return
else
convertee.visible_message("[convertee] writhes in pain as the markings below them glow a bloody red!", \
- "AAAAAAAAAAAAAA-")
- SSticker.mode.add_cultist(convertee.mind, 1)
- new /obj/item/tome(get_turf(src))
+ "AAAAAAAAAAAAAA-")
+ SSticker.mode.add_cultist(convertee.mind)
convertee.mind.special_role = "Cultist"
to_chat(convertee, "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible, truth. The veil of reality has been ripped away \
and something evil takes root.")
to_chat(convertee, "Assist your new compatriots in their dark dealings. Your goal is theirs, and theirs is yours. You serve [SSticker.cultdat.entity_title3] above all else. Bring it back.\
")
- return
+
+ if(ishuman(convertee))
+ var/mob/living/carbon/human/H = convertee
+ var/brutedamage = convertee.getBruteLoss()
+ var/burndamage = convertee.getFireLoss()
+ if(brutedamage || burndamage) // If the convertee is injured
+ // Heal 90% of all damage, including robotic limbs
+ H.adjustBruteLoss(-(brutedamage * 0.9), robotic = TRUE)
+ H.adjustFireLoss(-(burndamage * 0.9), robotic = TRUE)
+ if(ismachineperson(H))
+ H.visible_message("A dark force repairs [convertee]!",
+ "Your damage has been repaired. Now spread the blood to others.")
+ else
+ H.visible_message("[convertee]'s wounds heal and close!",
+ "Your wounds have been healed. Now spread the blood to others.")
+ for(var/obj/item/organ/external/E in H.bodyparts)
+ E.mend_fracture()
+ E.internal_bleeding = FALSE
+ for(var/datum/disease/critical/crit in H.viruses) // cure all crit conditions
+ crit.cure()
+
+ H.uncuff()
+ H.Silence(3) //Prevent "HALP MAINT CULT" before you realise you're converted
+
+ var/obj/item/melee/cultblade/dagger/D = new(get_turf(src))
+ if(H.equip_to_slot_if_possible(D, slot_in_backpack, FALSE, TRUE))
+ to_chat(H, "You have a dagger in your backpack. Use it to do [SSticker.cultdat.entity_title1]'s bidding. ")
+ else
+ to_chat(H, "There is a dagger on the floor. Use it to do [SSticker.cultdat.entity_title1]'s bidding.")
/obj/effect/rune/convert/proc/do_sacrifice(mob/living/offering, list/invokers)
var/mob/living/user = invokers[1] //the first invoker is always the user
- if(((ishuman(offering) || isrobot(offering)) && offering.stat != DEAD) || is_sacrifice_target(offering.mind)) //Requires three people to sacrifice living targets
- if(invokers.len < 3)
+
+ if(offering.stat != DEAD || (offering.mind && is_sacrifice_target(offering.mind))) //Requires three people to sacrifice living targets/sacrifice objective
+ if(length(invokers) < 3)
for(var/M in invokers)
to_chat(M, "[offering] is too greatly linked to the world! You need three acolytes!")
fail_invoke()
@@ -410,624 +363,650 @@ GLOBAL_LIST_EMPTY(teleport_runes)
return
var/sacrifice_fulfilled
- var/datum/game_mode/cult/cult_mode = SSticker.mode
+ var/datum/game_mode/gamemode = SSticker.mode
if(offering.mind)
- GLOB.sacrificed.Add(offering.mind)
+ GLOB.sacrificed += offering.mind
if(is_sacrifice_target(offering.mind))
sacrifice_fulfilled = TRUE
+ else
+ GLOB.sacrificed += offering
+
new /obj/effect/temp_visual/cult/sac(loc)
- if(SSticker && SSticker.mode && SSticker.mode.name == "cult")
- cult_mode.harvested++
for(var/M in invokers)
if(sacrifice_fulfilled)
to_chat(M, "\"Yes! This is the one I desire! You have done well.\"")
- cult_mode.additional_phase()
else
if(ishuman(offering) || isrobot(offering))
to_chat(M, "\"I accept this sacrifice.\"")
else
to_chat(M, "\"I accept this meager sacrifice.\"")
+ playsound(offering, 'sound/misc/demon_consume.ogg', 100, TRUE)
- playsound(offering, 'sound/misc/demon_consume.ogg', 100, 1)
- if((ishuman(offering) || isrobot(offering)) && offering.client_mobs_in_contents?.len)
+ if((ishuman(offering) || isrobot(offering) || isbrain(offering)) && offering.mind)
var/obj/item/soulstone/stone = new /obj/item/soulstone(get_turf(src))
- stone.invisibility = INVISIBILITY_MAXIMUM //so it's not picked up during transfer_soul()
- stone.transfer_soul("FORCE", offering, user) //If it cannot be added
+ stone.invisibility = INVISIBILITY_MAXIMUM // So it's not picked up during transfer_soul()
+ stone.transfer_soul("FORCE", offering, user) // If it cannot be added
stone.invisibility = 0
else
- offering.dust()
-
-//Ritual of Dimensional Rending: Calls forth the avatar of Nar-Sie upon the station.
-/obj/effect/rune/narsie
- cultist_name = "Tear Reality (God)"
- cultist_desc = "tears apart dimensional barriers, calling forth your god. Requires 9 invokers."
- invocation = "TOK-LYR RQA-NAP G'OLT-ULOFT!!"
- req_cultists = 9
- icon = 'icons/effects/96x96.dmi'
- color = rgb(125,23,23)
- icon_state = "rune_large"
- pixel_x = -32 //So the big ol' 96x96 sprite shows up right
- pixel_y = -32
- mouse_opacity = MOUSE_OPACITY_ICON //we're huge and easy to click
- scribe_delay = 450 //how long the rune takes to create
- scribe_damage = 40.1 //how much damage you take doing it
- var/used
-
-/obj/effect/rune/narsie/New()
- ..()
- cultist_name = "Summon [SSticker.cultdat ? SSticker.cultdat.entity_name : "your god"]"
- cultist_desc = "tears apart dimensional barriers, calling forth [SSticker.cultdat ? SSticker.cultdat.entity_title3 : "your god"]. Requires 9 invokers."
-
-/obj/effect/rune/narsie/check_icon()
- return
+ if(isrobot(offering))
+ offering.dust() //To prevent the MMI from remaining
+ else
+ offering.gib()
+ playsound(offering, 'sound/magic/disintegrate.ogg', 100, TRUE)
+ if(sacrifice_fulfilled)
+ gamemode.cult_objs.succesful_sacrifice()
+ return TRUE
-/obj/effect/rune/narsie/talismanhide() //can't hide this, and you wouldn't want to
- return
+/obj/effect/rune/teleport
+ cultist_name = "Teleport"
+ cultist_desc = "warps everything above it to another chosen teleport rune."
+ invocation = "Sas'so c'arta forbici!"
+ icon_state = "teleport"
+ req_keyword = TRUE
+ light_power = 4
+ var/obj/effect/temp_visual/cult/portal/inner_portal //The portal "hint" for off-station teleportations
+ var/obj/effect/temp_visual/cult/rune_spawn/rune2/outer_portal
+ var/listkey
-/obj/effect/rune/narsie/invoke(var/list/invokers)
- if(used)
- return
- var/mob/living/user = invokers[1]
- var/datum/game_mode/cult/cult_mode = SSticker.mode
- if(!(CULT_ELDERGOD in cult_mode.objectives))
- message_admins("[key_name_admin(user)] tried to summonn an eldritch horror when the objective was wrong")
- burn_invokers(invokers)
- log_game("Summon Nar-Sie rune failed - improper objective")
- return
- if(!is_station_level(user.z))
- message_admins("[key_name_admin(user)] tried to summon an eldritch horror off station")
- burn_invokers(invokers)
- log_game("Summon Nar-Sie rune failed - off station Z level")
- return
- if(!cult_mode.eldergod)
- for(var/M in invokers)
- to_chat(M, "[SSticker.cultdat.entity_name] is already on this plane!")
- log_game("Summon god rune failed - already summoned")
- return
- //BEGIN THE SUMMONING
- used = 1
- color = rgb(255, 0, 0)
+/obj/effect/rune/teleport/New(loc, set_keyword)
..()
- world << 'sound/effects/dimensional_rend.ogg'
- to_chat(world, "The veil... is... TORN!!!--")
- icon_state = "rune_large_distorted"
- var/turf/T = get_turf(src)
- sleep(40)
- new /obj/singularity/narsie/large(T) //Causes Nar-Sie to spawn even if the rune has been removed
- cult_mode.eldergod = 0
+ var/area/A = get_area(src)
+ var/locname = initial(A.name)
+ listkey = set_keyword ? "[set_keyword] [locname]":"[locname]"
+ GLOB.teleport_runes += src
-/obj/effect/rune/narsie/attackby(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal.
- if((istype(I, /obj/item/tome) && iscultist(user)))
- user.visible_message("[user] begins erasing [src]...", "You begin erasing [src]...")
- if(do_after(user, 50, target = src)) //Prevents accidental erasures.
- log_game("Summon Narsie rune erased by [key_name(user)] with a tome")
- message_admins("[key_name_admin(user)] erased a Narsie rune with a tome")
- return
- if(istype(I, /obj/item/nullrod)) //Begone foul magiks. You cannot hinder me.
- log_game("Summon Narsie rune erased by [key_name(user)] using a null rod")
- message_admins("[key_name_admin(user)] erased a Narsie rune with a null rod")
- return
+/obj/effect/rune/teleport/Destroy()
+ GLOB.teleport_runes -= src
return ..()
+/obj/effect/rune/teleport/invoke(list/invokers)
+ var/mob/living/user = invokers[1] //the first invoker is always the user
+ var/list/potential_runes = list()
+ var/list/teleportnames = list()
+ var/list/duplicaterunecount = list()
+ for(var/I in GLOB.teleport_runes)
+ var/obj/effect/rune/teleport/R = I
+ var/resultkey = R.listkey
+ if(resultkey in teleportnames)
+ duplicaterunecount[resultkey]++
+ resultkey = "[resultkey] ([duplicaterunecount[resultkey]])"
+ else
+ teleportnames += resultkey
+ duplicaterunecount[resultkey] = 1
+ if(R != src && is_level_reachable(R.z))
+ potential_runes[resultkey] = R
-/obj/effect/rune/slaughter
- cultist_name = "Call Forth The Slaughter (Demons)"
- cultist_desc = "Calls forth the doom of an eldritch being. Three slaughter demons will appear to wreak havoc on the station."
- invocation = null
- req_cultists = 9
- color = rgb(125,23,23)
- scribe_delay = 450
- scribe_damage = 40.1 //how much damage you take doing it
- icon = 'icons/effects/96x96.dmi'
- icon_state = "rune_large"
- pixel_x = -32
- pixel_y = -32
-
- var/used = 0
-
-/obj/effect/rune/slaughter/check_icon()
- return
-
-/obj/effect/rune/slaughter/talismanhide() //can't hide this, and you wouldn't want to
- return
+ if(!length(potential_runes))
+ to_chat(user, "There are no valid runes to teleport to!")
+ log_game("Teleport rune failed - no other teleport runes")
+ fail_invoke()
+ return
-/obj/effect/rune/slaughter/attackby(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal.
- if((istype(I, /obj/item/tome) && iscultist(user)))
- user.visible_message("[user.name] begins erasing [src]...", "You begin erasing [src]...")
- if(do_after(user, 50, target = src)) //Prevents accidental erasures.
- log_game("Summon demon rune erased by [key_name(user)] with a tome")
- message_admins("[key_name_admin(user)] erased a demon rune with a tome")
+ if(!is_level_reachable(user.z))
+ to_chat(user, "You are not in the right dimension!")
+ log_game("Teleport rune failed - user in away mission")
+ fail_invoke()
return
- if(istype(I, /obj/item/nullrod)) //Begone foul magiks. You cannot hinder me.
- log_game("Summon demon rune erased by [key_name(user)] using a null rod")
- message_admins("[key_name_admin(user)] erased a demon rune with a null rod")
+
+ var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in potential_runes //we know what key they picked
+ var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to?
+ if(!src || !Adjacent(user) || QDELETED(src) || user.incapacitated() || !actual_selected_rune)
+ fail_invoke()
return
- return ..()
+ var/turf/T = get_turf(src)
+ var/turf/target = get_turf(actual_selected_rune)
+ var/movedsomething = FALSE
+ var/moveuser = FALSE
+ for(var/atom/movable/A in T)
+ if(ishuman(A))
+ if(A != user) // Teleporting someone else
+ INVOKE_ASYNC(src, .proc/teleport_effect, A, T, target)
+ else // Teleporting yourself
+ INVOKE_ASYNC(src, .proc/teleport_effect, user, T, target)
+ if(A.move_resist == INFINITY)
+ continue //object cant move, shouldnt teleport
+ if(A == user)
+ moveuser = TRUE
+ movedsomething = TRUE
+ continue
+ if(!A.anchored)
+ movedsomething = TRUE
+ A.forceMove(target)
-/obj/effect/rune/slaughter/invoke(var/list/invokers)
- if(used)
- return
- var/mob/living/user = invokers[1]
- var/datum/game_mode/cult/cult_mode = SSticker.mode
- if(!(CULT_SLAUGHTER in cult_mode.objectives))
- message_admins("[key_name_admin(user)] tried to summon demons when the objective was wrong")
- burn_invokers(invokers)
- log_game("Summon Demons rune failed - improper objective")
- return
- if(!is_station_level(user.z))
- message_admins("[key_name_admin(user)] tried to summon demons off station")
- burn_invokers(invokers)
- log_game("Summon demons rune failed - off station Z level")
- return
- if(cult_mode.demons_summoned)
- for(var/M in invokers)
- to_chat(M, "Demons are already on this plane!")
- log_game("Summon Demons rune failed - already summoned")
- return
- //BEGIN THE SLAUGHTER
- used = 1
- for(var/mob/living/M in range(1,src))
- if(iscultist(M))
- M.say("TOK-LYR RQA-NAP SHA-NEX!!")
- world << 'sound/effects/dimensional_rend.ogg'
- to_chat(world, "A hellish cacaphony bombards from all around as something awful tears through the world...")
- icon_state = "rune_large_distorted"
- sleep(55)
- to_chat(world, "\"LIBREATE TE EX INFERIS!\"")//Fethas note:I COULDN'T HELP IT OKAY?!
- visible_message("[src] melts away into blood, and three horrific figures emerge from within!")
+ if(movedsomething)
+ ..()
+ if(is_mining_level(z) && !is_mining_level(target.z)) //No effect if you stay on lavaland
+ actual_selected_rune.handle_portal("lava")
+ else if(!is_station_level(z) || istype(get_area(src), /area/space))
+ actual_selected_rune.handle_portal("space", T)
+ user.visible_message("There is a sharp crack of inrushing air, and everything above the rune disappears!",
+ "You[moveuser ? "r vision blurs, and you suddenly appear somewhere else":" send everything above the rune away"].")
+ if(moveuser)
+ user.forceMove(target)
+ else
+ fail_invoke()
+
+/obj/effect/rune/teleport/proc/handle_portal(portal_type, turf/origin)
var/turf/T = get_turf(src)
- new /mob/living/simple_animal/slaughter/cult(T)
- new /mob/living/simple_animal/slaughter/cult(T, pick(NORTH, EAST, SOUTH, WEST))
- new /mob/living/simple_animal/slaughter/cult(T, pick(NORTHEAST, SOUTHEAST, NORTHWEST, SOUTHWEST))
- cult_mode.demons_summoned = 1
- SSshuttle.emergency.request(null, 0.5,null)
- SSshuttle.emergency.canRecall = FALSE
- cult_mode.third_phase()
- qdel(src)
+ if(inner_portal || outer_portal)
+ close_portal() // To avoid stacking descriptions/animations
+ playsound(T, pick('sound/effects/sparks1.ogg', 'sound/effects/sparks2.ogg', 'sound/effects/sparks3.ogg', 'sound/effects/sparks4.ogg'), 100, TRUE, 14)
+ inner_portal = new /obj/effect/temp_visual/cult/portal(T)
+
+ if(portal_type == "space")
+ light_color = color
+ desc += "
A tear in reality reveals a black void interspersed with dots of light... something recently teleported here from space.
"
+
+ // Space base near the station
+ if(is_station_level(origin.z))
+ desc += "The void feels like it's trying to pull you to the [dir2text(get_dir(T, origin))], near the station!"
+ // Space base on another Z-level
+ else
+ desc += "The void feels like it's trying to pull you to the [dir2text(get_dir(T, origin))], in the direction of space sector [origin.z]!"
+
+ else
+ inner_portal.icon_state = "lava"
+ light_color = LIGHT_COLOR_FIRE
+ desc += "
A tear in reality reveals a coursing river of lava... something recently teleported here from the Lavaland Mines!"
+
+ outer_portal = new(T, 60 SECONDS, color)
+ light_range = 4
+ update_light()
+ addtimer(CALLBACK(src, .proc/close_portal), 60 SECONDS, TIMER_UNIQUE)
+
+/obj/effect/rune/teleport/proc/close_portal()
+ qdel(inner_portal)
+ qdel(outer_portal)
+ desc = initial(desc)
+ light_range = 0
+ update_light()
+
+
+//Rune of Empowering : Enables carrying 4 blood spells, greatly reduce blood cost
+/obj/effect/rune/empower
+ cultist_name = "Empower"
+ cultist_desc = "allows cultists to prepare greater amounts of blood magic at far less of a cost."
+ invocation = "H'drak v'loso, mir'kanas verbot!"
+ icon_state = "empower"
+ construct_invoke = FALSE
+/obj/effect/rune/empower/invoke(list/invokers)
+ . = ..()
+ var/mob/living/user = invokers[1] //the first invoker is always the user
+ for(var/datum/action/innate/cult/blood_magic/BM in user.actions)
+ BM.Activate()
-//Rite of Resurrection: Requires two corpses. Revives one and gibs the other.
+//Rite of Resurrection: Requires a dead or inactive cultist. When reviving the dead, you can only perform one revival for every three sacrifices your cult has carried out.
/obj/effect/rune/raise_dead
- cultist_name = "Rite of Resurrection"
- cultist_desc = "requires two corpses, one on the rune and one adjacent to the rune. The one on the rune is brought to life, the other is turned to ash."
- invocation = null //Depends on the name of the user - see below
- icon_state = "1"
+ cultist_name = "Revive"
+ cultist_desc = "requires a dead, mindless, or inactive cultist placed upon the rune. For each three bodies sacrificed to the dark patron, one body will be mended and their mind awoken"
+ invocation = "Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!" //Depends on the name of the user - see below
+ icon_state = "revive"
+ var/static/sacrifices_used = -SOULS_TO_REVIVE // Cultists get one "free" revive
+
+/obj/effect/rune/raise_dead/examine(mob/user)
+ . = ..()
+ if(iscultist(user) || user.stat == DEAD)
+ . += "Sacrifices unrewarded: [length(GLOB.sacrificed) - sacrifices_used]"
+ . += "Sacrifice cost per ressurection:There are no eligible sacrifices nearby!")
- log_game("Raise Dead rune failed - no catalyst corpse")
- return
- mob_to_sacrifice = input(user, "Choose a corpse to sacrifice.", "Corpse to Sacrifice") as null|anything in potential_sacrifice_mobs
- if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated() || !mob_to_sacrifice || rune_in_use)
- return
+
+ rune_in_use = TRUE
for(var/mob/living/M in T.contents)
- if(M.stat == DEAD)
- potential_revive_mobs.Add(M)
- if(!potential_revive_mobs.len)
- to_chat(user, "There is no eligible revival target on the rune!")
- log_game("Raise Dead rune failed - no corpse to revive")
- return
- mob_to_revive = input(user, "Choose a corpse to revive.", "Corpse to Revive") as null|anything in potential_revive_mobs
- if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated() || rune_in_use || !mob_to_revive)
- return
- if(!in_range(mob_to_sacrifice,src))
- to_chat(user, "The sacrificial target has been moved!")
+ if(iscultist(M) && (M.stat == DEAD || !M.client || M.client.is_afk()))
+ potential_revive_mobs |= M
+ if(!length(potential_revive_mobs))
+ to_chat(user, "There are no dead cultists on the rune!")
+ log_game("Raise Dead rune failed - no cultists to revive")
fail_invoke()
- log_game("Raise Dead rune failed - catalyst corpse moved")
- return
- if(!(mob_to_revive in T.contents))
- to_chat(user, "The corpse to revive has been moved!")
- fail_invoke()
- log_game("Raise Dead rune failed - revival target moved")
return
- if(mob_to_sacrifice.stat != DEAD)
- to_chat(user, "The sacrificial target must be dead!")
+ if(length(potential_revive_mobs) > 1)
+ mob_to_revive = input(user, "Choose a cultist to revive.", "Cultist to Revive") as null|anything in potential_revive_mobs
+ else // If there's only one, no need for a menu
+ mob_to_revive = potential_revive_mobs[1]
+ if(!validness_checks(mob_to_revive, user))
fail_invoke()
- log_game("Raise Dead rune failed - catalyst corpse is not dead")
return
- rune_in_use = 1
- if(user.name == "Herbert West")
- user.say("To life, to life, I bring them!")
- else
- user.say("Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!")
+
..()
- mob_to_sacrifice.visible_message("[mob_to_sacrifice]'s body rises into the air, connected to [mob_to_revive] by a glowing tendril!")
- mob_to_revive.Beam(mob_to_sacrifice,icon_state="sendbeam",time=20)
- sleep(20)
- if(!mob_to_sacrifice || !in_range(mob_to_sacrifice, src))
- mob_to_sacrifice.visible_message("[mob_to_sacrifice] disintegrates into a pile of bones.")
- return
- mob_to_sacrifice.dust()
- if(!mob_to_revive || mob_to_revive.stat != DEAD)
- visible_message("The glowing tendril snaps against the rune with a shocking crack.")
- rune_in_use = 0
- return
- mob_to_revive.revive() //This does remove disabilities and such, but the rune might actually see some use because of it!
+ if(mob_to_revive.stat == DEAD)
+ var/diff = length(GLOB.sacrificed) - SOULS_TO_REVIVE - sacrifices_used
+ if(diff < 0)
+ to_chat(user, "Your cult must carry out [abs(diff)] more sacrifice\s before it can revive another cultist!")
+ fail_invoke()
+ return
+ sacrifices_used += SOULS_TO_REVIVE
+ mob_to_revive.revive()
+ mob_to_revive.grab_ghost()
+
+ if(!mob_to_revive.client || mob_to_revive.client.is_afk())
+ set waitfor = FALSE
+ to_chat(user, "[mob_to_revive] was revived, but their mind is lost! Seeking a lost soul to replace it.")
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Would you like to play as a revived Cultist?", ROLE_CULTIST, TRUE, poll_time = 20 SECONDS, source = /obj/item/melee/cultblade/dagger)
+ if(length(candidates))
+ var/mob/dead/observer/C = pick(candidates)
+ to_chat(mob_to_revive.mind, "Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form.")
+ message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(mob_to_revive)]) to replace an AFK player.")
+ mob_to_revive.ghostize(FALSE)
+ mob_to_revive.key = C.key
+ else
+ fail_invoke()
+ return
+
+ SEND_SOUND(mob_to_revive, 'sound/ambience/antag/bloodcult.ogg')
to_chat(mob_to_revive, "\"PASNAR SAVRAE YAM'TOTH. Arise.\"")
mob_to_revive.visible_message("[mob_to_revive] draws in a huge breath, red light shining from [mob_to_revive.p_their()] eyes.", \
"You awaken suddenly from the void. You're alive!")
- rune_in_use = 0
+ rune_in_use = FALSE
+/obj/effect/rune/raise_dead/proc/validness_checks(mob/living/target_mob, mob/living/user)
+ if(QDELETED(src))
+ return FALSE
+ if(QDELETED(user))
+ return FALSE
+ if(!Adjacent(user) || user.incapacitated())
+ return FALSE
+ if(QDELETED(target_mob))
+ return FALSE
+ var/turf/T = get_turf(src)
+ if(target_mob.loc != T)
+ to_chat(user, "The cultist to revive has been moved!")
+ log_game("Raise Dead rune failed - revival target moved")
+ return FALSE
+ return TRUE
/obj/effect/rune/raise_dead/fail_invoke()
..()
rune_in_use = FALSE
- for(var/mob/living/M in range(1,src))
- if(M.stat == DEAD)
+ for(var/mob/living/M in range(0, src))
+ if(iscultist(M) && M.stat == DEAD)
M.visible_message("[M] twitches.")
-
-//Rite of Disruption: Emits an EMP blast.
-/obj/effect/rune/emp
- cultist_name = "Rite of Disruption"
- cultist_desc = "emits a large electromagnetic pulse, increasing in size for each cultist invoking it, hindering electronics and disabling silicons."
- invocation = "Ta'gh fara'qha fel d'amar det!"
- icon_state = "5"
- allow_excess_invokers = 1
-
-/obj/effect/rune/emp/invoke(var/list/invokers)
- var/turf/E = get_turf(src)
- ..()
- visible_message("[src] glows blue for a moment before vanishing.")
- switch(invokers.len)
- if(1 to 2)
- playsound(E, 'sound/items/welder2.ogg', 25, 1)
- for(var/M in invokers)
- to_chat(M, "You feel a minute vibration pass through you...")
- if(3 to 6)
- playsound(E, 'sound/effects/EMPulse.ogg', 50, 1)
- for(var/M in invokers)
- to_chat(M, "Your hair stands on end as a shockwave emanates from the rune!")
- if(7 to INFINITY)
- playsound(E, 'sound/effects/EMPulse.ogg', 100, 1)
- for(var/M in invokers)
- var/mob/living/L = M
- to_chat(L, "You chant in unison and a colossal burst of energy knocks you backward!")
- L.Weaken(2)
- qdel(src) //delete before pulsing because it's a delay reee
- empulse(E, 9*invokers.len, 12*invokers.len, 1) // Scales now, from a single room to most of the station depending on # of chanters
-
-//Rite of Astral Communion: Separates one's spirit from their body. They will take damage while it is active.
-/obj/effect/rune/astral
- cultist_name = "Astral Communion"
- cultist_desc = "severs the link between one's spirit and body. This effect is taxing and one's physical body will take damage while this is active."
- invocation = "Fwe'sh mah erl nyag r'ya!"
- icon_state = "6"
- rune_in_use = 0 //One at a time, please!
- construct_invoke = 0
- var/mob/living/affecting = null
-
-/obj/effect/rune/astral/examine(mob/user)
- . = ..()
- if(affecting)
- . += "A translucent field encases [user] above the rune!"
-
-/obj/effect/rune/astral/can_invoke(mob/living/user)
- if(rune_in_use)
- to_chat(user, "[src] cannot support more than one body!")
- log_game("Astral Communion rune failed - more than one user")
- return list()
- var/turf/T = get_turf(src)
- if(!(user in T.contents))
- to_chat(user, "You must be standing on top of [src]!")
- log_game("Astral Communion rune failed - user not standing on rune")
- return list()
- return ..()
-
-/obj/effect/rune/astral/invoke(var/list/invokers)
- var/mob/living/user = invokers[1]
- ..()
- var/turf/T = get_turf(src)
- rune_in_use = 1
- affecting = user
- user.color = "#7e1717"
- user.visible_message("[user] freezes statue-still, glowing an unearthly red.", \
- "You see what lies beyond. All is revealed. While this is a wondrous experience, your physical form will waste away in this state. Hurry...")
- user.ghostize(1)
- while(user)
- if(!affecting)
- visible_message("[src] pulses gently before falling dark.")
- affecting = null //In case it's assigned to a number or something
- rune_in_use = 0
- return
- affecting.apply_damage(1, BRUTE)
- if(!(user in T.contents))
- user.visible_message("A spectral tendril wraps around [user] and pulls [user.p_them()] back to the rune!")
- Beam(user,icon_state="drainbeam",time=2)
- user.forceMove(get_turf(src)) //NO ESCAPE :^)
- if(user.key)
- user.visible_message("[user] slowly relaxes, the glow around [user.p_them()] dimming.", \
- "You are re-united with your physical form. [src] releases its hold over you.")
- user.color = initial(user.color)
- user.Weaken(3)
- rune_in_use = 0
- affecting = null
- user.update_sight()
- return
- if(user.stat == UNCONSCIOUS)
- if(prob(10))
- var/mob/dead/observer/G = user.get_ghost()
- if(G)
- to_chat(G, "You feel the link between you and your body weakening... you must hurry!")
- if(user.stat == DEAD)
- user.color = initial(user.color)
- rune_in_use = 0
- affecting = null
- var/mob/dead/observer/G = user.get_ghost()
- if(G)
- to_chat(G, "You suddenly feel your physical form pass on. [src]'s exertion has killed you!")
- return
- sleep(10)
- rune_in_use = 0
-
-
//Rite of the Corporeal Shield: When invoked, becomes solid and cannot be passed. Invoke again to undo.
/obj/effect/rune/wall
- cultist_name = "Rite of the Corporeal Shield"
- cultist_desc = "when invoked, makes an invisible wall to block passage. Can be invoked again to reverse this."
+ cultist_name = "Barrier"
+ cultist_desc = "when invoked, makes a temporary invisible wall to block passage. Can be destroyed by brute force. Can be invoked again to reverse this."
invocation = "Khari'd! Eske'te tannin!"
- icon_state = "1"
- invoke_damage = 2
+ icon_state = "barrier"
+ ///The barrier summoned by the rune when invoked. Tracked as a variable to prevent refreshing the barrier's integrity. shieldgen.dm
+ var/obj/machinery/shield/cult/barrier/B
-/obj/effect/rune/wall/examine(mob/user)
+/obj/effect/rune/wall/Initialize(mapload)
. = ..()
- if(density)
- . += "There is a barely perceptible shimmering of the air above [src]."
+ GLOB.wall_runes += src
+
+/obj/effect/rune/wall/Destroy()
+ GLOB.wall_runes -= src
+ if(B && !QDELETED(B))
+ QDEL_NULL(B)
+ return ..()
-/obj/effect/rune/wall/invoke(var/list/invokers)
+/obj/effect/rune/wall/invoke(list/invokers)
var/mob/living/user = invokers[1]
..()
- density = !density
- user.visible_message("[user] places [user.p_their()] hands on [src], and [density ? "the air above it begins to shimmer" : "the shimmer above it fades"].", \
- "You channel your life energy into [src], [density ? "preventing" : "allowing"] passage above it.")
+ if(!B)
+ B = new /obj/machinery/shield/cult/barrier(loc)
+ B.parent_rune = src
+ B.Toggle()
if(iscarbon(user))
var/mob/living/carbon/C = user
- C.apply_damage(2, BRUTE, pick("l_arm", "r_arm"))
-
+ C.cult_self_harm(2)
//Rite of Joined Souls: Summons a single cultist.
/obj/effect/rune/summon
- cultist_name = "Rite of Joined Souls"
- cultist_desc = "summons a single cultist to the rune. Requires 2 invokers."
+ cultist_name = "Summon Cultist"
+ cultist_desc = "summons a single cultist to the rune. (Cannot summon restrained cultists!)"
invocation = "N'ath reth sh'yro eth d'rekkathnor!"
req_cultists = 2
- allow_excess_invokers = 1
- icon_state = "5"
- invoke_damage = 5
- var/summoning = FALSE
- var/summontime = 0
+ invoke_damage = 10
+ icon_state = "summon"
-/obj/effect/rune/summon/invoke(var/list/invokers)
+/obj/effect/rune/summon/invoke(list/invokers)
var/mob/living/user = invokers[1]
var/list/cultists = list()
+
for(var/datum/mind/M in SSticker.mode.cult)
if(!(M.current in invokers) && M.current && M.current.stat != DEAD)
- cultists |= M.current
- var/mob/living/cultist_to_summon = input(user, "Who do you wish to call to [src]?", "Followers of [SSticker.cultdat.entity_title3]") as null|anything in cultists
- if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated())
+ cultists[M.current.real_name] = M.current
+ var/input = input(user, "Who do you wish to call to [src]?", "Acolytes") as null|anything in cultists
+ var/mob/living/cultist_to_summon = cultists[input]
+ if(!src || QDELETED(src) || !Adjacent(user) || user.incapacitated())
+ return
+ if(!cultist_to_summon)
+ log_game("Summon Cultist rune failed - no target")
return
- if(summoning)
- to_chat(user, "You are already summoning a target!")
+ if(cultist_to_summon.stat == DEAD)
+ to_chat(user, "[cultist_to_summon] has died!")
fail_invoke()
+ log_game("Summon Cultist rune failed - target died")
return
-
- if(!cultist_to_summon)
- to_chat(user, "You require a summoning target!")
+ if(cultist_to_summon.pulledby || cultist_to_summon.buckled)
+ to_chat(user, "[cultist_to_summon] is being held in place!")
+ to_chat(cultist_to_summon, "You feel a tugging sensation, but you are being held in place!")
fail_invoke()
- log_game("Summon Cultist rune failed - no target")
+ log_game("Summon Cultist rune failed - target restrained")
return
if(!iscultist(cultist_to_summon))
- to_chat(user, "[cultist_to_summon] is not a follower of [SSticker.cultdat.entity_title3]!")
+ to_chat(user, "[cultist_to_summon] is not a follower of the [SSticker.cultdat.entity_title3]!")
fail_invoke()
- log_game("Summon Cultist rune failed - no target")
+ log_game("Summon Cultist rune failed - target was deconverted")
return
- if(!is_level_reachable(cultist_to_summon.z))
+ if(is_away_level(cultist_to_summon.z))
to_chat(user, "[cultist_to_summon] is not in our dimension!")
fail_invoke()
log_game("Summon Cultist rune failed - target in away mission")
return
- var/hard_summon = (cultist_to_summon.reagents && cultist_to_summon.reagents.has_reagent("holywater")) || cultist_to_summon.restrained()
- if(hard_summon && invokers.len < 3)
- to_chat(user, "The summoning of [cultist_to_summon] is being blocked somehow! You need 3 invokers to counter it!")
- fail_invoke()
- new /obj/effect/temp_visual/cult/sparks(get_turf(cultist_to_summon)) //observer warning
- log_game("Summon Cultist rune failed - holywater in target")
- return
- summoning = TRUE
- ..()
- if(hard_summon)
- summontime = 20
-
- if(do_after(user, summontime, target = src))
- summoning = FALSE // Here incase the proc stops after the qdel
- cultist_to_summon.visible_message("[cultist_to_summon] suddenly disappears in a flash of red light!", \
- "Overwhelming vertigo consumes you as you are hurled through the air!")
- visible_message("A foggy shape materializes atop [src] and solidifies into [cultist_to_summon]!")
- cultist_to_summon.forceMove(get_turf(src))
- qdel(src)
- summoning = FALSE
+ cultist_to_summon.visible_message("[cultist_to_summon] suddenly disappears in a flash of red light!", \
+ "Overwhelming vertigo consumes you as you are hurled through the air!")
+ ..()
+ INVOKE_ASYNC(src, .proc/teleport_effect, cultist_to_summon, get_turf(cultist_to_summon), src)
+ visible_message("[src] begins to bubble and rises into the form of [cultist_to_summon]!")
+ cultist_to_summon.forceMove(get_turf(src))
+ qdel(src)
-//Rite of Boiling Blood: Deals extremely high amounts of damage to non-cultists nearby
+/**
+ * # Blood Boil Rune
+ *
+ * When invoked deals up to 30 burn damage to nearby non-cultists and sets them on fire.
+ *
+ * On activation the rune charges for six seconds, changing colour, glowing, and giving out a warning to all nearby mobs.
+ * After the charging period the rune burns any non-cultists in view and sets them on fire. After another short wait it does the same again with slightly higher damage.
+ * If the cultists channeling the rune move away or are stunned at any point, the rune is deleted. So it can be countered pretty easily with flashbangs.
+ */
/obj/effect/rune/blood_boil
cultist_name = "Boil Blood"
- cultist_desc = "boils the blood of non-believers who can see the rune, dealing extreme amounts of damage. Requires 3 invokers."
+ cultist_desc = "boils the blood of non-believers who can see the rune, rapidly dealing extreme amounts of damage. Requires 2 invokers channeling the rune."
invocation = "Dedo ol'btoh!"
- icon_state = "4"
- construct_invoke = 0
- req_cultists = 3
+ icon_state = "blood_boil"
+ light_color = LIGHT_COLOR_LAVA
+ req_cultists = 2
invoke_damage = 15
+ construct_invoke = FALSE
+ var/tick_damage = 10 // 30 burn damage total + damage taken by being on fire/overheating
+ rune_in_use = FALSE
-/obj/effect/rune/blood_boil/do_invoke_glow()
- return
-
-/obj/effect/rune/blood_boil/invoke(var/list/invokers)
+/obj/effect/rune/blood_boil/invoke(list/invokers)
+ if(rune_in_use)
+ return
..()
+ rune_in_use = TRUE
var/turf/T = get_turf(src)
- visible_message("[src] briefly bubbles before exploding!")
- for(var/mob/living/carbon/C in viewers(T))
- if(!iscultist(C))
- var/obj/item/nullrod/N = C.null_rod_check()
- if(N)
- to_chat(C, "\The [N] suddenly burns hotly before returning to normal!")
+ var/list/targets = list()
+ for(var/mob/living/L in viewers(T))
+ if(!iscultist(L) && L.blood_volume && !ismachineperson(L))
+ var/atom/I = L.null_rod_check()
+ if(I)
+ if(isitem(I))
+ to_chat(L, "[I] suddenly burns hotly before returning to normal!")
continue
- to_chat(C, "Your blood boils in your veins!")
- C.take_overall_damage(45,45)
- C.Stun(7)
+ targets += L
+
+ // Six seconds buildup
+ visible_message("A haze begins to form above [src]!")
+ animate(src, color = "#FC9A6D", time = 6 SECONDS)
+ set_light(6, 1, color)
+ sleep(6 SECONDS)
+ visible_message("[src] turns a bright, burning orange!")
+ if(!burn_check())
+ return
+
+ for(var/I in targets)
+ to_chat(I, "Your blood boils in your veins!")
+ do_area_burn(T, 1)
+ animate(src, color = "#FFDF80", time = 5 SECONDS)
+ sleep(5 SECONDS)
+ if(!burn_check())
+ return
+
+ do_area_burn(T, 2)
+ animate(src, color = "#FFFFFF", time = 5 SECONDS)
+ sleep(5 SECONDS)
+ if(!burn_check())
+ return
+
+ do_area_burn(T, 3)
qdel(src)
- explosion(T, -1, 0, 1, 5)
-//Deals brute damage to all targets on the rune and heals the invoker for each target drained.
-/obj/effect/rune/leeching
- cultist_name = "Drain Life"
- cultist_desc = "drains the life of all targets on the rune, restoring life to the user."
- invocation = "Yu'gular faras desdae. Umathar uf'kal thenar!"
- icon_state = "3"
+/obj/effect/rune/blood_boil/proc/do_area_burn(turf/T, iteration)
+ var/multiplier = iteration / 2 // Iteration 1 = 0.5, Iteration 2 = 1, etc.
+ set_light(6, 1 * iteration, color)
+ for(var/mob/living/L in viewers(T))
+ if(!iscultist(L) && L.blood_volume && !ismachineperson(L))
+ if(L.null_rod_check())
+ continue
+ L.take_overall_damage(0, tick_damage * multiplier)
+ L.adjust_fire_stacks(2)
+ L.IgniteMob()
+ playsound(src, 'sound/effects/bamf.ogg', 100, TRUE)
+ do_invoke_glow()
+ sleep(0.6 SECONDS) // Only one 'animate()' can play at once, so this waits for the pulse to finish
-/obj/effect/rune/leeching/can_invoke(mob/living/user)
- if(world.time <= user.next_move)
- return list()
- var/turf/T = get_turf(src)
- var/list/potential_targets = list()
- for(var/mob/living/carbon/M in T.contents - user)
- if(M.stat != DEAD)
- potential_targets += M
- if(!potential_targets.len)
- to_chat(user, "There must be at least one valid target on the rune!")
- log_game("Leeching rune failed - no valid targets")
- return list()
- return ..()
+/obj/effect/rune/blood_boil/proc/burn_check()
+ . = TRUE
+ if(QDELETED(src))
+ return FALSE
+ var/list/cultists = list()
+ for(var/mob/living/M in range(1, src)) // Get all cultists currently in range
+ if(iscultist(M) && !M.incapacitated())
+ cultists += M
+
+ if(length(cultists) < req_cultists) // Stop the rune there's not enough invokers
+ visible_message("[src] loses its glow and dissipates!")
+ qdel(src)
-/obj/effect/rune/leeching/invoke(var/list/invokers)
- var/mob/living/user = invokers[1]
- user.changeNext_move(CLICK_CD_MELEE)
- ..()
- var/turf/T = get_turf(src)
- for(var/mob/living/carbon/M in T.contents - user)
- if(M.stat != DEAD)
- var/drained_amount = rand(10,20)
- M.apply_damage(drained_amount, BRUTE, "chest")
- user.adjustBruteLoss(-drained_amount)
- to_chat(M, "You feel extremely weak.")
- user.Beam(T,icon_state="drainbeam",time=5)
- user.visible_message("Blood flows from the rune into [user]!", \
- "Blood flows into you, healing your wounds and revitalizing your spirit.")
-
-
-//Rite of Spectral Manifestation: Summons a ghost on top of the rune as a cultist human with no items. User must stand on the rune at all times, and takes damage for each summoned ghost.
/obj/effect/rune/manifest
- cultist_name = "Rite of Spectral Manifestation"
- cultist_desc = "manifests a spirit as a servant of your god. The invoker must not move from atop the rune, and will take damage for each summoned spirit."
+ cultist_name = "Spirit Realm"
+ cultist_desc = "manifests a spirit servant of the Dark One and allows you to ascend as a spirit yourself. The invoker must not move from atop the rune, and will take damage for each summoned spirit."
invocation = "Gal'h'rfikk harfrandid mud'gib!" //how the fuck do you pronounce this
- icon_state = "6"
- construct_invoke = 0
- color = rgb(200, 0, 0)
- var/list/summoned_guys = list()
- var/ghost_limit = 5
+ icon_state = "spirit_realm"
+ construct_invoke = FALSE
+ var/mob/dead/observer/ghost = null //The cult ghost of the user
+ var/default_ghost_limit = 4 //Lowered by the amount of cult objectives done
+ var/minimum_ghost_limit = 2 //But cant go lower than this
var/ghosts = 0
- invoke_damage = 10
-
-/obj/effect/rune/manifest/New(loc)
- ..()
- cultist_desc = "manifests a spirit as a servant of [SSticker.cultdat.entity_title3]. The invoker must not move from atop the rune, and will take damage for each summoned spirit."
- notify_ghosts("Manifest rune created in [get_area(src)].", ghost_sound='sound/effects/ghost2.ogg', source = src)
+/obj/effect/rune/manifest/examine(mob/user)
+ . = ..()
+ if(iscultist(user) || user.stat == DEAD)
+ . += "Amount of ghosts summoned: [ghosts]"
+ . += "Maximum amount of ghosts: [clamp(default_ghost_limit - SSticker.mode.cult_objs.sacrifices_done, minimum_ghost_limit, default_ghost_limit)]"
+ . += "Lowers to a minimum of [minimum_ghost_limit] for each objective accomplished."
-/obj/effect/rune/manifest/can_invoke(mob/living/user)
- if(ghosts >= ghost_limit)
- to_chat(user, "You are sustaining too many ghosts to summon more!")
- fail_invoke()
- log_game("Manifest rune failed - too many summoned ghosts")
- return list()
+/obj/effect/rune/manifest/invoke(list/invokers)
+ . = ..()
+ var/mob/living/user = invokers[1]
+ var/turf/T = get_turf(src)
if(!(user in get_turf(src)))
- to_chat(user,"You must be standing on [src]!")
+ to_chat(user, "You must be standing on [src]!")
fail_invoke()
log_game("Manifest rune failed - user not standing on rune")
- return list()
+ return
if(user.has_status_effect(STATUS_EFFECT_SUMMONEDGHOST))
- to_chat(user, "Ghosts can't summon more ghosts!")
+ to_chat(user, "Ghosts can't summon more ghosts!")
fail_invoke()
log_game("Manifest rune failed - user is a ghost")
- return list()
+ return
+
+ var/choice = alert(user, "You tear open a connection to the spirit realm...", null, "Summon a Cult Ghost", "Ascend as a Dark Spirit", "Cancel")
+ if(choice == "Summon a Cult Ghost")
+ if(!is_station_level(z) || istype(get_area(src), /area/space))
+ to_chat(user, "The veil is not weak enough here to manifest spirits, you must be on station!")
+ fail_invoke()
+ log_game("Manifest rune failed - not on station")
+ return
+ if(user.health <= 40)
+ to_chat(user, "Your body is too weak to manifest spirits, heal yourself first.")
+ fail_invoke()
+ log_game("Manifest rune failed - not enough health")
+ return list()
+ if(ghosts >= clamp(default_ghost_limit - SSticker.mode.cult_objs.sacrifices_done, minimum_ghost_limit, default_ghost_limit))
+ to_chat(user, "You are sustaining too many ghosts to summon more!")
+ fail_invoke()
+ log_game("Manifest rune failed - too many summoned ghosts")
+ return list()
+ summon_ghosts(user, T)
+
+ else if(choice == "Ascend as a Dark Spirit")
+ ghostify(user, T)
+
+
+/obj/effect/rune/manifest/proc/summon_ghosts(mob/living/user, turf/T)
+ notify_ghosts("Manifest rune created in [get_area(src)].", ghost_sound = 'sound/effects/ghost2.ogg', source = src)
var/list/ghosts_on_rune = list()
- for(var/mob/dead/observer/O in get_turf(src))
- if(O.client && !jobban_isbanned(O, ROLE_CULTIST) && !jobban_isbanned(O, ROLE_SYNDICATE))
- ghosts_on_rune |= O
- if(!ghosts_on_rune.len)
+ for(var/mob/dead/observer/O in T)
+ if(O.client && !iscultist(O) && !jobban_isbanned(O, ROLE_CULTIST) && !O.has_enabled_antagHUD && !QDELETED(src) && !QDELETED(O))
+ ghosts_on_rune += O
+ if(!length(ghosts_on_rune))
to_chat(user, "There are no spirits near [src]!")
fail_invoke()
log_game("Manifest rune failed - no nearby ghosts")
return list()
- return ..()
-/obj/effect/rune/manifest/invoke(var/list/invokers)
- var/mob/living/user = invokers[1]
- var/list/ghosts_on_rune = list()
- for(var/mob/dead/observer/O in get_turf(src))
- if(O.client && !jobban_isbanned(O, ROLE_CULTIST) && !jobban_isbanned(O, ROLE_SYNDICATE))
- ghosts_on_rune |= O
var/mob/dead/observer/ghost_to_spawn = pick(ghosts_on_rune)
- var/mob/living/carbon/human/new_human = new(get_turf(src))
+ var/mob/living/carbon/human/new_human = new(T)
new_human.real_name = ghost_to_spawn.real_name
+ new_human.key = ghost_to_spawn.key
new_human.alpha = 150 //Makes them translucent
new_human.equipOutfit(/datum/outfit/ghost_cultist) //give them armor
- new_human.apply_status_effect(STATUS_EFFECT_SUMMONEDGHOST) //ghosts can't summon more ghosts
- new_human.color = "grey" //heh..cult greytide...litterly...
- ..()
-
- playsound(src, 'sound/misc/exit_blood.ogg', 50, 1)
- visible_message("A cloud of red mist forms above [src], and from within steps... a humanoid shape.")
- to_chat(user, "Your blood begins flowing into [src]. You must remain in place and conscious to maintain the forms of those summoned. This will hurt you slowly but surely...")
- var/obj/machinery/shield/N = new(get_turf(src))
- N.name = "Invoker's Shield"
- N.desc = "A weak shield summoned by cultists to protect them while they carry out delicate rituals"
- N.color = "red"
- N.health = 20
- N.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- new_human.key = ghost_to_spawn.key
- SSticker.mode.add_cultist(new_human.mind, FALSE)
- new_human.mind.special_role = SPECIAL_ROLE_CULTIST
- summoned_guys |= new_human
+ new_human.apply_status_effect(STATUS_EFFECT_SUMMONEDGHOST) //ghosts can't summon more ghosts, also lets you see actual ghosts
ghosts++
- to_chat(new_human, "You are a servant of [SSticker.cultdat.entity_title3]. You have been made semi-corporeal by the cult of [SSticker.cultdat.entity_name], and you are to serve them at all costs.")
-
- while(user in get_turf(src))
- if(user.stat)
+ playsound(src, 'sound/misc/exit_blood.ogg', 50, TRUE)
+ user.visible_message("A cloud of red mist forms above [src], and from within steps... a [new_human.gender == FEMALE ? "wo" : ""]man.",
+ "Your blood begins flowing into [src]. You must remain in place and conscious to maintain the forms of those summoned. This will hurt you slowly but surely...")
+
+ var/obj/machinery/shield/cult/weak/shield = new(T)
+ SSticker.mode.add_cultist(new_human.mind, 0)
+ to_chat(new_human, "You are a servant of the [SSticker.cultdat.entity_title3]. You have been made semi-corporeal by the cult of [SSticker.cultdat.entity_name], and you are to serve them at all costs.")
+
+ while(!QDELETED(src) && !QDELETED(user) && !QDELETED(new_human) && (user in T))
+ if(new_human.InCritical())
+ to_chat(user, "You feel your connection to [new_human.real_name] severs as they are destroyed.")
+ if(ghost)
+ to_chat(ghost, "You feel your connection to [new_human.real_name] severs as they are destroyed.")
break
- if(!atoms_share_level(get_turf(new_human), get_turf(user)))
+ if(user.stat || user.health <= 40)
+ to_chat(user, "Your body can no longer sustain the connection, and your link to the spirit realm fades.")
+ if(ghost)
+ to_chat(ghost, "Your body is damaged and your connection to the spirit realm weakens, any ghost you may have manifested are destroyed.")
break
user.apply_damage(0.1, BRUTE)
- sleep(3)
+ user.apply_damage(0.1, BURN)
+ sleep(2) //Takes two pylons to sustain the damage taken by summoning one ghost
- qdel(N)
+ qdel(shield)
+ ghosts--
if(new_human)
- new_human.visible_message("[new_human] suddenly dissolves into bones and ashes.", \
+ new_human.visible_message("[new_human] suddenly dissolves into bones and ashes.",
"Your link to the world fades. Your form breaks apart.")
- for(var/obj/I in new_human)
+ for(var/obj/item/I in new_human.get_all_slots())
new_human.unEquip(I)
- summoned_guys -= new_human
- ghosts--
- SSticker.mode.remove_cultist(new_human.mind, FALSE)
+ SSticker.mode.remove_cultist(new_human, FALSE)
new_human.dust()
-/obj/effect/rune/manifest/Destroy()
- for(var/mob/living/carbon/human/guy in summoned_guys)
- for(var/obj/I in guy)
- guy.unEquip(I)
- guy.dust()
- summoned_guys.Cut()
+/obj/effect/rune/manifest/proc/ghostify(mob/living/user, turf/T)
+ user.add_atom_colour(RUNE_COLOR_DARKRED, ADMIN_COLOUR_PRIORITY)
+ user.visible_message("[user] freezes statue-still, glowing an unearthly red.",
+ "You see what lies beyond. All is revealed. In this form you find that your voice booms above all others.")
+ ghost = user.ghostize(TRUE)
+ var/datum/action/innate/cult/comm/spirit/CM = new
+ var/datum/action/innate/cult/check_progress/V = new
+ //var/datum/action/innate/cult/ghostmark/GM = new
+ ghost.name = "Dark Spirit of [ghost.name]"
+ ghost.color = "red"
+ CM.Grant(ghost)
+ V.Grant(ghost)
+ //GM.Grant(ghost)
+ while(!QDELETED(user))
+ if(!(user in T))
+ user.visible_message("A spectral tendril wraps around [user] and pulls [user.p_them()] back to the rune!")
+ Beam(user, icon_state = "drainbeam", time = 2)
+ user.forceMove(get_turf(src)) //NO ESCAPE :^)
+ if(user.key)
+ user.visible_message("[user] slowly relaxes, the glow around [user.p_them()] dimming.",
+ "You are re-united with your physical form. [src] releases its hold over you.")
+ user.Weaken(3)
+ break
+ if(user.health <= 10)
+ to_chat(ghost, "Your body can no longer sustain the connection!")
+ break
+ sleep(5)
+ CM.Remove(ghost)
+ V.Remove(ghost)
+ //GM.Remove(ghost)
+ user.remove_atom_colour(ADMIN_COLOUR_PRIORITY, RUNE_COLOR_DARKRED)
+ user.grab_ghost()
+ user = null
+ rune_in_use = FALSE
+
+
+//Ritual of Dimensional Rending: Calls forth the avatar of Nar'Sie upon the station.
+/obj/effect/rune/narsie
+ cultist_name = "Tear Veil"
+ cultist_desc = "tears apart dimensional barriers, calling forth your god."
+ invocation = "TOK-LYR RQA-NAP G'OLT-ULOFT!!"
+ req_cultists = 9
+ icon = 'icons/effects/96x96.dmi'
+ icon_state = "rune_large"
+ pixel_x = -32 //So the big ol' 96x96 sprite shows up right
+ pixel_y = -32
+ mouse_opacity = MOUSE_OPACITY_ICON //we're huge and easy to click
+ scribe_delay = 45 SECONDS //how long the rune takes to create
+ scribe_damage = 10 //how much damage you take doing it
+ var/used = FALSE
+
+/obj/effect/rune/narsie/New()
+ ..()
+ cultist_name = "Summon [SSticker.cultdat ? SSticker.cultdat.entity_name : "your god"]"
+ cultist_desc = "tears apart dimensional barriers, calling forth [SSticker.cultdat ? SSticker.cultdat.entity_title3 : "your god"]."
+
+/obj/effect/rune/narsie/check_icon()
+ return
+
+/obj/effect/rune/narsie/cult_conceal() //can't hide this, and you wouldn't want to
+ return
+
+/obj/effect/rune/narsie/invoke(list/invokers)
+ if(used)
+ return
+ var/mob/living/user = invokers[1]
+ var/datum/game_mode/gamemode = SSticker.mode
+ if(!is_station_level(user.z))
+ message_admins("[key_name_admin(user)] tried to summon an eldritch horror off station")
+ log_game("Summon Nar'Sie rune failed - off station Z level")
+ return
+ if(gamemode.cult_objs.cult_status == NARSIE_HAS_RISEN)
+ for(var/M in invokers)
+ to_chat(M, "\"I am already here. There is no need to try to summon me now.\"")
+ log_game("Summon god rune failed - already summoned")
+ return
+
+ //BEGIN THE SUMMONING
+ gamemode.cult_objs.succesful_summon()
+ used = TRUE
+ color = rgb(255, 0, 0)
+ ..()
+ SEND_SOUND(world, 'sound/effects/dimensional_rend.ogg')
+ to_chat(world, "The veil... is... TORN!!!--")
+ icon_state = "rune_large_distorted"
+ var/turf/T = get_turf(src)
+ sleep(40)
+ new /obj/singularity/narsie/large(T) //Causes Nar'Sie to spawn even if the rune has been removed
+
+/obj/effect/rune/narsie/attackby(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal.
+ if((istype(I, /obj/item/melee/cultblade/dagger) && iscultist(user)))
+ log_game("Summon Narsie rune erased by [key_name(user)] with a cult dagger")
+ message_admins("[key_name_admin(user)] erased a Narsie rune with a cult dagger")
+ if(istype(I, /obj/item/nullrod)) //Begone foul magiks. You cannot hinder me.
+ log_game("Summon Narsie rune erased by [key_name(user)] using a null rod")
+ message_admins("[key_name_admin(user)] erased a Narsie rune with a null rod")
return ..()
diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm
deleted file mode 100644
index af0dfb5e6e344..0000000000000
--- a/code/game/gamemodes/cult/talisman.dm
+++ /dev/null
@@ -1,436 +0,0 @@
-/obj/item/paper/talisman
- icon = 'icons/obj/bureaucracy.dmi'
- icon_state = "paper_talisman"
- var/cultist_name = "talisman"
- var/cultist_desc = "A basic talisman. It serves no purpose."
- var/invocation = "Naise meam!"
- info = "​
"
- var/uses = 1
- var/health_cost = 0 //The amount of health taken from the user when invoking the talisman
-
-/obj/item/paper/talisman/update_icon()//overriding this so the update_icon doesn't turn them into normal looking paper
- SEND_SIGNAL(src, COMSIG_OBJ_UPDATE_ICON)
-
-/obj/item/paper/talisman/examine(mob/user)
- . = ..()
- if(iscultist(user) || user.stat == DEAD)
- . += "Name: [cultist_name]"
- . += "Effect: [cultist_desc]"
- . += "Uses Remaining: [uses]"
- else
- . += "You see strange symbols on the paper. Are they supposed to mean something?"
-
-/obj/item/paper/talisman/attack_self(mob/living/user)
- if(!iscultist(user))
- to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?")
- return
- if(invoke(user))
- uses--
- if(uses <= 0)
- user.drop_item()
- qdel(src)
-
-/obj/item/paper/talisman/proc/invoke(mob/living/user, successfuluse = 1)
- . = successfuluse
- if(successfuluse) //if the calling whatever says we succeed, do the fancy stuff
- if(invocation)
- user.whisper(invocation)
- if(health_cost && iscarbon(user))
- var/mob/living/carbon/C = user
- C.apply_damage(health_cost, BRUTE, pick("l_arm", "r_arm"))
-
-//Malformed Talisman: If something goes wrong.
-/obj/item/paper/talisman/malformed
- cultist_name = "malformed talisman"
- cultist_desc = "A talisman with gibberish scrawlings. No good can come from invoking this."
- invocation = "Ra'sha yoka!"
-
-/obj/item/paper/talisman/malformed/invoke(mob/living/user, successfuluse = 1)
- to_chat(user, "You feel a pain in your head. [SSticker.cultdat.entity_title3] is displeased.")
- if(iscarbon(user))
- var/mob/living/carbon/C = user
- C.apply_damage(10, BRUTE, "head")
-
-//Supply Talisman: Has a few unique effects. Granted only to starter cultists.
-/obj/item/paper/talisman/supply
- cultist_name = "Supply Talisman"
- icon_state = "supply"
- cultist_desc = "A multi-use talisman that can create various objects. Intended to increase the cult's strength early on."
- invocation = null
- uses = 3
-
-/obj/item/paper/talisman/supply/invoke(mob/living/user, successfuluse = 1)
- var/dat = list()
- 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! - Summons an arcane tome, used to scribe runes and communicate with other cultists.
"
- dat += "Bar'tea eas! - Provides 5 runed metal.
"
- dat += "Sas'so c'arta forbici! - Allows you to move to a selected teleportation rune.
"
- dat += "Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range.
"
- dat += "Fuu ma'jin! - Allows you to stun a person by attacking them with the talisman.
"
- dat += "Kla'atu barada nikt'o! - Two use talisman, first use makes all nearby runes invisible, second use reveals nearby hidden runes.
"
- dat += "Kal'om neth! - Summons a soul stone, used to capture the spirits of dead or dying humans.
"
- dat += "Daa'ig osk! - Summons a construct shell for use with soulstone-captured souls. It is too large to carry on your person.
"
- var/datum/browser/popup = new(user, "talisman", "", 400, 400)
- popup.set_content(jointext(dat, ""))
- popup.open()
- return 0
-
-/obj/item/paper/talisman/supply/Topic(href, href_list)
- if(src)
- if(usr.stat || usr.restrained() || !in_range(src, usr))
- return
- if(href_list["rune"])
- switch(href_list["rune"])
- if("newtome")
- var/obj/item/tome/T = new(usr)
- usr.put_in_hands(T)
- if("metal")
- if(istype(src, /obj/item/paper/talisman/supply/weak))
- usr.visible_message("Lesser supply talismans lack the strength to materialize runed metal!")
- return
- var/obj/item/stack/sheet/runed_metal/R = new(usr,5)
- usr.put_in_hands(R)
- if("teleport")
- var/obj/item/paper/talisman/teleport/T = new(usr)
- usr.put_in_hands(T)
- if("emp")
- var/obj/item/paper/talisman/emp/T = new(usr)
- usr.put_in_hands(T)
- if("runestun")
- var/obj/item/paper/talisman/stun/T = new(usr)
- usr.put_in_hands(T)
- if("soulstone")
- var/obj/item/soulstone/T = new(usr)
- usr.put_in_hands(T)
- if("construct")
- new /obj/structure/constructshell(get_turf(usr))
- if("veiling")
- var/obj/item/paper/talisman/true_sight/T = new(usr)
- usr.put_in_hands(T)
- uses--
- if(uses <= 0)
- if(iscarbon(usr))
- var/mob/living/carbon/C = usr
- C.drop_item()
- visible_message("[src] crumbles to dust.")
- qdel(src)
-
-/obj/item/paper/talisman/supply/weak
- uses = 2
-
-//Rite of Translocation: Same as rune
-/obj/item/paper/talisman/teleport
- cultist_name = "Talisman of Teleportation"
- icon_state = "teleport"
- cultist_desc = "A single-use talisman that will teleport a user to a random rune of the same keyword."
- invocation = "Sas'so c'arta forbici!"
- health_cost = 5
-
-/obj/item/paper/talisman/teleport/invoke(mob/living/user, successfuluse = 1)
- var/list/potential_runes = list()
- var/list/teleportnames = list()
- var/list/duplicaterunecount = list()
- for(var/R in GLOB.teleport_runes)
- var/obj/effect/rune/teleport/T = R
- var/resultkey = T.listkey
- if(resultkey in teleportnames)
- duplicaterunecount[resultkey]++
- resultkey = "[resultkey] ([duplicaterunecount[resultkey]])"
- else
- teleportnames.Add(resultkey)
- duplicaterunecount[resultkey] = 1
- potential_runes[resultkey] = T
-
- if(!potential_runes.len)
- to_chat(user, "There are no valid runes to teleport to!")
- log_game("Teleport talisman failed - no other teleport runes")
- return ..(user, 0)
-
- if(!is_level_reachable(user.z))
- to_chat(user, "You are not in the right dimension!")
- log_game("Teleport talisman failed - user in away mission")
- return ..(user, 0)
-
- var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in potential_runes //we know what key they picked
- var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to?
- if(!src || QDELETED(src) || !user || user.l_hand != src && user.r_hand != src || user.incapacitated() || !actual_selected_rune)
- return ..(user, 0)
-
- user.visible_message("Dust flows from [user]'s hand, and [user.p_they()] disappear[user.p_s()] in a flash of red light!", \
- "You speak the words of the talisman and find yourself somewhere else!")
- user.forceMove(get_turf(actual_selected_rune))
- return ..()
-
-
-/obj/item/paper/talisman/summon_tome
- cultist_name = "Talisman of Tome Summoning"
- icon_state = "tome"
- cultist_desc = "A one-use talisman that will call an untranslated tome from the archives of a cult."
- invocation = "N'ath reth sh'yro eth d'raggathnor!"
- health_cost = 1
-
-/obj/item/paper/talisman/summon_tome/invoke(mob/living/user, successfuluse = 1)
- . = ..()
- user.visible_message("[user]'s hand glows red for a moment.", \
- "You speak the words of the talisman!")
- new /obj/item/tome(get_turf(user))
- user.visible_message("A tome appears at [user]'s feet!", \
- "An arcane tome materializes at your feet.")
-
-/obj/item/paper/talisman/true_sight
- cultist_name = "Talisman of Veiling"
- icon_state = "veil"
- cultist_desc = "A multi-use talisman that hides nearby runes. On its second use, will reveal nearby runes."
- invocation = "Kla'atu barada nikt'o!"
- health_cost = 1
- uses = 2
- var/revealing = FALSE //if it reveals or not
-
-/obj/item/paper/talisman/true_sight/invoke(mob/living/user, successfuluse = 1)
- . = ..()
- if(!revealing)
- user.visible_message("Thin grey dust falls from [user]'s hand!", \
- "You speak the words of the talisman, hiding nearby runes.")
- invocation = "Nikt'o barada kla'atu!"
- revealing = TRUE
- for(var/obj/effect/rune/R in range(3,user))
- R.talismanhide()
- else
- user.visible_message("A flash of light shines from [user]'s hand!", \
- "You speak the words of the talisman, revealing nearby runes.")
- for(var/obj/effect/rune/R in range(3,user))
- R.talismanreveal()
-
-//Rite of False Truths: Same as rune
-/obj/item/paper/talisman/make_runes_fake
- cultist_name = "Talisman of Disguising"
- icon_state = "disguising"
- cultist_desc = "A talisman that will make nearby runes appear fake."
- invocation = "By'o nar'nar!"
-
-/obj/item/paper/talisman/make_runes_fake/invoke(mob/living/user, successfuluse = 1)
- . = ..()
- user.visible_message("Dust flows from [user]s hand.", \
- "You speak the words of the talisman, making nearby runes appear fake.")
- for(var/obj/effect/rune/R in orange(6,user))
- R.talismanfake()
- R.desc = "A rune drawn in crayon."
-
-
-//Rite of Disruption: Weaker than rune
-/obj/item/paper/talisman/emp
- cultist_name = "Talisman of Electromagnetic Pulse"
- icon_state = "emp"
- cultist_desc = "A talisman that will cause a moderately-sized electromagnetic pulse."
- invocation = "Ta'gh fara'qha fel d'amar det!"
- health_cost = 5
-
-/obj/item/paper/talisman/emp/invoke(mob/living/user, successfuluse = 1)
- . = ..()
- user.visible_message("[user]'s hand flashes a bright blue!", \
- "You speak the words of the talisman, emitting an EMP blast.")
- empulse(src, 4, 8, 1)
-
-
-//Rite of Disorientation: Stuns and inhibit speech on a single target for quite some time
-/obj/item/paper/talisman/stun
- cultist_name = "Talisman of Stunning"
- icon_state = "stunning"
- cultist_desc = "A talisman that will stun and inhibit speech on a single target. To use, attack target directly."
- invocation = "Dream sign:Evil sealing talisman!"
- health_cost = 10
-
-/obj/item/paper/talisman/stun/invoke(mob/living/user, successfuluse = 0)
- if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff
- return ..()
- if(iscultist(user))
- to_chat(user, "To use this talisman, attack the target directly.")
- else
- to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?")
- return 0
-
-/obj/item/paper/talisman/stun/attack(mob/living/target, mob/living/user, successfuluse = 1)
- if(iscultist(user))
- invoke(user, 1)
- user.visible_message("[user] holds up [src], which explodes in a flash of red light!", \
- "You stun [target] with the talisman!")
- var/obj/item/nullrod/N = locate() in target
- if(N)
- target.visible_message("[target]'s holy weapon absorbs the talisman's light!", \
- "Your holy weapon absorbs the blinding light!")
- else
- add_attack_logs(user, target, "Stunned with a talisman")
- target.Weaken(10)
- target.Stun(10)
- target.flash_eyes(1,1)
- if(issilicon(target))
- var/mob/living/silicon/S = target
- S.emp_act(1)
- if(iscarbon(target))
- var/mob/living/carbon/C = target
- C.AdjustSilence(5)
- C.AdjustStuttering(15)
- C.AdjustCultSlur(20)
- C.Jitter(15)
- user.drop_item()
- qdel(src)
- return
- ..()
-
-
-//Rite of Arming: Equips cultist armor on the user, where available
-/obj/item/paper/talisman/armor
- cultist_name = "Talisman of Arming"
- icon_state = "arming"
- cultist_desc = "A talisman that will equip the invoker with cultist equipment if there is a slot to equip it to."
- invocation = "N'ath reth sh'yro eth draggathnor!"
-
-/obj/item/paper/talisman/armor/invoke(mob/living/user, successfuluse = 1)
- . = ..()
- var/mob/living/carbon/human/H = user
- user.visible_message("Otherworldly equipment suddenly appears on [user]!", \
- "You speak the words of the talisman, arming yourself!")
-
- H.equip_to_slot_or_del(new /obj/item/clothing/suit/hooded/cultrobes/alt(user), slot_wear_suit)
- H.equip_to_slot_or_del(new /obj/item/storage/backpack/cultpack(user), slot_back)
- H.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult(user), slot_shoes)
- H.put_in_hands(new /obj/item/melee/cultblade(user))
- H.put_in_hands(new /obj/item/restraints/legcuffs/bola/cult(user))
-
-/obj/item/paper/talisman/armor/attack(mob/living/target, mob/living/user)
- if(iscultist(user) && iscultist(target))
- user.drop_item()
- qdel(src)
- invoke(target)
- return
- ..()
-
-
-//Talisman of Horrors: Breaks the mind of the victim with nightmarish hallucinations
-/obj/item/paper/talisman/horror
- cultist_name = "Talisman of Horrors"
- icon_state = "horror"
- cultist_desc = "A talisman that will break the mind of the victim with nightmarish hallucinations."
- invocation = "Lo'Nab Na'Dm!"
-
-/obj/item/paper/talisman/horror/attack(mob/living/target, mob/living/user)
- if(iscultist(user))
- to_chat(user, "You disturb [target] with visions of the end!")
- if(iscarbon(target))
- var/mob/living/carbon/H = target
- H.AdjustHallucinate(30)
- qdel(src)
-
-
-//Talisman of Fabrication: Creates a construct shell out of 25 metal sheets.
-/obj/item/paper/talisman/construction
- cultist_name = "Talisman of Construction"
- icon_state = "construction"
- cultist_desc = "Use this talisman on at least twenty-five metal sheets to create an empty construct shell or on plasteel to make runed metal"
- invocation = "Ethra p'ni dedol!"
- uses = 25
-
-/obj/item/paper/talisman/construction/attack_self(mob/living/user)
- if(iscultist(user))
- to_chat(user, "To use this talisman, place it upon a stack of metal sheets or plasteel sheets!")
- else
- to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?")
-
-
-/obj/item/paper/talisman/construction/attack(obj/M,mob/living/user)
- if(iscultist(user))
- to_chat(user, "This talisman will only work on a stack of metal sheets or plasteel sheets!")
- log_game("Construct talisman failed - not a valid target")
-
-/obj/item/paper/talisman/construction/afterattack(obj/item/stack/sheet/target, mob/user, proximity_flag, click_parameters)
- ..()
- if(proximity_flag && iscultist(user))
- if(istype(target, /obj/item/stack/sheet/metal))
- var/turf/T = get_turf(target)
- if(target.use(25))
- new /obj/structure/constructshell(T)
- to_chat(user, "The talisman clings to the metal and twists it into a construct shell!")
- user << sound('sound/magic/staff_chaos.ogg',0,1,25)
- qdel(src)
- return
- if(istype(target, /obj/item/stack/sheet/plasteel))
- var/turf/T = get_turf(target)
- var/quantity = min(target.amount, uses)
- uses -= quantity
- new /obj/item/stack/sheet/runed_metal(T,quantity)
- target.use(quantity)
- to_chat(user, "The talisman clings to the plasteel, transforming it into runed metal!")
- user << sound('sound/magic/staff_chaos.ogg',0,1,25)
- invoke(user, 1)
- if(uses <= 0)
- qdel(src)
- else
- to_chat(user, "The talisman must be used on metal or plasteel!")
-
-//Talisman of Shackling: Applies special cuffs directly from the talisman
-/obj/item/paper/talisman/shackle
- cultist_name = "Talisman of Shackling"
- icon_state = "shackling"
- cultist_desc = "Use this talisman on a victim to handcuff them with dark bindings."
- invocation = "In'totum Lig'abis!"
- uses = 4
-
-/obj/item/paper/talisman/shackle/invoke(mob/living/user, successfuluse = 0)
- if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff
- return ..()
- if(iscultist(user))
- to_chat(user, "To use this talisman, attack the target directly.")
- else
- to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?")
- return 0
-
-/obj/item/paper/talisman/shackle/attack(mob/living/carbon/target, mob/living/user)
- if(iscultist(user) && istype(target))
- if(target.stat == DEAD)
- user.visible_message("This talisman's magic does not affect the dead!")
- return
- CuffAttack(target, user)
- return
- ..()
-
-/obj/item/paper/talisman/shackle/proc/CuffAttack(mob/living/carbon/C, mob/living/user)
- if(!C.handcuffed)
- invoke(user, 1)
- playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
- C.visible_message("[user] begins restraining [C] with dark magic!", \
- "[user] begins shaping a dark magic around your wrists!")
- if(do_mob(user, C, 30))
- if(!C.handcuffed)
- C.handcuffed = new /obj/item/restraints/handcuffs/energy/cult/used(C)
- C.update_handcuffed()
- to_chat(user, "You shackle [C].")
- add_attack_logs(user, C, "Handcuffed (shackle talisman)")
- uses--
- else
- to_chat(user, "[C] is already bound.")
- else
- to_chat(user, "You fail to shackle [C].")
- else
- to_chat(user, "[C] is already bound.")
- if(uses <= 0)
- user.drop_item()
- qdel(src)
- return
-
-/obj/item/restraints/handcuffs/energy/cult //For the talisman of shackling
- name = "cult shackles"
- desc = "Shackles that bind the wrists with sinister magic."
- trashtype = /obj/item/restraints/handcuffs/energy/used
- origin_tech = "materials=2;magnets=5"
-
-/obj/item/restraints/handcuffs/energy/used
- desc = "energy discharge"
- flags = DROPDEL
-
-/obj/item/restraints/handcuffs/energy/cult/used/dropped(mob/user)
- user.visible_message("[user]'s shackles shatter in a discharge of dark magic!", \
- "Your [src] shatters in a discharge of dark magic!")
- qdel(src)
- . = ..()
diff --git a/code/game/gamemodes/miniantags/slaughter/slaughter.dm b/code/game/gamemodes/miniantags/slaughter/slaughter.dm
index e58be20152185..be0e649cc2b28 100644
--- a/code/game/gamemodes/miniantags/slaughter/slaughter.dm
+++ b/code/game/gamemodes/miniantags/slaughter/slaughter.dm
@@ -120,7 +120,7 @@
melee_damage_lower = 60
environment_smash = ENVIRONMENT_SMASH_RWALLS //Smashes through EVERYTHING - r-walls included
faction = list("cult")
- playstyle_string = "You are a Harbinger of the Slaughter. Brought forth by the servants of Nar-Sie, you have a single purpose: slaughter the heretics \
+ playstyle_string = "You are a Harbinger of the Slaughter. Brought forth by the servants of Nar'Sie, you have a single purpose: slaughter the heretics \
who do not worship your master. You may use the ability 'Blood Crawl' near a pool of blood to enter it and become incorporeal. Using the ability again near a blood pool will allow you \
to emerge from it. You are fast, powerful, and almost invincible. By dragging a dead or unconscious body into a blood pool with you, you will consume it after a time and fully regain \
your health. You may use the ability 'Sense Victims' in your Cultist tab to locate a random, living heretic."
diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm
index aab4318c77488..d01c68c6ff286 100644
--- a/code/game/gamemodes/wizard/soulstone.dm
+++ b/code/game/gamemodes/wizard/soulstone.dm
@@ -1,21 +1,32 @@
+// This whole file really needs reorganising at some point, or at the very least the construct stuff should be moved somewhere else.
/obj/item/soulstone
- name = "Soul Stone Shard"
+ name = "soul stone shard"
icon = 'icons/obj/wizard.dmi'
icon_state = "soulstone"
item_state = "electronic"
+ var/icon_state_full = "soulstone2"
desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artifact's power."
w_class = WEIGHT_CLASS_TINY
slot_flags = SLOT_BELT
origin_tech = "bluespace=4;materials=5"
- var/optional = FALSE //does this soulstone ask the victim whether they want to be turned into a shade
- var/usability = FALSE // Can this soul stone be used by anyone, or only cultists/wizards?
- var/reusable = TRUE // Can this soul stone be used more than once?
- var/spent = FALSE // If the soul stone can only be used once, has it been used?
+ /// Does this soulstone ask the victim whether they want to be turned into a shade
+ var/optional = FALSE
+ /// Can this soul stone be used by anyone, or only cultists/wizards?
+ var/usability = FALSE
+ /// Can this soul stone be used more than once?
+ var/reusable = TRUE
+ /// If the soul stone can only be used once, has it been used?
+ var/spent = FALSE
- var/opt_in = FALSE // for tracking during the 'optional' bit
+ /// For tracking during the 'optional' bit
+ var/opt_in = FALSE
+ var/purified = FALSE
/obj/item/soulstone/proc/can_use(mob/living/user)
+ if(iscultist(user) && purified && !iswizard(user))
+ return FALSE
+
if(iscultist(user) || iswizard(user) || usability)
return TRUE
@@ -32,21 +43,36 @@
/obj/item/soulstone/anybody
usability = TRUE
-/obj/item/soulstone/anybody/chaplain
+/obj/item/soulstone/anybody/purified
+ icon_state = "purified_soulstone"
+ icon_state_full = "purified_soulstone2"
+ purified = TRUE
+ optional = TRUE
+
+/obj/item/soulstone/anybody/purified/chaplain
name = "mysterious old shard"
reusable = FALSE
- optional = TRUE
/obj/item/soulstone/pickup(mob/living/user)
. = ..()
+ if(iscultist(user) && purified && !iswizard(user))
+ to_chat(user, "[src] reeks of holy magic. You will need to cleanse it with a ritual dagger before anything can be done with it.")
if(!can_use(user))
- to_chat(user, "An overwhelming feeling of dread comes over you as you pick up the soulstone. It would be wise to be rid of this quickly.")
- user.Dizzy(120)
+ to_chat(user, "An overwhelming feeling of dread comes over you as you pick up [src].")
+
+/obj/item/soulstone/Destroy() //Stops the shade from being qdel'd immediately and their ghost being sent back to the arrival shuttle.
+ for(var/mob/living/simple_animal/shade/A in src)
+ A.death()
+ return ..()
//////////////////////////////Capturing////////////////////////////////////////////////////////
-/obj/item/soulstone/attack(mob/living/carbon/human/M as mob, mob/user as mob)
+/obj/item/soulstone/attack(mob/living/carbon/human/M, mob/user)
+ if(M == user)
+ return
+
if(!can_use(user))
- user.Paralyse(5)
+ user.Weaken(5)
+ user.emote("scream")
to_chat(user, "Your body is wracked with debilitating pain!")
return
@@ -61,7 +87,7 @@
to_chat(user, "This being is corrupted by an alien intelligence and cannot be soul trapped.")
return ..()
- if(jobban_isbanned(M, "cultist") || jobban_isbanned(M, "Syndicate"))
+ if(jobban_isbanned(M, ROLE_CULTIST) || jobban_isbanned(M, ROLE_SYNDICATE))
to_chat(user, "A mysterious force prevents you from trapping this being's soul.")
return ..()
@@ -77,8 +103,9 @@
to_chat(user, "You attempt to channel [M]'s soul into [src]. You must give the soul some time to react and stand still...")
var/mob/player_mob = M
- if(M.get_ghost())//in case our player ghosted and we need to throw the alert at their ghost instead
- player_mob = M.get_ghost()
+ var/ghost = M.get_ghost()
+ if(ghost) // In case our player ghosted and we need to throw the alert at their ghost instead
+ player_mob = ghost
var/client/player_client = player_mob.client
to_chat(player_mob, "[user] is trying to capture your soul into [src]! Click the button in the top right of the game window to respond.")
player_client << 'sound/misc/notice2.ogg'
@@ -88,11 +115,11 @@
if(player_client.prefs && player_client.prefs.UI_style)
A.icon = ui_style2icon(player_client.prefs.UI_style)
- //pass the stuff to the alert itself
+ // Pass the stuff to the alert itself
A.stone = src
A.stoner = user.real_name
- //layer shenanigans to make the alert display the soulstone
+ // Layer shenanigans to make the alert display the soulstone
var/old_layer = layer
var/old_plane = plane
layer = FLOAT_LAYER
@@ -101,7 +128,7 @@
layer = old_layer
plane = old_plane
- //give the victim 10 seconds to respond
+ // Give the victim 10 seconds to respond
sleep(10 SECONDS)
if(!opt_in)
@@ -117,57 +144,81 @@
transfer_soul("VICTIM", M, user)
return
-///////////////////Options for using captured souls///////////////////////////////////////
+/obj/item/soulstone/attackby(obj/item/O, mob/user)
+ if(istype(O, /obj/item/storage/bible) && !iscultist(user))
+ if(purified)
+ return
+ to_chat(user, "You begin to exorcise [src].")
+ playsound(src, 'sound/hallucinations/veryfar_noise.ogg', 40, TRUE)
+ if(do_after(user, 40, target = src))
+ usability = TRUE
+ purified = TRUE
+ optional = TRUE
+ icon_state = "purified_soulstone"
+ icon_state_full = "purified_soulstone2"
+ for(var/mob/M in contents)
+ if(M.mind)
+ icon_state = "purified_soulstone2"
+ if(iscultist(M))
+ SSticker.mode.remove_cultist(M.mind, FALSE)
+ to_chat(M, "You feel the cult's influence vanish. Assist [user], your saviour, and get vengeance on those who enslaved you!")
+ else
+ to_chat(M, "Your soulstone has been exorcised, and you are now bound to obey [user]. ")
+
+ for(var/mob/living/simple_animal/shade/EX in src)
+ EX.holy = TRUE
+ EX.icon_state = "shade_angelic"
+ user.visible_message("[user] purifies [src]!", "You purify [src]!")
+
+ else if(istype(O, /obj/item/melee/cultblade/dagger) && iscultist(user))
+ if(!purified)
+ return
+ to_chat(user, "You begin to cleanse [src] of holy magic.")
+ if(do_after(user, 40, target = src))
+ usability = FALSE
+ purified = FALSE
+ optional = FALSE
+ icon_state = "soulstone"
+ icon_state_full = "soulstone2"
+ for(var/mob/M in contents)
+ if(M.mind)
+ icon_state = "soulstone2"
+ SSticker.mode.add_cultist(M.mind)
+ to_chat(M, "Your shard has been cleansed of holy magic, and you are now bound to the cult's will. Obey them and assist in their goals.")
+ for(var/mob/living/simple_animal/shade/EX in src)
+ EX.holy = FALSE
+ EX.icon_state = SSticker.cultdat?.shade_icon_state
+ to_chat(user, "You have cleansed [src] of holy magic.")
+ else
+ ..()
+
/obj/item/soulstone/attack_self(mob/user)
if(!in_range(src, user))
return
if(!can_use(user))
- user.Paralyse(5)
+ user.Weaken(5)
+ user.emote("scream")
to_chat(user, "Your body is wracked with debilitating pain!")
return
- user.set_machine(src)
- var/dat = "Soul Stone
"
- for(var/mob/living/simple_animal/shade/A in src)
- dat += "Captured Soul: [A.name]
"
- dat += {"Summon Shade"}
- dat += "
"
- dat += {" Close"}
- user << browse(dat, "window=aicard")
- onclose(user, "aicard")
+ release_shades(user)
return
-/obj/item/soulstone/Topic(href, href_list)
- var/mob/U = usr
- if(!in_range(src, U) || U.machine != src || !can_use(usr))
- U << browse(null, "window=aicard")
- U.unset_machine()
- return
-
- add_fingerprint(U)
- U.set_machine(src)
-
- switch(href_list["choice"])//Now we switch based on choice.
- if("Close")
- U << browse(null, "window=aicard")
- U.unset_machine()
- return
-
- if("Summon")
- for(var/mob/living/simple_animal/shade/A in src)
- A.status_flags &= ~GODMODE
- A.canmove = 1
- A.forceMove(get_turf(usr))
- A.cancel_camera()
- icon_state = "soulstone"
- name = initial(name)
- if(iswizard(usr) || usability)
- to_chat(A, "You have been released from your prison, but you are still bound to [usr.real_name]'s will. Help [usr.p_them()] succeed in [usr.p_their()] goals at all costs.")
- else if(iscultist(usr))
- to_chat(A, "You have been released from your prison, but you are still bound to the cult's will. Help [usr.p_them()] succeed in [usr.p_their()] goals at all costs.")
- was_used()
- attack_self(U)
+/obj/item/soulstone/proc/release_shades(mob/user)
+ for(var/mob/living/simple_animal/shade/A in src)
+ A.forceMove(get_turf(user))
+ A.cancel_camera()
+ if(purified)
+ icon_state = "purified_soulstone"
+ else
+ icon_state = "soulstone"
+ name = initial(name)
+ if(iscultist(A))
+ to_chat(A, "You have been released from your prison, but you are still bound to the cult's will. Help them succeed in their goals at all costs.")
+ else
+ to_chat(A, "You have been released from your prison, but you are still bound to your [purified ? "saviour" : "creator"]'s will.")
+ was_used()
///////////////////////////Transferring to constructs/////////////////////////////////////////////////////
/obj/structure/constructshell
@@ -175,204 +226,207 @@
icon = 'icons/obj/wizard.dmi'
icon_state = "construct-cult"
desc = "A wicked machine used by those skilled in magical arts. It is inactive"
+ /// Is someone currently placing a soulstone into the shell
+ var/active = FALSE
/obj/structure/constructshell/examine(mob/user)
. = ..()
if(in_range(user, src) && (iscultist(user) || iswizard(user) || user.stat == DEAD))
. += "A construct shell, used to house bound souls from a soulstone."
. += "Placing a soulstone with a soul into this shell allows you to produce your choice of the following:"
- . += "An Artificer, which can produce more shells and soulstones, as well as fortifications."
- . += "A Wraith, which does high damage and can jaunt through walls, though it is quite fragile."
- . += "A Juggernaut, which is very hard to kill and can produce temporary walls, but is slow."
+ . += "An Artificer, which can produce more shells and soulstones, as well as fortifications."
+ . += "A Wraith, which does high damage and can jaunt through walls, though it is quite fragile."
+ . += "A Juggernaut, which is very hard to kill and can produce temporary walls, but is slow."
-/obj/structure/constructshell/attackby(obj/item/O as obj, mob/user as mob, params)
- if(istype(O, /obj/item/soulstone))
- var/obj/item/soulstone/SS = O
+/obj/structure/constructshell/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/soulstone))
+ var/obj/item/soulstone/SS = I
if(!SS.can_use(user))
- to_chat(user, "An overwhelming feeling of dread comes over you as you attempt to place the soulstone into the shell. It would be wise to be rid of this quickly.")
- user.Dizzy(120)
+ to_chat(user, "An overwhelming feeling of dread comes over you as you attempt to place the soulstone into the shell.")
+ user.Confused(10)
return
- SS.transfer_soul("CONSTRUCT",src,user)
+ SS.transfer_soul("CONSTRUCT", src, user)
SS.was_used()
else
return ..()
////////////////////////////Proc for moving soul in and out off stone//////////////////////////////////////
-/obj/item/proc/transfer_soul(var/choice as text, var/target, var/mob/U as mob)
+/obj/item/soulstone/proc/transfer_soul(choice, target, mob/user)
switch(choice)
if("FORCE")
- var/obj/item/soulstone/SS = src
var/mob/living/T = target
if(T.client != null)
- SS.init_shade(T, U)
+ init_shade(T, user)
else
- to_chat(U, "Capture failed!: The soul has already fled its mortal frame. You attempt to bring it back...")
+ to_chat(user, "Capture failed! The soul has already fled its mortal frame. You attempt to bring it back...")
T.Paralyse(20)
- if(!SS.getCultGhost(T,U))
+ if(!get_cult_ghost(T, user))
T.dust() //If we can't get a ghost, kill the sacrifice anyway.
if("VICTIM")
var/mob/living/carbon/human/T = target
- var/obj/item/soulstone/SS = src
if(T.stat == 0)
- to_chat(U, "Capture failed!: Kill or maim the victim first!")
+ to_chat(user, "Capture failed! Kill or maim the victim first!")
else
- if(!T.client_mobs_in_contents?.len)
- to_chat(U, "They have no soul!")
+ if(!length(T.client_mobs_in_contents))
+ to_chat(user, "They have no soul!")
else
if(T.client == null)
- to_chat(U, "Capture failed!: The soul has already fled its mortal frame. You attempt to bring it back...")
- SS.getCultGhost(T,U)
+ to_chat(user, "Capture failed! The soul has already fled its mortal frame. You attempt to bring it back...")
+ get_cult_ghost(T, user)
else
- if(SS.contents.len)
- to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.")
+ if(length(contents))
+ to_chat(user, "Capture failed! The soul stone is full! Use or free an existing soul to make room.")
else
- SS.init_shade(T, U, vic = 1)
+ init_shade(T, user, TRUE)
if("SHADE")
var/mob/living/simple_animal/shade/T = target
- var/obj/item/soulstone/SS = src
- if(!SS.can_use(U))
- U.Paralyse(5)
- to_chat(U, "Your body is wracked with debilitating pain!")
+ if(!can_use(user))
+ user.Weaken(5)
+ to_chat(user, "Your body is wracked with debilitating pain!")
return
if(T.stat == DEAD)
- to_chat(U, "Capture failed!: The shade has already been banished!")
+ to_chat(user, "Capture failed! The shade has already been banished!")
+ if((iscultist(T) && purified) || (T.holy && !purified))
+ to_chat(user, "Capture failed! The shade recoils away from [src]!")
else
- if(SS.contents.len)
- to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.")
+ if(length(contents))
+ to_chat(user, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.")
else
- T.loc = SS //put shade in stone
- T.status_flags |= GODMODE
+ T.loc = src //put shade in stone
T.canmove = 0
T.health = T.maxHealth
- T.faction |= "\ref[U]"
- SS.icon_state = "soulstone2"
- to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form")
- to_chat(U, "Capture successful!: [T.name]'s has been recaptured and stored within the soul stone.")
+ icon_state = icon_state_full
+ name = "soulstone : [T.name]"
+ to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form")
+ to_chat(user, "Capture successful! [T.name]'s has been recaptured and stored within the soul stone.")
+
if("CONSTRUCT")
- var/obj/structure/constructshell/T = target
- var/obj/item/soulstone/SS = src
- var/mob/living/simple_animal/shade/SH = locate() in SS
- if(SH)
- var/construct_class = alert(U, "Please choose which type of construct you wish to create.",,"Juggernaut","Wraith","Artificer")
+ var/obj/structure/constructshell/shell = target
+ var/mob/living/simple_animal/shade/shade = locate() in src
+ if(shade)
+ var/construct_class = alert(user, "Please choose which type of construct you wish to create.", null, "Juggernaut", "Wraith", "Artificer")
+ if(shell.active)
+ return // Don't want two people doing it at once
+ shell.active = TRUE
switch(construct_class)
if("Juggernaut")
- var/mob/living/simple_animal/hostile/construct/armoured/C = new /mob/living/simple_animal/hostile/construct/armoured (get_turf(T.loc))
+ var/mob/living/simple_animal/hostile/construct/armoured/C = new(shell.loc)
+ C.init_construct(shade, src, shell)
to_chat(C, "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, create shield walls and even deflect energy weapons, and rip apart enemies and walls alike.")
- init_construct(C,SH,SS,T)
if("Wraith")
- var/mob/living/simple_animal/hostile/construct/wraith/C = new /mob/living/simple_animal/hostile/construct/wraith (get_turf(T.loc))
+ var/mob/living/simple_animal/hostile/construct/wraith/C = new(shell.loc)
+ C.init_construct(shade, src, shell)
to_chat(C, "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.")
- init_construct(C,SH,SS,T)
if("Artificer")
- var/mob/living/simple_animal/hostile/construct/builder/C = new /mob/living/simple_animal/hostile/construct/builder (get_turf(T.loc))
+ var/mob/living/simple_animal/hostile/construct/builder/C = new(shell.loc)
+ C.init_construct(shade, src, shell)
to_chat(C, "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, use magic missile, repair allied constructs (by clicking on them), and most important of all create new constructs (Use your Artificer spell to summon a new construct shell and Summon Soulstone to create a new soulstone).")
- init_construct(C,SH,SS,T)
else
- to_chat(U, "Creation failed!: The soul stone is empty! Go kill someone!")
+ to_chat(user, "Creation failed!: The soul stone is empty! Go kill someone!")
return
-/proc/init_construct(mob/living/simple_animal/hostile/construct/C, mob/living/simple_animal/shade/SH, obj/item/soulstone/SS, obj/structure/constructshell/T)
- SH.mind.transfer_to(C)
- if(iscultist(C))
- var/datum/action/innate/cultcomm/CC = new()
- CC.Grant(C) //We have to grant the cult comms again because they're lost during the mind transfer.
- qdel(T)
- qdel(SH)
- to_chat(C, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
- C.cancel_camera()
+/mob/living/simple_animal/hostile/construct/proc/init_construct(mob/living/simple_animal/shade/shade, obj/item/soulstone/SS, obj/structure/constructshell/shell)
+ if(shade.mind)
+ shade.mind.transfer_to(src)
+ if(SS.purified)
+ set_light(3, 5, LIGHT_COLOR_DARK_BLUE)
+ name = "Holy [name]"
+ real_name = "Holy [real_name]"
+ if(iscultist(src) && !SS.purified) // Re-grant cult actions, lost in the transfer
+ var/datum/action/innate/cult/comm/CC = new
+ var/datum/action/innate/cult/check_progress/D = new
+ CC.Grant(src)
+ D.Grant(src)
+ SSticker.mode.cult_objs.study(src) // Display objectives again
+ to_chat(src, "You are still bound to serve the cult, follow their orders and help them complete their goals at all costs.")
+ else
+ to_chat(src, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
+ cancel_camera()
+ qdel(shell)
+ qdel(shade)
qdel(SS)
-/proc/makeNewConstruct(var/mob/living/simple_animal/hostile/construct/ctype, var/mob/target, var/mob/stoner = null, cultoverride = 0)
- if(jobban_isbanned(target, "cultist") || jobban_isbanned(target, "Syndicate"))
+/proc/make_new_construct(mob/living/simple_animal/hostile/construct/c_type, mob/target, mob/user, cult_override = FALSE)
+ if(jobban_isbanned(target, "cultist"))
return
- var/mob/living/simple_animal/hostile/construct/newstruct = new ctype(get_turf(target))
- newstruct.faction |= "\ref[stoner]"
- newstruct.key = target.key
- if(stoner && iscultist(stoner) || cultoverride)
- if(SSticker.mode.name == "cult")
- SSticker.mode:add_cultist(newstruct.mind)
- else
- SSticker.mode.cult+=newstruct.mind
- SSticker.mode.update_cult_icons_added(newstruct.mind)
- if(stoner && iswizard(stoner))
- to_chat(newstruct, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
- else if(stoner && iscultist(stoner))
- to_chat(newstruct, "You are still bound to serve the cult, follow their orders and help them complete their goals at all costs.")
+ var/mob/living/simple_animal/hostile/construct/C = new c_type(get_turf(target))
+ C.faction |= "\ref[user]"
+ C.key = target.key
+ if(user && iscultist(user) || cult_override)
+ SSticker.mode.add_cultist(C.mind)
+ SSticker.mode.update_cult_icons_added(C.mind)
+ if(user && iscultist(user))
+ to_chat(C, "You are still bound to serve the cult, follow their orders and help them complete their goals at all costs.")
else
- to_chat(newstruct, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
- newstruct.cancel_camera()
-
-/obj/item/soulstone/proc/init_shade(mob/living/T, mob/U, vic = 0)
- new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton
- T.invisibility = 101
- var/atom/movable/overlay/animation = new /atom/movable/overlay( T.loc )
- animation.icon_state = "blank"
- animation.icon = 'icons/mob/mob.dmi'
- animation.master = T
- flick("dust-h", animation)
- qdel(animation)
- var/path = get_shade_type()
- var/mob/living/simple_animal/shade/S = new path(src)
- S.status_flags |= GODMODE //So they won't die inside the stone somehow
- S.canmove = 0//Can't move out of the soul stone
- S.name = "Shade of [T.real_name]"
- S.real_name = "Shade of [T.real_name]"
- S.key = T.key
+ to_chat(C, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
+ C.cancel_camera()
+
+/obj/item/soulstone/proc/init_shade(mob/living/M, mob/user, forced = FALSE)
+ var/type = get_shade_type()
+ var/mob/living/simple_animal/shade/S = new type(src)
+ S.canmove = FALSE // Can't move out of the soul stone
+ S.name = "Shade of [M.real_name]"
+ S.real_name = "Shade of [M.real_name]"
+ S.key = M.key
S.cancel_camera()
- name = "soulstone: Shade of [T.real_name]"
- icon_state = "soulstone2"
- if(U)
- S.faction |= "\ref[U]" //Add the master as a faction, allowing inter-mob cooperation
- if(iswizard(U))
+ name = "soulstone: [S.name]"
+ icon_state = icon_state_full
+
+ if(user)
+ S.faction |= "\ref[user]" //Add the master as a faction, allowing inter-mob cooperation
+ if(iswizard(user))
SSticker.mode.update_wiz_icons_added(S.mind)
S.mind.special_role = SPECIAL_ROLE_WIZARD_APPRENTICE
- if(iscultist(U))
- SSticker.mode.add_cultist(S.mind, 0)
+ if(iscultist(user))
+ SSticker.mode.add_cultist(S.mind)
S.mind.special_role = SPECIAL_ROLE_CULTIST
S.mind.store_memory("Serve the cult's will.")
- to_chat(S, "Your soul has been captured! You are now bound to the cult's will. Help them succeed in their goals at all costs.")
+ to_chat(S, "Your soul has been captured! You are now bound to the cult's will. Help them succeed in their goals at all costs.")
else
- S.mind.store_memory("Serve [U.real_name], your creator.")
- to_chat(S, "Your soul has been captured! You are now bound to [U.real_name]'s will. Help them succeed in their goals at all costs.")
- if(vic && U)
- to_chat(U, "Capture successful!: [T.real_name]'s soul has been ripped from [U.p_their()] body and stored within the soul stone.")
- if(isrobot(T))//Robots have to dust or else they spill out an empty robot brain, and unequiping them spills robot components that shouldn't spawn.
- T.dust()
+ S.mind.store_memory("Serve [user.real_name], your creator.")
+ to_chat(S, "Your soul has been captured! You are now bound to [user.real_name]'s will. Help them succeed in their goals at all costs.")
+ if(forced && user)
+ to_chat(user, "Capture successful!: [M.real_name]'s soul has been ripped from [user.p_their()] body and stored within the soul stone.")
+ if(isrobot(M))//Robots have to dust or else they spill out an empty robot brain, and unequiping them spills robot components that shouldn't spawn.
+ M.dust()
else
- for(var/obj/item/W in T)
- T.unEquip(W)
- qdel(T)
+ for(var/obj/item/I in M)
+ M.unEquip(I)
+ M.dust()
/obj/item/soulstone/proc/get_shade_type()
+ if(purified)
+ return /mob/living/simple_animal/shade/holy
return /mob/living/simple_animal/shade/cult
-/obj/item/soulstone/anybody/get_shade_type()
- return /mob/living/simple_animal/shade
-
-/obj/item/soulstone/proc/getCultGhost(mob/living/T, mob/U)
+/obj/item/soulstone/proc/get_cult_ghost(mob/living/M, mob/user)
var/mob/dead/observer/chosen_ghost
- for(var/mob/dead/observer/ghost in GLOB.player_list) //We put them back in their body
- if(ghost.mind && ghost.mind.current == T && ghost.client)
+ for(var/mob/dead/observer/ghost in GLOB.player_list) // We put them back in their body
+ if(ghost.mind && ghost.mind.current == M && ghost.client)
chosen_ghost = ghost
break
- if(!chosen_ghost) //Failing that, we grab a ghost
- var/list/consenting_candidates = SSghost_spawns.poll_candidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 10 SECONDS, source = /mob/living/simple_animal/shade)
- if(consenting_candidates.len)
+ if(!chosen_ghost) // Failing that, we grab a ghost
+ var/list/consenting_candidates
+ if(purified)
+ consenting_candidates = SSghost_spawns.poll_candidates("Would you like to play as a Holy Shade?", ROLE_SENTIENT, FALSE, poll_time = 10 SECONDS, source = /mob/living/simple_animal/shade/holy)
+ else
+ consenting_candidates = SSghost_spawns.poll_candidates("Would you like to play as a Shade?", ROLE_SENTIENT, FALSE, poll_time = 10 SECONDS, source = /mob/living/simple_animal/shade)
+ if(length(consenting_candidates))
chosen_ghost = pick(consenting_candidates)
- if(!T)
- return 0
+ if(!M)
+ return FALSE
if(!chosen_ghost)
- to_chat(U, "There were no spirits willing to become a shade.")
- return 0
- if(contents.len) //If they used the soulstone on someone else in the meantime
- return 0
- T.ckey = chosen_ghost.ckey
- init_shade(T, U)
- return 1
+ to_chat(user, "There were no spirits willing to become a shade.")
+ return FALSE
+ if(length(contents)) //If they used the soulstone on someone else in the meantime
+ return FALSE
+ M.ckey = chosen_ghost.ckey
+ init_shade(M, user)
+ return TRUE
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index c622b12c94cd7..a7b7bb287c97a 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -451,9 +451,12 @@ GLOBAL_LIST_INIT(cloner_biomass_items, list(\
if(H.mind in SSticker.mode.syndicates)
SSticker.mode.update_synd_icons_added()
if(H.mind in SSticker.mode.cult)
- SSticker.mode.add_cultist(occupant.mind)
- SSticker.mode.update_cult_icons_added() //So the icon actually appears
- SSticker.mode.update_cult_comms_added(H.mind) //So the comms actually appears
+ SSticker.mode.update_cult_icons_added(H.mind) // Adds the cult antag hud
+ SSticker.mode.add_cult_actions(H.mind) // And all the actions
+ if(SSticker.mode.cult_risen)
+ SSticker.mode.rise(H)
+ if(SSticker.mode.cult_ascendant)
+ SSticker.mode.ascend(H)
if(H.mind.vampire)
H.mind.vampire.update_owner(H)
if((H.mind in SSticker.mode.vampire_thralls) || (H.mind in SSticker.mode.vampire_enthralled))
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index 46cc06b50a3a7..40e462d2212e6 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -372,19 +372,10 @@
else
I.forceMove(loc)
- // Skip past any cult sacrifice objective using this person
- if(GAMEMODE_IS_CULT && is_sacrifice_target(occupant.mind))
- var/datum/game_mode/cult/cult_mode = SSticker.mode
- var/list/p_s_t = cult_mode.get_possible_sac_targets()
- if(p_s_t.len)
- cult_mode.sacrifice_target = pick(p_s_t)
- for(var/datum/mind/H in SSticker.mode.cult)
- if(H.current)
- to_chat(H.current, "[SSticker.cultdat.entity_name] murmurs, [occupant] is beyond your reach. Sacrifice [cult_mode.sacrifice_target.current] instead...")
- H.current << 'sound/ambience/alarm4.ogg'
- cult_mode.update_sac_objective(occupant.mind, occupant.mind.assigned_role)
- else
- cult_mode.bypass_phase()
+ // Find a new sacrifice target if needed, if unable allow summoning
+ if(is_sacrifice_target(occupant.mind))
+ if(!SSticker.mode.cult_objs.find_new_sacrifice_target())
+ SSticker.mode.cult_objs.ready_to_summon()
//Update any existing objectives involving this mob.
for(var/datum/objective/O in GLOB.all_objectives)
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 5189ff1095a22..92a7dc7159ca5 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -939,7 +939,7 @@ About the new airlock wires panel:
else if(istype(C, /obj/item/pai_cable)) // -- TLE
var/obj/item/pai_cable/cable = C
cable.plugin(src, user)
- else if((istype(C, /obj/item/paper) && !istype(C, /obj/item/paper/talisman)) || istype(C, /obj/item/photo))
+ else if(istype(C, /obj/item/paper) || istype(C, /obj/item/photo))
if(note)
to_chat(user, "There's already something pinned to this airlock! Use wirecutters or your hands to remove it.")
return
@@ -1412,7 +1412,7 @@ About the new airlock wires panel:
return "photo"
//Removes the current note on the door if any. Returns if a note is removed
-/obj/machinery/door/airlock/proc/remove_airlock_note(mob/user, wirecutters_used=TRUE)
+/obj/machinery/door/airlock/proc/remove_airlock_note(mob/user, wirecutters_used = TRUE)
if(note)
if(!wirecutters_used)
if (ishuman(user) && user.a_intent == INTENT_GRAB)//grab that note
@@ -1431,21 +1431,29 @@ About the new airlock wires panel:
return TRUE
return FALSE
-/obj/machinery/door/airlock/narsie_act()
+/obj/machinery/door/airlock/narsie_act(weak = FALSE)
var/turf/T = get_turf(src)
var/runed = prob(20)
var/obj/machinery/door/airlock/cult/A
- if(glass)
- if(runed)
- A = new/obj/machinery/door/airlock/cult/glass(T)
- else
- A = new/obj/machinery/door/airlock/cult/unruned/glass(T)
+ if(weak)
+ A = new/obj/machinery/door/airlock/cult/weak(T)
else
- if(runed)
- A = new/obj/machinery/door/airlock/cult(T)
+ if(glass)
+ if(runed)
+ A = new/obj/machinery/door/airlock/cult/glass(T)
+ else
+ A = new/obj/machinery/door/airlock/cult/unruned/glass(T)
else
- A = new/obj/machinery/door/airlock/cult/unruned(T)
+ if(runed)
+ A = new/obj/machinery/door/airlock/cult(T)
+ else
+ A = new/obj/machinery/door/airlock/cult/unruned(T)
A.name = name
+ A.stealth_icon = icon
+ A.stealth_overlays = overlays_file
+ A.stealth_opacity = opacity
+ A.stealth_glass = glass
+ A.stealth_airlock_material = airlock_material
qdel(src)
/obj/machinery/door/airlock/proc/ai_control_callback()
diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm
index 1d19450735715..9f99b45afa423 100644
--- a/code/game/machinery/doors/airlock_types.dm
+++ b/code/game/machinery/doors/airlock_types.dm
@@ -506,8 +506,22 @@
hackProof = TRUE
aiControlDisabled = AICONTROLDISABLED_ON
paintable = FALSE
+ /// Spawns an effect when opening
var/openingoverlaytype = /obj/effect/temp_visual/cult/door
+ /// Will the door let anyone through
var/friendly = FALSE
+ /// Is this door currently concealed
+ var/stealthy = FALSE
+ /// Door sprite when concealed
+ var/stealth_icon = 'icons/obj/doors/airlocks/station/maintenance.dmi'
+ /// Door overlays when concealed (Bolt lights, maintenance panel, etc.)
+ var/stealth_overlays = 'icons/obj/doors/airlocks/station/overlays.dmi'
+ /// Is the concealed airlock glass
+ var/stealth_glass = FALSE
+ /// Opacity when concealed (For glass doors)
+ var/stealth_opacity = TRUE
+ /// Inner airlock material (Glass, plasteel)
+ var/stealth_airlock_material = null
/obj/machinery/door/airlock/cult/Initialize()
. = ..()
@@ -521,19 +535,42 @@
/obj/machinery/door/airlock/cult/allowed(mob/living/L)
if(!density)
- return 1
+ return TRUE
if(friendly || iscultist(L) || isshade(L)|| isconstruct(L))
- new openingoverlaytype(loc)
- return 1
+ if(!stealthy)
+ new openingoverlaytype(loc)
+ return TRUE
else
- new /obj/effect/temp_visual/cult/sac(loc)
- var/atom/throwtarget
- throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(L, src)))
- L << pick(sound('sound/hallucinations/turn_around1.ogg',0,1,50), sound('sound/hallucinations/turn_around2.ogg',0,1,50))
- L.Weaken(2)
- spawn(0)
+ if(!stealthy)
+ new /obj/effect/temp_visual/cult/sac(loc)
+ var/atom/throwtarget
+ throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(L, src)))
+ SEND_SOUND(L, pick(sound('sound/hallucinations/turn_around1.ogg', 0, 1, 50), sound('sound/hallucinations/turn_around2.ogg', 0, 1, 50)))
+ L.Weaken(2)
L.throw_at(throwtarget, 5, 1,src)
- return 0
+ return FALSE
+
+/obj/machinery/door/airlock/cult/cult_conceal()
+ icon = stealth_icon
+ overlays_file = stealth_overlays
+ opacity = stealth_opacity
+ glass = stealth_glass
+ airlock_material = stealth_airlock_material
+ name = "airlock"
+ desc = "It opens and closes."
+ stealthy = TRUE
+ update_icon()
+
+/obj/machinery/door/airlock/cult/cult_reveal()
+ icon = SSticker.cultdat?.airlock_runed_icon_file
+ overlays_file = SSticker.cultdat?.airlock_runed_overlays_file
+ opacity = initial(opacity)
+ glass = initial(glass)
+ airlock_material = initial(airlock_material)
+ name = initial(name)
+ desc = initial(desc)
+ stealthy = initial(stealthy)
+ update_icon()
/obj/machinery/door/airlock/cult/narsie_act()
return
@@ -578,6 +615,13 @@
/obj/machinery/door/airlock/cult/unruned/glass/friendly
friendly = TRUE
+/obj/machinery/door/airlock/cult/weak
+ name = "brittle cult airlock"
+ desc = "An airlock hastily corrupted by blood magic, it is unusually brittle in this state."
+ normal_integrity = 150
+ damage_deflection = 5
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+
//////////////////////////////////
/*
Misc Airlocks
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index 5a8f401ac29b3..b9eb3f5dd7016 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -196,8 +196,9 @@ Class Procs:
/obj/machinery/emp_act(severity)
if(use_power && !stat)
use_power(7500/severity)
- new /obj/effect/temp_visual/emp(loc)
+ . = TRUE
..()
+
/obj/machinery/default_welder_repair(mob/user, obj/item/I)
. = ..()
if(.)
diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm
index 75ef71724553a..2f119e6efcfcd 100644
--- a/code/game/machinery/shieldgen.dm
+++ b/code/game/machinery/shieldgen.dm
@@ -7,8 +7,7 @@
opacity = FALSE
anchored = 1
resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
- var/const/max_health = 200
- var/health = max_health //The shield can only take so much beating (prevents perma-prisons)
+ max_integrity = 200
/obj/machinery/shield/New()
dir = pick(NORTH, SOUTH, EAST, WEST)
@@ -37,42 +36,6 @@
/obj/machinery/shield/CanAtmosPass(turf/T)
return !density
-/obj/machinery/shield/attackby(obj/item/I, mob/user, params)
- if(!istype(I))
- return
-
- //Calculate damage
- var/aforce = I.force
- if(I.damtype == BRUTE || I.damtype == BURN)
- health -= aforce
-
- //Play a fitting sound
- playsound(loc, 'sound/effects/empulse.ogg', 75, 1)
-
- if(health <= 0)
- visible_message("The [src] dissipates")
- qdel(src)
- return
-
- opacity = TRUE
- spawn(20)
- if(src)
- opacity = FALSE
-
- ..()
-
-/obj/machinery/shield/bullet_act(obj/item/projectile/Proj)
- health -= Proj.damage
- ..()
- if(health <=0)
- visible_message("The [src] dissipates")
- qdel(src)
- return
- opacity = TRUE
- spawn(20)
- if(src)
- opacity = FALSE
-
/obj/machinery/shield/ex_act(severity)
switch(severity)
if(1.0)
@@ -96,33 +59,61 @@
/obj/machinery/shield/blob_act()
qdel(src)
+/obj/machinery/shield/cult
+ name = "cult barrier"
+ desc = "A shield summoned by cultists to keep heretics away."
+ max_integrity = 100
+ icon_state = "shield-cult"
-/obj/machinery/shield/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
- ..()
- var/tforce = 0
- if(ismob(AM))
- tforce = 40
- else if(isobj(AM))
- var/obj/O = AM
- tforce = O.throwforce
-
- health -= tforce
-
- //This seemed to be the best sound for hitting a force field.
- playsound(loc, 'sound/effects/empulse.ogg', 100, 1)
+/obj/machinery/shield/cult/emp_act(severity)
+ return
- //Handle the destruction of the shield
- if(health <= 0)
- visible_message("The [src] dissipates")
- qdel(src)
- return
+/obj/machinery/shield/cult/narsie
+ name = "sanguine barrier"
+ desc = "A potent shield summoned by cultists to defend their rites."
+ max_integrity = 60
+
+/obj/machinery/shield/cult/weak
+ name = "Invoker's Shield"
+ desc = "A weak shield summoned by cultists to protect them while they carry out delicate rituals."
+ max_integrity = 20
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ layer = ABOVE_MOB_LAYER
+
+/obj/machinery/shield/cult/barrier
+ density = FALSE //toggled on right away by the parent rune
+ /// The rune that created the shield itself. Used to delete the rune when the shield is destroyed.
+ var/obj/effect/rune/parent_rune
+
+/obj/machinery/shield/cult/barrier/attack_hand(mob/living/user)
+ parent_rune.attack_hand(user)
+
+/obj/machinery/shield/cult/barrier/attack_animal(mob/living/simple_animal/user)
+ if(iscultist(user))
+ parent_rune.attack_animal(user)
+ else
+ ..()
- //The shield becomes dense to absorb the blow.. purely asthetic.
- opacity = TRUE
- spawn(20)
- if(src)
- opacity = FALSE
+/obj/machinery/shield/cult/barrier/Destroy()
+ if(parent_rune && !QDELETED(parent_rune))
+ QDEL_NULL(parent_rune)
+ return ..()
+/**
+* Turns the shield on and off.
+*
+* The shield has 2 states: on and off. When on, it will block movement, projectiles, items, etc. and be clearly visible, and block atmospheric gases.
+* When off, the rune no longer blocks anything and turns invisible.
+* The barrier itself is not intended to interact with the conceal runes cult spell for balance purposes.
+*/
+/obj/machinery/shield/cult/barrier/proc/Toggle()
+ if(!density) // Currently invisible
+ density = TRUE // Turn visible
+ invisibility = initial(invisibility)
+ else // Currently visible
+ density = FALSE // Turn invisible
+ invisibility = INVISIBILITY_MAXIMUM
+ air_update_turf(1)
/obj/machinery/shieldgen
name = "Emergency shield projector"
diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm
index b7eea1b367fa3..bf24498e4b249 100644
--- a/code/game/objects/effects/decals/Cleanable/humans.dm
+++ b/code/game/objects/effects/decals/Cleanable/humans.dm
@@ -37,26 +37,12 @@ GLOBAL_LIST_EMPTY(splatter_cache)
/obj/effect/decal/cleanable/blood/Initialize(mapload)
. = ..()
update_icon()
- if(GAMEMODE_IS_CULT)
- var/datum/game_mode/cult/mode_ticker = SSticker.mode
- var/turf/T = get_turf(src)
- if(T && (is_station_level(T.z)))//F I V E T I L E S
- if(!(T in mode_ticker.bloody_floors))
- mode_ticker.bloody_floors += T
- mode_ticker.bloody_floors[T] = T
- mode_ticker.blood_check()
if(type == /obj/effect/decal/cleanable/blood/gibs)
return
if(!.)
dry_timer = addtimer(CALLBACK(src, .proc/dry), DRYING_TIME * (amount+1), TIMER_STOPPABLE)
/obj/effect/decal/cleanable/blood/Destroy()
- if(GAMEMODE_IS_CULT)
- var/datum/game_mode/cult/mode_ticker = SSticker.mode
- var/turf/T = get_turf(src)
- if(T && (is_station_level(T.z)))
- mode_ticker.bloody_floors -= T
- mode_ticker.blood_check()
if(dry_timer)
deltimer(dry_timer)
return ..()
diff --git a/code/game/objects/effects/decals/Cleanable/tracks.dm b/code/game/objects/effects/decals/Cleanable/tracks.dm
index 832a66a93bad2..2c6b2678e6392 100644
--- a/code/game/objects/effects/decals/Cleanable/tracks.dm
+++ b/code/game/objects/effects/decals/Cleanable/tracks.dm
@@ -136,3 +136,8 @@ GLOBAL_LIST_EMPTY(fluidtrack_cache)
if(blood_state != C.blood_state) //We only replace footprints of the same type as us
return
..()
+
+/obj/effect/decal/cleanable/blood/footprints/can_bloodcrawl_in()
+ if(basecolor == COLOR_BLOOD_MACHINE)
+ return FALSE
+ return TRUE
diff --git a/code/game/objects/effects/decals/crayon.dm b/code/game/objects/effects/decals/crayon.dm
index e8666d26d64ae..e336ace5fb54b 100644
--- a/code/game/objects/effects/decals/crayon.dm
+++ b/code/game/objects/effects/decals/crayon.dm
@@ -9,7 +9,7 @@
mergeable_decal = FALSE // Allows crayon drawings to overlap one another.
-/obj/effect/decal/cleanable/crayon/Initialize(mapload, main = "#FFFFFF", var/type = "rune1", var/e_name = "rune")
+/obj/effect/decal/cleanable/crayon/Initialize(mapload, main = "#FFFFFF", type = "rune1", e_name = "rune")
. = ..()
name = e_name
diff --git a/code/game/objects/effects/temporary_visuals/cult.dm b/code/game/objects/effects/temporary_visuals/cult.dm
index 775b4eafceb7f..a1b2d4320a973 100644
--- a/code/game/objects/effects/temporary_visuals/cult.dm
+++ b/code/game/objects/effects/temporary_visuals/cult.dm
@@ -11,7 +11,7 @@
/obj/effect/temp_visual/dir_setting/cult/phase
name = "phase glow"
- duration = 7
+ duration = 12
icon = 'icons/effects/cult_effects.dmi'
icon_state = "cultin"
@@ -19,7 +19,7 @@
icon_state = "cultout"
/obj/effect/temp_visual/cult/sac
- name = "maw of Nar-Sie"
+ name = "maw of Nar'Sie"
icon_state = "sacconsume"
/obj/effect/temp_visual/cult/door
@@ -39,3 +39,113 @@
icon_state = "floorglow"
duration = 5
plane = FLOOR_PLANE
+
+/obj/effect/temp_visual/cult/portal
+ icon_state = "space"
+ duration = 600
+ layer = ABOVE_OBJ_LAYER
+
+/obj/effect/temp_visual/emp/cult
+ name = "cult emp sparks"
+ icon_state = "empdisable_cult"
+
+/obj/effect/temp_visual/emp/pulse/cult
+ name = "cult emp pulse"
+ icon_state = "emppulse_cult"
+
+//visuals for runes being magically created
+/obj/effect/temp_visual/cult/rune_spawn
+ icon_state = "runeouter"
+ alpha = 0
+ var/turnedness = 179 //179 turns counterclockwise, 181 turns clockwise
+
+/obj/effect/temp_visual/cult/rune_spawn/Initialize(mapload, set_duration, set_color)
+ if(isnum(set_duration))
+ duration = set_duration
+ if(set_color)
+ add_atom_colour(set_color, FIXED_COLOUR_PRIORITY)
+ . = ..()
+ var/oldtransform = transform
+ transform = matrix() * 2
+ var/matrix/M = transform
+ M.Turn(turnedness)
+ transform = M
+ animate(src, alpha = 255, time = duration, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL)
+ animate(src, transform = oldtransform, time = duration, flags = ANIMATION_PARALLEL)
+
+/obj/effect/temp_visual/cult/rune_spawn/rune1
+ icon_state = "rune1words"
+ turnedness = 181
+
+/obj/effect/temp_visual/cult/rune_spawn/rune1/inner
+ icon_state = "rune1inner"
+ turnedness = 179
+
+/obj/effect/temp_visual/cult/rune_spawn/rune1/center
+ icon_state = "rune1center"
+
+/obj/effect/temp_visual/cult/rune_spawn/rune2
+ icon_state = "rune2words"
+ turnedness = 181
+
+/obj/effect/temp_visual/cult/rune_spawn/rune2/inner
+ icon_state = "rune2inner"
+ turnedness = 179
+
+/obj/effect/temp_visual/cult/rune_spawn/rune2/center
+ icon_state = "rune2center"
+
+/obj/effect/temp_visual/cult/rune_spawn/rune3
+ icon_state = "rune3words"
+ turnedness = 181
+
+/obj/effect/temp_visual/cult/rune_spawn/rune3/inner
+ icon_state = "rune3inner"
+ turnedness = 179
+
+/obj/effect/temp_visual/cult/rune_spawn/rune3/center
+ icon_state = "rune3center"
+
+/obj/effect/temp_visual/cult/rune_spawn/rune4
+ icon_state = "rune4words"
+ turnedness = 181
+
+/obj/effect/temp_visual/cult/rune_spawn/rune4/inner
+ icon_state = "rune4inner"
+ turnedness = 179
+
+/obj/effect/temp_visual/cult/rune_spawn/rune4/center
+ icon_state = "rune4center"
+
+/obj/effect/temp_visual/cult/rune_spawn/rune5
+ icon_state = "rune5words"
+ turnedness = 181
+
+/obj/effect/temp_visual/cult/rune_spawn/rune5/inner
+ icon_state = "rune5inner"
+ turnedness = 179
+
+/obj/effect/temp_visual/cult/rune_spawn/rune5/center
+ icon_state = "rune5center"
+
+/obj/effect/temp_visual/cult/rune_spawn/rune6
+ icon_state = "rune6words"
+ turnedness = 181
+
+/obj/effect/temp_visual/cult/rune_spawn/rune6/inner
+ icon_state = "rune6inner"
+ turnedness = 179
+
+/obj/effect/temp_visual/cult/rune_spawn/rune6/center
+ icon_state = "rune6center"
+
+/obj/effect/temp_visual/cult/rune_spawn/rune7
+ icon_state = "rune7words"
+ turnedness = 181
+
+/obj/effect/temp_visual/cult/rune_spawn/rune7/inner
+ icon_state = "rune7inner"
+ turnedness = 179
+
+/obj/effect/temp_visual/cult/rune_spawn/rune7/center
+ icon_state = "rune7center"
diff --git a/code/game/objects/empulse.dm b/code/game/objects/empulse.dm
index 3e7f476e34678..30c0063ea7849 100644
--- a/code/game/objects/empulse.dm
+++ b/code/game/objects/empulse.dm
@@ -1,4 +1,4 @@
-/proc/empulse(turf/epicenter, heavy_range, light_range, log=0, cause = null)
+/proc/empulse(turf/epicenter, heavy_range, light_range, log = FALSE, cause = null)
if(!epicenter) return
if(!istype(epicenter, /turf))
@@ -9,7 +9,10 @@
log_game("EMP with size ([heavy_range], [light_range]) in area [epicenter.loc.name] [cause ? "(Cause: [cause])" : ""] [COORD(epicenter)]")
if(heavy_range > 1)
- new/obj/effect/temp_visual/emp/pulse(epicenter)
+ if(cause == "cult")
+ new /obj/effect/temp_visual/emp/pulse/cult(epicenter)
+ else
+ new /obj/effect/temp_visual/emp/pulse(epicenter)
if(heavy_range > light_range)
light_range = heavy_range
@@ -17,16 +20,28 @@
for(var/mob/M in range(heavy_range, epicenter))
M << 'sound/effects/empulse.ogg'
for(var/atom/T in range(light_range, epicenter))
+ if(cause == "cult" && iscultist(T))
+ continue
var/distance = get_dist(epicenter, T)
+ var/will_affect = FALSE
+
if(distance < 0)
distance = 0
if(distance < heavy_range)
- T.emp_act(1)
+ will_affect = T.emp_act(1)
+
else if(distance == heavy_range)
if(prob(50))
- T.emp_act(1)
+ will_affect = T.emp_act(1)
else
- T.emp_act(2)
+ will_affect = T.emp_act(2)
+
else if(distance <= light_range)
- T.emp_act(2)
- return 1
+ will_affect = T.emp_act(2)
+
+ if(will_affect)
+ if(cause == "cult")
+ new /obj/effect/temp_visual/emp/cult(T.loc)
+ else
+ new /obj/effect/temp_visual/emp(T.loc)
+ return TRUE
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index ce52e260bca91..4ae316236dfb7 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -29,7 +29,7 @@
/obj/item/toy/crayon/New()
..()
name = "[colourName] crayon" //Makes crayons identifiable in things like grinders
- drawtype = pick(pick(graffiti), pick(letters), "rune[rand(1,10)]")
+ drawtype = pick(pick(graffiti), pick(letters), "rune[rand(1, 8)]")
/obj/item/toy/crayon/attack_self(mob/living/user as mob)
update_window(user)
@@ -40,8 +40,8 @@
dat += "
"
dat += "Runes:
"
dat += "Random rune"
- for(var/i = 1; i <= 10; i++)
- dat += "Rune[i]"
+ for(var/i = 1; i <= 8; i++)
+ dat += "Rune [i]"
if(!((i + 1) % 3)) //3 buttons in a row
dat += "
"
dat += "
"
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 6b70ac015ca2a..d6e21d6d482c2 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -344,12 +344,12 @@ GLOBAL_LIST_INIT(cardboard_recipes, list (
*/
GLOBAL_LIST_INIT(cult_recipes, list ( \
- new/datum/stack_recipe/cult("runed door", /obj/machinery/door/airlock/cult, 1, time = 50, one_per_turf = 1, on_floor = 1),
- new/datum/stack_recipe/cult("runed girder", /obj/structure/girder/cult, 1, time = 50, one_per_turf = 1, on_floor = 1), \
- new/datum/stack_recipe/cult("pylon", /obj/structure/cult/functional/pylon, 3, time = 40, one_per_turf = 1, on_floor = 1), \
- new/datum/stack_recipe/cult("forge", /obj/structure/cult/functional/forge, 5, time = 40, one_per_turf = 1, on_floor = 1), \
- new/datum/stack_recipe/cult("archives", /obj/structure/cult/functional/archives, 2, time = 40, one_per_turf = 1, on_floor = 1), \
- new/datum/stack_recipe/cult("altar", /obj/structure/cult/functional/altar, 5, time = 40, one_per_turf = 1, on_floor = 1), \
+ new /datum/stack_recipe/cult("runed door (stuns non-cultists)", /obj/machinery/door/airlock/cult, 1, time = 50, one_per_turf = TRUE, on_floor = TRUE, no_cult_structure = TRUE),
+ new /datum/stack_recipe/cult("runed girder (used to make cult walls)", /obj/structure/girder/cult, 1, time = 10, one_per_turf = TRUE, on_floor = TRUE, no_cult_structure = TRUE), \
+ new /datum/stack_recipe/cult("pylon (heals nearby cultists)", /obj/structure/cult/functional/pylon, 4, time = 40, one_per_turf = TRUE, on_floor = TRUE, no_cult_structure = TRUE), \
+ new /datum/stack_recipe/cult("forge (crafts shielded robes, flagellant's robes, and mirror shields)", /obj/structure/cult/functional/forge, 3, time = 40, one_per_turf = TRUE, on_floor = TRUE, no_cult_structure = TRUE), \
+ new /datum/stack_recipe/cult("archives (crafts zealot's blindfolds, shuttle curse orbs, and veil shifters)", /obj/structure/cult/functional/archives, 3, time = 40, one_per_turf = TRUE, on_floor = TRUE, no_cult_structure = TRUE), \
+ new /datum/stack_recipe/cult("altar (crafts eldritch whetstones, construct shells, and flasks of unholy water)", /obj/structure/cult/functional/altar, 3, time = 40, one_per_turf = TRUE, on_floor = TRUE, no_cult_structure = TRUE),
))
/obj/item/stack/sheet/runed_metal
@@ -360,6 +360,7 @@ GLOBAL_LIST_INIT(cult_recipes, list ( \
item_state = "sheet-metal"
sheettype = "runed"
merge_type = /obj/item/stack/sheet/runed_metal
+ recipe_width = 700
/obj/item/stack/sheet/runed_metal/New()
. = ..()
@@ -380,8 +381,8 @@ GLOBAL_LIST_INIT(cult_recipes, list ( \
return ..()
/datum/stack_recipe/cult
- one_per_turf = 1
- on_floor = 1
+ one_per_turf = TRUE
+ on_floor = TRUE
/datum/stack_recipe/cult/post_build(obj/item/stack/S, obj/result)
if(ishuman(S.loc))
@@ -389,6 +390,9 @@ GLOBAL_LIST_INIT(cult_recipes, list ( \
H.bleed(5)
..()
+/obj/item/stack/sheet/runed_metal/ten
+ amount = 10
+
/obj/item/stack/sheet/runed_metal/fifty
amount = 50
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index ab592cc040233..b57c3435132ff 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -16,6 +16,8 @@
var/to_transfer = 0
var/max_amount = 50 //also see stack recipes initialisation, param "max_res_amount" must be equal to this max_amount
var/merge_type = null // This path and its children should merge with this stack, defaults to src.type
+ var/recipe_width = 400 //Width of the recipe popup
+ var/recipe_height = 400 //Height of the recipe popup
/obj/item/stack/New(loc, new_amount, merge = TRUE)
..()
@@ -138,7 +140,7 @@
if(!(max_multiplier in multipliers))
t1 += " [max_multiplier * R.res_amount]x"
- var/datum/browser/popup = new(user, "stack", name, 400, 400)
+ var/datum/browser/popup = new(user, "stack", name, recipe_width, recipe_height)
popup.set_content(t1)
popup.open(0)
onclose(user, "stack")
@@ -170,7 +172,7 @@
to_chat(usr, "You haven't got enough [src] to build \the [R.req_amount * multiplier] [R.title]\s!")
else
to_chat(usr, "You haven't got enough [src] to build \the [R.title]!")
- return 0
+ return FALSE
if(R.window_checks && !valid_window_location(usr.loc, usr.dir))
to_chat(usr, "The [R.title] won't fit here!")
@@ -178,11 +180,15 @@
if(R.one_per_turf && (locate(R.result_type) in usr.drop_location()))
to_chat(usr, "There is another [R.title] here!")
- return 0
+ return FALSE
if(R.on_floor && !istype(usr.drop_location(), /turf/simulated))
to_chat(usr, "\The [R.title] must be constructed on the floor!")
- return 0
+ return FALSE
+
+ if(R.no_cult_structure && (locate(/obj/structure/cult) in usr.drop_location()))
+ to_chat(usr, "There is a structure here!")
+ return FALSE
if(R.time)
to_chat(usr, "Building [R.title] ...")
diff --git a/code/game/objects/items/stacks/stack_recipe.dm b/code/game/objects/items/stacks/stack_recipe.dm
index 5197029728191..20d360059af46 100644
--- a/code/game/objects/items/stacks/stack_recipe.dm
+++ b/code/game/objects/items/stacks/stack_recipe.dm
@@ -12,8 +12,9 @@
var/one_per_turf = 0
var/on_floor = 0
var/window_checks = FALSE
+ var/no_cult_structure = FALSE
-/datum/stack_recipe/New(title, result_type, req_amount = 1, res_amount = 1, max_res_amount = 1, time = 0, one_per_turf = 0, on_floor = 0, window_checks = FALSE)
+/datum/stack_recipe/New(title, result_type, req_amount = 1, res_amount = 1, max_res_amount = 1, time = 0, one_per_turf = 0, on_floor = 0, window_checks = FALSE, no_cult_structure = FALSE)
src.title = title
src.result_type = result_type
src.req_amount = req_amount
@@ -23,6 +24,7 @@
src.one_per_turf = one_per_turf
src.on_floor = on_floor
src.window_checks = window_checks
+ src.no_cult_structure = no_cult_structure
/datum/stack_recipe/proc/post_build(obj/item/stack/S, obj/result)
return
diff --git a/code/game/objects/items/weapons/holy_weapons.dm b/code/game/objects/items/weapons/holy_weapons.dm
index 974bd3b677fbc..39f2c71f77e55 100644
--- a/code/game/objects/items/weapons/holy_weapons.dm
+++ b/code/game/objects/items/weapons/holy_weapons.dm
@@ -1,6 +1,6 @@
/obj/item/nullrod
name = "null rod"
- desc = "A rod of pure obsidian, its very presence disrupts and dampens the powers of Nar-Sie's followers."
+ desc = "A rod of pure obsidian, its very presence disrupts and dampens the powers of Nar'Sie's followers."
icon_state = "nullrod"
item_state = "nullrod"
force = 15
diff --git a/code/game/objects/items/weapons/soap.dm b/code/game/objects/items/weapons/soap.dm
index cb220ff9565a0..fbe7e9b370ed3 100644
--- a/code/game/objects/items/weapons/soap.dm
+++ b/code/game/objects/items/weapons/soap.dm
@@ -28,7 +28,7 @@
to_chat(user, "You take a bite of the [name]. Delicious!")
playsound(user.loc, 'sound/items/eatfood.ogg', 50, 0)
user.adjust_nutrition(2)
- else if(istype(target,/obj/effect/decal/cleanable))
+ else if(istype(target, /obj/effect/decal/cleanable) || istype(target, /obj/effect/rune))
user.visible_message("[user] begins to scrub \the [target.name] out with [src].")
if(do_after(user, cleanspeed, target = target) && target)
to_chat(user, "You scrub \the [target.name] out.")
diff --git a/code/game/objects/items/weapons/storage/bible.dm b/code/game/objects/items/weapons/storage/bible.dm
index c4afed4b266f7..d6bd46df8e2de 100644
--- a/code/game/objects/items/weapons/storage/bible.dm
+++ b/code/game/objects/items/weapons/storage/bible.dm
@@ -93,10 +93,14 @@
return
if(istype(A, /turf/simulated/floor))
to_chat(user, "You hit the floor with the bible.")
- if(user.mind && (user.mind.isholy))
- for(var/obj/effect/rune/R in A)
- if(R.invisibility)
- R.talismanreveal()
+ if(user.mind && user.mind.isholy)
+ for(var/obj/O in A)
+ O.cult_reveal()
+ if(istype(A, /obj/machinery/door/airlock))
+ to_chat(user, "You hit the airlock with the bible.")
+ if(user.mind && user.mind.isholy)
+ var/obj/airlock = A
+ airlock.cult_reveal()
if(user.mind && (user.mind.isholy))
if(A.reagents && A.reagents.has_reagent("water")) //blesses all the water in the holder
to_chat(user, "You bless [A].")
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index aa00235828db5..1a4f0691e2113 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -353,6 +353,12 @@ a {
/obj/proc/check_uplink_validity()
return TRUE
+/obj/proc/cult_conceal() //Called by cult conceal spell
+ return
+
+/obj/proc/cult_reveal() //Called by cult reveal spell and chaplain's bible
+ return
+
/obj/proc/force_eject_occupant(mob/target)
// This proc handles safely removing occupant mobs from the object if they must be teleported out (due to being SSD/AFK, by admin teleport, etc) or transformed.
// In the event that the object doesn't have an overriden version of this proc to do it, log a runtime so one can be added.
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/chaplain.dm b/code/game/objects/structures/crates_lockers/closets/secure/chaplain.dm
index aec562ac843df..1edebe36cf0c0 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/chaplain.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/chaplain.dm
@@ -23,7 +23,7 @@
new /obj/item/storage/backpack/cultpack(src)
new /obj/item/clothing/head/helmet/riot/knight/templar(src)
new /obj/item/clothing/suit/armor/riot/knight/templar(src)
- new /obj/item/soulstone/anybody/chaplain(src)
+ new /obj/item/soulstone/anybody/purified/chaplain(src)
new /obj/item/storage/fancy/candle_box/eternal(src)
new /obj/item/storage/fancy/candle_box/eternal(src)
new /obj/item/storage/fancy/candle_box/eternal(src)
diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm
index 6d27b21e68671..d4b3ea08115b9 100644
--- a/code/game/objects/structures/girders.dm
+++ b/code/game/objects/structures/girders.dm
@@ -70,7 +70,7 @@
to_chat(user, "There is already a false wall present!")
return
if(istype(W, /obj/item/stack/sheet/runed_metal))
- to_chat(user, "You can't seem to make the metal bend..")
+ to_chat(user, "You can't seem to make the metal bend.")
return
if(istype(W,/obj/item/stack/rods))
@@ -336,8 +336,7 @@
return
state = GIRDER_DISASSEMBLED
TOOL_DISMANTLE_SUCCESS_MESSAGE
- var/obj/item/stack/sheet/metal/M = new(loc, 2)
- M.add_fingerprint(user)
+ refundMetal(metalUsed)
qdel(src)
else
if(!isfloorturf(loc))
@@ -417,13 +416,9 @@
. = ..()
icon_state = SSticker.cultdat?.cult_girder_icon_state
-/obj/structure/girder/cult/refundMetal(metalAmount)
- for(var/i=0;i < metalAmount;i++)
- new /obj/item/stack/sheet/runed_metal(get_turf(src))
-
/obj/structure/girder/cult/attackby(obj/item/W, mob/user, params)
add_fingerprint(user)
- if(istype(W, /obj/item/tome) && iscultist(user)) //Cultists can demolish cult girders instantly with their tomes
+ if(istype(W, /obj/item/melee/cultblade/dagger) && iscultist(user)) //Cultists can demolish cult girders instantly with their dagger
user.visible_message("[user] strikes [src] with [W]!", "You demolish [src].")
refundMetal(metalUsed)
qdel(src)
@@ -451,7 +446,7 @@
to_chat(user, "You need at least one sheet of runed metal to construct a runed wall!")
return 0
user.visible_message("[user] begins laying runed metal on [src]...", "You begin constructing a runed wall...")
- if(do_after(user, 50, target = src))
+ if(do_after(user, 10, target = src))
if(R.get_amount() < 1 || !R)
return
user.visible_message("[user] plates [src] with runed metal.", "You construct a runed wall.")
diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index 700e838169558..2986035e15acb 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -442,12 +442,33 @@
dat += check_role_table("Ninjas", ticker.mode.ninjas)*/
if(SSticker.mode.cult.len)
+ var/datum/game_mode/gamemode = SSticker.mode
+ var/datum/objective/current_sac_obj = gamemode.cult_objs.current_sac_objective()
dat += check_role_table("Cultists", SSticker.mode.cult)
- dat += "
use Cult Mindspeak"
- if(GAMEMODE_IS_CULT)
- var/datum/game_mode/cult/cult_round = SSticker.mode
- if(!cult_round.narsie_condition_cleared)
- dat += "
complete objective (debug)"
+ if(current_sac_obj)
+ dat += "
Current cult objective:
[current_sac_obj.explanation_text]"
+ else if(gamemode.cult_objs.cult_status == NARSIE_NEEDS_SUMMONING)
+ dat += "
Current cult objective: Summon [SSticker.cultdat ? SSticker.cultdat.entity_name : "Nar'Sie"]"
+ else if(gamemode.cult_objs.cult_status == NARSIE_HAS_RISEN)
+ dat += "
Current cult objective: Feed [SSticker.cultdat ? SSticker.cultdat.entity_name : "Nar'Sie"]"
+ else if(gamemode.cult_objs.cult_status == NARSIE_HAS_FALLEN)
+ dat += "
Current cult objective: Kill all non-cultists"
+ else
+ dat += "
Current cult objective: None! (This is most likely a bug, or var editing gone wrong.)"
+ dat += "
Sacrifice objectives completed: [gamemode.cult_objs.sacrifices_done]"
+ dat += "
Sacrifice objectives needed for summoning: [gamemode.cult_objs.sacrifices_required]"
+ dat += "
Summoning locations: [english_list(gamemode.cult_objs.obj_summon.summon_spots)]"
+ dat += "
Cult Mindspeak"
+
+ if(gamemode.cult_objs.cult_status == NARSIE_DEMANDS_SACRIFICE)
+ dat += "
Modify amount of sacrifices required"
+ dat += "
Reroll sacrifice target"
+ else
+ dat += "
Modify amount of sacrifices required (Summon available!)"
+ dat += "
Reroll sacrifice target (Summon available!)"
+
+ dat += "
Reroll summoning locations"
+ dat += "
Unlock Nar'Sie summoning"
if(SSticker.mode.traitors.len)
dat += check_role_table("Traitors", SSticker.mode.traitors)
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 622667a9ac78d..c1b77572a17bb 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -1676,40 +1676,65 @@
return
SStickets.autoRespond(index)
- if(href_list["convert_ticket"])
- var/indexNum = text2num(href_list["convert_ticket"])
- if(href_list["is_mhelp"])
- SSmentor_tickets.convert_to_other_ticket(indexNum)
- else
- SStickets.convert_to_other_ticket(indexNum)
- else if(href_list["cult_nextobj"])
- if(alert(usr, "Validate the current Cult objective and unlock the next one?", "Cult Cheat Code", "Yes", "No") != "Yes")
- return
-
- if(!GAMEMODE_IS_CULT)
- alert("Couldn't locate cult mode datum! This shouldn't ever happen, tell a coder!")
- return
-
- var/datum/game_mode/cult/cult_round = SSticker.mode
- cult_round.bypass_phase()
- message_admins("Admin [key_name_admin(usr)] has unlocked the Cult's next objective.")
- log_admin("Admin [key_name_admin(usr)] has unlocked the Cult's next objective.")
-
else if(href_list["cult_mindspeak"])
- var/input = stripped_input(usr, "Communicate to all the cultists with the voice of [SSticker.cultdat.entity_name]", "Voice of [SSticker.cultdat.entity_name]", "")
+ var/input = stripped_input(usr, "Communicate to all the cultists with the voice of [SSticker.cultdat.entity_name]", "Voice of [SSticker.cultdat.entity_name]")
if(!input)
return
for(var/datum/mind/H in SSticker.mode.cult)
- if (H.current)
- to_chat(H.current, "[SSticker.cultdat.entity_name] murmurs, [input]")
+ if(H.current)
+ to_chat(H.current, "[SSticker.cultdat.entity_name] murmurs, \"[input]\"")
for(var/mob/dead/observer/O in GLOB.player_list)
- to_chat(O, "[SSticker.cultdat.entity_name] murmurs, [input]")
+ to_chat(O, "[SSticker.cultdat.entity_name] murmurs, \"[input]\"")
message_admins("Admin [key_name_admin(usr)] has talked with the Voice of [SSticker.cultdat.entity_name].")
log_admin("[key_name(usr)] Voice of [SSticker.cultdat.entity_name]: [input]")
+ else if(href_list["cult_adjustsacnumber"])
+ var/amount = input("Adjust the amount of sacrifices required before summoning Nar'Sie", "Sacrifice Adjustment", 2) as null | num
+ if(amount > 0)
+ var/datum/game_mode/gamemode = SSticker.mode
+ var/old = gamemode.cult_objs.sacrifices_required
+ gamemode.cult_objs.sacrifices_required = amount
+ message_admins("Admin [key_name_admin(usr)] has modified the amount of cult sacrifices required before summoning from [old] to [amount]")
+ log_admin("Admin [key_name_admin(usr)] has modified the amount of cult sacrifices required before summoning from [old] to [amount]")
+
+ else if(href_list["cult_newtarget"])
+ if(alert(usr, "Reroll the cult's sacrifice target?", "Cult Debug", "Yes", "No") != "Yes")
+ return
+
+ var/datum/game_mode/gamemode = SSticker.mode
+ if(!gamemode.cult_objs.find_new_sacrifice_target())
+ gamemode.cult_objs.ready_to_summon()
+
+ message_admins("Admin [key_name_admin(usr)] has rerolled the Cult's sacrifice target.")
+ log_admin("Admin [key_name_admin(usr)] has rerolled the Cult's sacrifice target.")
+
+ else if(href_list["cult_newsummonlocations"])
+ if(alert(usr, "Reroll the cult's summoning locations?", "Cult Debug", "Yes", "No") != "Yes")
+ return
+
+ var/datum/game_mode/gamemode = SSticker.mode
+ gamemode.cult_objs.obj_summon.find_summon_locations(TRUE)
+ if(gamemode.cult_objs.cult_status == NARSIE_NEEDS_SUMMONING) //Only update cultists if they are already have the summon goal since they arent aware of summon spots till then
+ for(var/datum/mind/cult_mind in gamemode.cult)
+ if(cult_mind && cult_mind.current)
+ to_chat(cult_mind.current, "The veil has shifted! Our summoning will need to take place elsewhere.")
+ to_chat(cult_mind.current, "Current goal : [gamemode.cult_objs.obj_summon.explanation_text]")
+
+ message_admins("Admin [key_name_admin(usr)] has rerolled the Cult's sacrifice target.")
+ log_admin("Admin [key_name_admin(usr)] has rerolled the Cult's sacrifice target.")
+
+ else if(href_list["cult_unlocknarsie"])
+ if(alert(usr, "Unlock the ability to summon Nar'Sie?", "Cult Debug", "Yes", "No") != "Yes")
+ return
+
+ var/datum/game_mode/gamemode = SSticker.mode
+ gamemode.cult_objs.ready_to_summon()
+ message_admins("Admin [key_name_admin(usr)] has unlocked the Cult's ability to summon Nar'Sie.")
+ log_admin("Admin [key_name_admin(usr)] has unlocked the Cult's ability to summon Nar'Sie.")
+
else if(href_list["adminplayerobservecoodjump"])
if(!check_rights(R_ADMIN)) return
diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm
index 779687b25011c..4edc94bbf5aaa 100644
--- a/code/modules/admin/verbs/one_click_antag.dm
+++ b/code/modules/admin/verbs/one_click_antag.dm
@@ -178,12 +178,6 @@
H = pick(candidates)
SSticker.mode.add_cultist(H.mind)
candidates.Remove(H)
- if(!GLOB.summon_spots.len)
- while(GLOB.summon_spots.len < SUMMON_POSSIBILITIES)
- var/area/summon = pick(return_sorted_areas() - GLOB.summon_spots)
- if(summon && is_station_level(summon.z) && summon.valid_territory)
- GLOB.summon_spots += summon
-
return 1
return 0
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index e216ad6f5d1c6..0bf474aca01b6 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -2237,6 +2237,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
character.change_gender(MALE)
character.change_eye_color(e_colour)
+ character.original_eye_color = e_colour
if(disabilities & DISABILITY_FLAG_FAT)
character.dna.SetSEState(GLOB.fatblock, TRUE, TRUE)
diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm
index 62f740ad7b03e..240d6181b6647 100644
--- a/code/modules/clothing/shoes/miscellaneous.dm
+++ b/code/modules/clothing/shoes/miscellaneous.dm
@@ -147,7 +147,7 @@
/obj/item/clothing/shoes/cult
name = "boots"
- desc = "A pair of boots worn by the followers of Nar-Sie."
+ desc = "A pair of boots usually worn by cultists."
icon_state = "cult"
item_state = "cult"
item_color = "cult"
@@ -156,6 +156,7 @@
min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT
heat_protection = FEET
max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT
+ magical = TRUE
/obj/item/clothing/shoes/cyborg
name = "cyborg boots"
diff --git a/code/modules/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm
index 76bcd87921dce..bd485a732ceb3 100644
--- a/code/modules/clothing/under/accessories/accessory.dm
+++ b/code/modules/clothing/under/accessories/accessory.dm
@@ -507,7 +507,7 @@
to_chat(user, "You have to open it first.")
return
- if(istype(O,/obj/item/paper) || istype(O, /obj/item/photo) && !(istype(O, /obj/item/paper/talisman)))
+ if(istype(O,/obj/item/paper) || istype(O, /obj/item/photo))
if(held)
to_chat(usr, "[src] already has something inside it.")
else
diff --git a/code/modules/library/computers/checkout.dm b/code/modules/library/computers/checkout.dm
index 934234f645ee3..1735f6aa1eb91 100644
--- a/code/modules/library/computers/checkout.dm
+++ b/code/modules/library/computers/checkout.dm
@@ -47,8 +47,8 @@
dat += ""
if(src.arcanecheckout)
- new /obj/item/tome(src.loc)
- 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.")
+ new /obj/item/melee/cultblade/dagger(src.loc)
+ 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 strange looking dagger sitting on the desk. You don't really remember where it came from.")
user.visible_message("[user] stares at the blank screen for a few moments, [user.p_their()] expression frozen in fear. When [user.p_they()] finally awaken[user.p_s()] from it, [user.p_they()] look[user.p_s()] a lot older.", 2)
src.arcanecheckout = 0
if(1)
@@ -186,7 +186,7 @@
if(8)
dat += {"Accessing Forbidden Lore Vault v 1.3
- Are you absolutely sure you want to proceed? EldritchTomes Inc. takes no responsibilities for loss of sanity resulting from this action.
+ Are you absolutely sure you want to proceed? EldritchArtifacts Inc. takes no responsibilities for loss of sanity resulting from this action.
Yes.
No.
"}
diff --git a/code/modules/mob/dead/observer/orbit.dm b/code/modules/mob/dead/observer/orbit.dm
index 77b1546dcb00b..fc5a3e4f9849f 100644
--- a/code/modules/mob/dead/observer/orbit.dm
+++ b/code/modules/mob/dead/observer/orbit.dm
@@ -58,7 +58,7 @@
var/mob/M = poi
if(istype(M))
- if (isobserver(M))
+ if(isobserver(M))
ghosts += list(serialized)
else if(M.stat == DEAD)
dead += list(serialized)
@@ -80,16 +80,24 @@
antag_serialized["antag"] = A.name
antagonists += list(antag_serialized)
+ // Changelings
if(mind.changeling)
var/antag_serialized = serialized.Copy()
antag_serialized["antag"] = "Changeling"
antagonists += list(antag_serialized)
+ // Vampires
if(mind.vampire)
var/antag_serialized = serialized.Copy()
antag_serialized["antag"] = "Vampire"
antagonists += list(antag_serialized)
+ // Cultists
+ if(SSticker.mode.cult && (mind in SSticker.mode.cult))
+ var/antag_serialized = serialized.Copy()
+ antag_serialized["antag"] = "Cultist"
+ antagonists += list(antag_serialized)
+
// Other antags are not in the list, mostly because I don't know their code well enough,
// and am not sure how to extract the "is this is an antag?" Info easily.
// If you are annoyed by this - datumize them and put under `.antag_datums`!
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index 8f4f4dbb2da50..f61e04ba06404 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -263,3 +263,12 @@
return r_hand
return null
+//search for a path in inventory and storage items in that inventory (backpack, belt, etc) and return it. Not recursive, so doesnt search storage in storage
+/mob/proc/find_item(path)
+ for(var/obj/item/I in contents)
+ if(istype(I, /obj/item/storage))
+ for(var/obj/item/SI in I.contents)
+ if(istype(SI, path))
+ return SI
+ else if(istype(I, path))
+ return I
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index e0cba0b028114..73ab48a4312e4 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -72,3 +72,16 @@
/mob/living/carbon/is_mouth_covered(head_only = FALSE, mask_only = FALSE)
if((!mask_only && head && (head.flags_cover & HEADCOVERSMOUTH)) || (!head_only && wear_mask && (wear_mask.flags_cover & MASKCOVERSMOUTH)))
return TRUE
+
+//Called when drawing cult runes/using cult spells. Deal damage to a random arm/hand, or chest if not there.
+/mob/living/carbon/cult_self_harm(damage, rune_message = FALSE)
+ var/dam_zone = pick("l_arm", "l_hand", "r_arm", "r_hand")
+ var/obj/item/organ/external/affecting = get_organ(dam_zone)
+ if(!affecting)
+ affecting = get_organ("chest")
+ if(!affecting) //bruh where's your chest
+ return FALSE
+ apply_damage(damage, BRUTE, affecting)
+ if(rune_message)
+ visible_message("[src] cuts open [src.p_their()] [affecting.name] and begins writing in [src.p_their()] own blood!",
+ "You slice open your [affecting.name] and begin drawing a sigil of [SSticker.cultdat.entity_title3].")
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 7f4b90aef0bb3..fbaaeb124b310 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -23,6 +23,8 @@
if(wear_mask)
skipface |= wear_mask.flags_inv & HIDEFACE
+ skipeyes |= wear_mask.flags_inv & HIDEEYES
+
var/msg = "*---------*\nThis is "
if(!(skipjumpsuit && skipface) && icon) //big suits/masks/helmets make it hard to tell their gender
@@ -143,11 +145,14 @@
msg += "[p_they(TRUE)] [p_have()] [bicon(wear_mask)] \a [wear_mask] on [p_their()] face.\n"
//eyes
- if(glasses && !skipeyes && !(glasses.flags & ABSTRACT))
- if(glasses.blood_DNA)
- msg += "[p_they(TRUE)] [p_have()] [bicon(glasses)] [glasses.gender==PLURAL?"some":"a"] [glasses.blood_color != "#030303" ? "blood-stained":"oil-stained"] [glasses] covering [p_their()] eyes!\n"
- else
- msg += "[p_they(TRUE)] [p_have()] [bicon(glasses)] \a [glasses] covering [p_their()] eyes.\n"
+ if(!skipeyes)
+ if(glasses && !(glasses.flags & ABSTRACT))
+ if(glasses.blood_DNA)
+ msg += "[p_they(TRUE)] [p_have()] [bicon(glasses)] [glasses.gender==PLURAL?"some":"a"] [glasses.blood_color != "#030303" ? "blood-stained":"oil-stained"] [glasses] covering [p_their()] eyes!\n"
+ else
+ msg += "[p_they(TRUE)] [p_have()] [bicon(glasses)] \a [glasses] covering [p_their()] eyes.\n"
+ else if(iscultist(src) && HAS_TRAIT(src, CULT_EYES))
+ msg += "[p_their(TRUE)] eyes are glowing an unnatural red!\n"
//left ear
if(l_ear && !skipears)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 379a54970d500..2f2c4da0f11ca 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -1608,6 +1608,11 @@ Eyes need to have significantly high darksight to shine unless the mob has the X
to_chat(src, "\The [S] pulls \the [hand] from your grip!")
apply_effect(current_size * 3, IRRADIATE)
+/mob/living/carbon/human/narsie_act()
+ if(iswizard(src) && iscultist(src)) //Wizard cultists are immune to narsie because it would prematurely end the wiz round that's about to end by the automated shuttle call anyway
+ return
+ ..()
+
/mob/living/carbon/human/proc/do_cpr(mob/living/carbon/human/H)
if(H == src)
to_chat(src, "You cannot perform CPR on yourself!")
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index bc5c85e29299a..4cd2d1847d941 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -68,3 +68,4 @@
var/tail // Name of tail image in species effects icon file.
var/list/splinted_limbs = list() //limbs we know are splinted
+ var/original_eye_color = "#000000"
diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm
index 6716c8f1a0f0d..1b4bc42009631 100644
--- a/code/modules/mob/living/carbon/human/species/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/_species.dm
@@ -78,7 +78,7 @@
var/bodyflags = 0
var/dietflags = 0 // Make sure you set this, otherwise it won't be able to digest a lot of foods
- var/blood_color = "#A10808" //Red.
+ var/blood_color = COLOR_BLOOD_BASE //Red.
var/flesh_color = "#d1aa2e" //Gold.
var/single_gib_type = /obj/effect/decal/cleanable/blood/gibs
var/remains_type = /obj/effect/decal/remains/human //What sort of remains is left behind when the species dusts
@@ -831,6 +831,9 @@ It'll return null if the organ doesn't correspond, so include null checks when u
if(XRAY in H.mutations)
H.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
+ if(H.has_status_effect(STATUS_EFFECT_SUMMONEDGHOST))
+ H.see_invisible = SEE_INVISIBLE_OBSERVER
+
H.sync_lighting_plane_alpha()
/datum/species/proc/water_act(mob/living/carbon/human/M, volume, temperature, source, method = REAGENT_TOUCH)
diff --git a/code/modules/mob/living/carbon/human/species/machine.dm b/code/modules/mob/living/carbon/human/species/machine.dm
index a1cab2ea46645..b54b02929f91f 100644
--- a/code/modules/mob/living/carbon/human/species/machine.dm
+++ b/code/modules/mob/living/carbon/human/species/machine.dm
@@ -27,7 +27,7 @@
bodyflags = HAS_SKIN_COLOR | HAS_HEAD_MARKINGS | HAS_HEAD_ACCESSORY | ALL_RPARTS
dietflags = 0 //IPCs can't eat, so no diet
taste_sensitivity = TASTE_SENSITIVITY_NO_TASTE
- blood_color = "#1F181F"
+ blood_color = COLOR_BLOOD_MACHINE
flesh_color = "#AAAAAA"
//Default styles for created mobs.
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 71ee1e00871e5..91f285b28732f 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -525,6 +525,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
UpdateDamageIcon()
force_update_limbs()
update_tail_layer()
+ update_halo_layer()
overlays.Cut() // Force all overlays to regenerate
update_fire()
update_icons()
@@ -1264,6 +1265,17 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
apply_overlay(MISC_LAYER)
+/mob/living/carbon/human/proc/update_halo_layer()
+ remove_overlay(HALO_LAYER)
+
+ if(iscultist(src) && SSticker.mode.cult_ascendant)
+ var/istate = pick("halo1", "halo2", "halo3", "halo4", "halo5", "halo6")
+ var/mutable_appearance/new_halo_overlay = mutable_appearance('icons/effects/32x64.dmi', istate, -HALO_LAYER)
+ overlays_standing[HALO_LAYER] = new_halo_overlay
+
+ apply_overlay(HALO_LAYER)
+
+
/mob/living/carbon/human/admin_Freeze(client/admin, skip_overlays = TRUE, mech = null)
if(..())
overlays_standing[FROZEN_LAYER] = mutable_appearance(frozen, layer = -FROZEN_LAYER)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 883b781c43ce7..bb76b436ad0ce 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -422,6 +422,11 @@
C.reagents.clear_reagents()
QDEL_LIST(C.reagents.addiction_list)
C.reagents.addiction_threshold_accumulated.Cut()
+ if(iscultist(src))
+ if(SSticker.mode.cult_risen)
+ SSticker.mode.rise(src)
+ if(SSticker.mode.cult_ascendant)
+ SSticker.mode.ascend(src)
QDEL_LIST(C.processing_patches)
@@ -835,7 +840,7 @@
/mob/living/narsie_act()
if(client)
- makeNewConstruct(/mob/living/simple_animal/hostile/construct/harvester, src, null, 1)
+ make_new_construct(/mob/living/simple_animal/hostile/construct/harvester, src, cult_override = TRUE)
spawn_dust()
gib()
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 9ecda42de04ac..bc3a7e5a41b1b 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -357,3 +357,6 @@
if(INTENT_DISARM)
M.do_attack_animation(src, ATTACK_EFFECT_DISARM)
return TRUE
+
+/mob/living/proc/cult_self_harm(damage, rune_message = FALSE)
+ return FALSE
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index 4c2c2332924cf..96d60cdca2b17 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -1,4 +1,3 @@
-
/mob/living/simple_animal/hostile/construct
name = "Construct"
real_name = "Construct"
@@ -13,6 +12,7 @@
a_intent = INTENT_HARM
stop_automated_movement = 1
status_flags = CANPUSH
+ see_in_dark = 8
attack_sound = 'sound/weapons/punch1.ogg'
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
@@ -44,12 +44,11 @@
for(var/spell in construct_spells)
AddSpell(new spell(null))
- if(SSticker.cultdat?.theme == "blood")
- updateglow()
+ set_light(2, 3, l_color = SSticker.cultdat ? SSticker.cultdat.construct_glow : LIGHT_COLOR_BLOOD_MAGIC)
/mob/living/simple_animal/hostile/construct/death(gibbed)
. = ..()
- SSticker.mode.remove_cultist(src.mind, FALSE)
+ SSticker.mode.remove_cultist(mind, FALSE)
/mob/living/simple_animal/hostile/construct/examine(mob/user)
. = ..()
@@ -85,7 +84,6 @@
else if(src != M)
return ..()
-
/mob/living/simple_animal/hostile/construct/narsie_act()
return
@@ -122,7 +120,6 @@
playstyle_string = "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, \
create shield walls, rip apart enemies and walls alike, and even deflect energy weapons."
-
/mob/living/simple_animal/hostile/construct/armoured/hostile //actually hostile, will move around, hit things
AIStatus = AI_ON
environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP
@@ -160,7 +157,6 @@
melee_damage_lower = 25
melee_damage_upper = 25
attacktext = "slashes"
- see_in_dark = 8
attack_sound = 'sound/weapons/bladeslice.ogg'
const_type = "wraith"
construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift)
@@ -257,14 +253,14 @@
/mob/living/simple_animal/hostile/construct/behemoth
name = "Behemoth"
real_name = "Behemoth"
- desc = "The pinnacle of occult technology, Behemoths are the ultimate weapon in the Cult of Nar-Sie's arsenal."
+ desc = "The pinnacle of occult technology, Behemoths are the ultimate weapon in the Cult's arsenal."
icon = 'icons/mob/mob.dmi'
icon_state = "behemoth"
icon_living = "behemoth"
maxHealth = 750
health = 750
speak_emote = list("rumbles")
- response_harm = "harmlessly punches"
+ response_harm = "harmlessly punches"
harm_intent_damage = 0
melee_damage_lower = 50
melee_damage_upper = 50
@@ -291,7 +287,7 @@
/mob/living/simple_animal/hostile/construct/harvester
name = "Harvester"
real_name = "Harvester"
- desc = "A harbinger of Nar-Sie's enlightenment. It'll be all over soon."
+ desc = "A harbinger of enlightenment. It'll be all over soon."
icon = 'icons/mob/mob.dmi'
icon_state = "harvester"
icon_living = "harvester"
@@ -301,7 +297,6 @@
melee_damage_upper = 5
attacktext = "prods"
environment_smash = ENVIRONMENT_SMASH_RWALLS
- see_in_dark = 8
attack_sound = 'sound/weapons/tap.ogg'
const_type = "harvester"
construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision,
@@ -314,23 +309,13 @@
/mob/living/simple_animal/hostile/construct/harvester/Process_Spacemove(var/movement_dir = 0)
- return 1
+ return TRUE
/mob/living/simple_animal/hostile/construct/harvester/hostile //actually hostile, will move around, hit things
AIStatus = AI_ON
environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP
-////////////////Glow////////////////////
-/mob/living/simple_animal/hostile/construct/proc/updateglow()
- overlays = 0
- var/overlay_layer = LIGHTING_LAYER + 1
- if(layer != MOB_LAYER)
- overlay_layer=TURF_LAYER+0.2
-
- overlays += image(icon,"glow-[icon_state]",overlay_layer)
- set_light(2, -2, l_color = "#FFFFFF")
-
///ui stuff
/mob/living/simple_animal/hostile/construct/armoured/update_health_hud()
diff --git a/code/modules/mob/living/simple_animal/hostile/illusion.dm b/code/modules/mob/living/simple_animal/hostile/illusion.dm
index 3756fabf5229d..79c7e63a341bc 100644
--- a/code/modules/mob/living/simple_animal/hostile/illusion.dm
+++ b/code/modules/mob/living/simple_animal/hostile/illusion.dm
@@ -71,3 +71,10 @@
/mob/living/simple_animal/hostile/illusion/escape/AttackingTarget()
return
+
+///////Cult Illusions/////////
+/mob/living/simple_animal/hostile/illusion/cult
+ loot = list(/obj/effect/temp_visual/cult/sparks) // So that they SPARKLE on death
+
+/mob/living/simple_animal/hostile/illusion/escape/cult
+ loot = list(/obj/effect/temp_visual/cult/sparks)
diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm
index 1e3c723b278b9..2c99640b3ccb5 100644
--- a/code/modules/mob/living/simple_animal/shade.dm
+++ b/code/modules/mob/living/simple_animal/shade.dm
@@ -20,41 +20,42 @@
maxbodytemp = 4000
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
speed = -1
- stop_automated_movement = 1
+ stop_automated_movement = TRUE
status_flags = 0
+ pull_force = 0
+ universal_speak = TRUE
faction = list("cult")
status_flags = CANPUSH
flying = TRUE
loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm)
- del_on_death = 1
+ del_on_death = TRUE
deathmessage = "lets out a contented sigh as their form unwinds."
+ var/holy = FALSE
/mob/living/simple_animal/shade/death(gibbed)
. = ..()
- SSticker.mode.remove_cultist(src.mind, FALSE)
+ SSticker.mode.remove_cultist(mind, FALSE)
-/mob/living/simple_animal/shade/attackby(var/obj/item/O as obj, var/mob/user as mob) //Marker -Agouri
+/mob/living/simple_animal/shade/attackby(obj/item/O, mob/user) //Marker -Agouri
if(istype(O, /obj/item/soulstone))
- O.transfer_soul("SHADE", src, user)
+ var/obj/item/soulstone/SS = O
+ SS.transfer_soul("SHADE", src, user)
else
- if(O.force)
- var/damage = O.force
- if(O.damtype == STAMINA)
- damage = 0
- health -= damage
- user.visible_message("[src] has been attacked with the [O] by [user]. ")
- else
- user.visible_message("[user] gently taps [src] with the [O]. ", "This weapon is ineffective, it does no damage.")
- return
+ ..()
+
+/mob/living/simple_animal/shade/Process_Spacemove()
+ return TRUE
+
/mob/living/simple_animal/shade/cult/Initialize(mapload)
. = ..()
- name = SSticker.cultdat?.shade_name
- real_name = SSticker.cultdat?.shade_name
icon_state = SSticker.cultdat?.shade_icon_state
+/mob/living/simple_animal/shade/holy
+ holy = TRUE
+ icon_state = "shade_angelic"
+
/mob/living/simple_animal/shade/sword
- universal_speak = 1
faction = list("neutral")
/mob/living/simple_animal/shade/sword/Initialize(mapload)
diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm
index ce266ba5b8fb0..e23baf18489ad 100644
--- a/code/modules/mob/living/status_procs.dm
+++ b/code/modules/mob/living/status_procs.dm
@@ -371,10 +371,10 @@
// CULTSLURRING
/mob/living/CultSlur(amount)
- SetSlur(max(slurring, amount))
+ SetCultSlur(max(cultslurring, amount))
/mob/living/SetCultSlur(amount)
- slurring = max(amount, 0)
+ cultslurring = max(amount, 0)
/mob/living/AdjustCultSlur(amount, bound_lower = 0, bound_upper = INFINITY)
var/new_value = directional_bounded_sum(cultslurring, amount, bound_lower, bound_upper)
diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm
index bead9c935ef90..257d98fffc1e5 100644
--- a/code/modules/power/singularity/narsie.dm
+++ b/code/modules/power/singularity/narsie.dm
@@ -1,25 +1,28 @@
/obj/singularity/narsie //Moving narsie to a child object of the singularity so it can be made to function differently. --NEO
- name = "Nar-sie's Avatar"
+ name = "Nar'sie's Avatar"
desc = "Your mind begins to bubble and ooze as it tries to comprehend what it sees."
icon = 'icons/obj/magic_terror.dmi'
pixel_x = -89
pixel_y = -85
current_size = 9 //It moves/eats like a max-size singulo, aside from range. --NEO
- contained = 0 //Are we going to move around?
- dissipate = 0 //Do we lose energy over time?
- move_self = 1 //Do we move on our own?
- grav_pull = 5 //How many tiles out do we pull?
- consume_range = 6 //How many tiles out do we eat
+ contained = FALSE
+ dissipate = FALSE
+ move_self = TRUE
+ grav_pull = 5
+ consume_range = 6
gender = FEMALE
+/obj/singularity/narsie/admin_investigate_setup()
+ return
+
/obj/singularity/narsie/large
- name = "Nar-Sie"
+ name = "Nar'Sie"
icon = 'icons/obj/narsie.dmi'
// Pixel stuff centers Narsie.
pixel_x = -236
pixel_y = -256
current_size = 12
- move_self = 1 //Do we move on our own?
+ move_self = TRUE //Do we move on our own?
grav_pull = 10
consume_range = 12 //How many tiles out do we eat
@@ -27,22 +30,38 @@
..()
icon_state = SSticker.cultdat?.entity_icon_state
name = SSticker.cultdat?.entity_name
- to_chat(world, " [name] HAS RISEN")
- world << pick(sound('sound/hallucinations/im_here1.ogg'), sound('sound/hallucinations/im_here2.ogg'))
+ to_chat(world, " [uppertext(name)] HAS RISEN")
+ SEND_SOUND(world, pick('sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg'))
+
+ var/datum/game_mode/gamemode = SSticker.mode
+ if(gamemode)
+ gamemode.cult_objs.succesful_summon()
var/area/A = get_area(src)
if(A)
var/image/alert_overlay = image('icons/effects/cult_effects.dmi', "ghostalertsie")
- notify_ghosts("Nar-Sie has risen in \the [A.name]. Reach out to the Geometer to be given a new shell for your soul.", source = src, alert_overlay = alert_overlay, action=NOTIFY_ATTACK)
+ notify_ghosts("[name] has risen in \the [A.name]. Reach out to the Geometer to be given a new shell for your soul.", source = src, alert_overlay = alert_overlay, action = NOTIFY_ATTACK)
narsie_spawn_animation()
- sleep(70)
- SSshuttle.emergency.request(null, 0.3) // Cannot recall
- SSshuttle.emergency.canRecall = FALSE
+ sleep(7 SECONDS)
+ SSshuttle.emergency.request(null, 0.3)
+ SSshuttle.emergency.canRecall = FALSE // Cannot recall
+
+/obj/singularity/narsie/large/Destroy()
+ to_chat(world, " [uppertext(name)] HAS FALLEN")
+ SEND_SOUND(world, 'sound/hallucinations/wail.ogg')
+ var/datum/game_mode/gamemode = SSticker.mode
+ if(gamemode)
+ gamemode.cult_objs.narsie_death()
+ for(var/datum/mind/cult_mind in SSticker.mode.cult)
+ if(cult_mind && cult_mind.current)
+ to_chat(cult_mind.current, "RETRIBUTION!")
+ to_chat(cult_mind.current, "Current goal: Slaughter the heretics!")
+ ..()
-/obj/singularity/narsie/large/attack_ghost(mob/dead/observer/user as mob)
- makeNewConstruct(/mob/living/simple_animal/hostile/construct/harvester, user, null, 1)
+/obj/singularity/narsie/large/attack_ghost(mob/dead/observer/user)
+ make_new_construct(/mob/living/simple_animal/hostile/construct/harvester, user, cult_override = TRUE)
new /obj/effect/particle_effect/smoke/sleeping(user.loc)
@@ -63,17 +82,16 @@
godsmack(A)
return
-/obj/singularity/narsie/proc/godsmack(var/atom/A)
+/obj/singularity/narsie/proc/godsmack(atom/A)
if(istype(A,/obj/))
var/obj/O = A
- O.ex_act(1.0)
+ O.ex_act(1)
if(O) qdel(O)
else if(isturf(A))
var/turf/T = A
T.ChangeTurf(/turf/simulated/floor/engine/cult)
-
/obj/singularity/narsie/mezzer()
for(var/mob/living/carbon/M in oviewers(8, src))
if(M.stat == CONSCIOUS)
@@ -82,13 +100,14 @@
M.apply_effect(3, STUN)
-/obj/singularity/narsie/consume(var/atom/A)
+/obj/singularity/narsie/consume(atom/A)
A.narsie_act()
-
/obj/singularity/narsie/ex_act() //No throwing bombs at it either. --NEO
return
+/obj/singularity/narsie/singularity_act() //handled in /obj/singularity/proc/consume
+ return
/obj/singularity/narsie/proc/pickcultist() //Narsie rewards his cultists with being devoured first, then picks a ghost to follow. --NEO
var/list/cultists = list()
@@ -103,11 +122,11 @@
else
noncultists += food
- if(cultists.len) //cultists get higher priority
+ if(length(cultists)) //cultists get higher priority
acquire(pick(cultists))
return
- if(noncultists.len)
+ if(length(noncultists))
acquire(pick(noncultists))
return
@@ -119,12 +138,12 @@
if(pos.z != src.z)
continue
cultists += ghost
- if(cultists.len)
+ if(length(cultists))
acquire(pick(cultists))
return
-/obj/singularity/narsie/proc/acquire(var/mob/food)
+/obj/singularity/narsie/proc/acquire(mob/food)
if(food == target)
return
if(!target)
@@ -150,8 +169,8 @@
/obj/singularity/narsie/proc/narsie_spawn_animation()
icon = 'icons/obj/narsie_spawn_anim.dmi'
dir = SOUTH
- move_self = 0
+ move_self = FALSE
flick(SSticker.cultdat?.entity_spawn_animation, src)
sleep(11)
- move_self = 1
+ move_self = TRUE
icon = initial(icon)
diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm
index 78c623adacf70..d5e511af0090d 100644
--- a/code/modules/power/singularity/singularity.dm
+++ b/code/modules/power/singularity/singularity.dm
@@ -280,6 +280,15 @@
name = "supermatter-charged [initial(name)]"
consumedSupermatter = 1
set_light(10)
+ if(istype(A, /obj/singularity/narsie))
+ if(current_size == STAGE_SIX)
+ visible_message("[SSticker.cultdat?.entity_name] is consumed by [src]!")
+ qdel(A)
+ else
+ visible_message("[SSticker.cultdat?.entity_name] strikes down [src]!")
+ investigate_log("has been destroyed by Nar'Sie","singulo")
+ qdel(src)
+
return
diff --git a/code/modules/projectiles/ammunition/special.dm b/code/modules/projectiles/ammunition/special.dm
index f37690e278744..3a05a5cbf381d 100644
--- a/code/modules/projectiles/ammunition/special.dm
+++ b/code/modules/projectiles/ammunition/special.dm
@@ -42,6 +42,9 @@
projectile_type = pick(typesof(/obj/item/projectile/magic))
..()
+/obj/item/ammo_casing/magic/arcane_barrage
+ projectile_type = /obj/item/projectile/magic/arcane_barrage
+
/obj/item/ammo_casing/magic/forcebolt
projectile_type = /obj/item/projectile/forcebolt
diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm
index cafe9f4cb21f1..55f6a757a5e8e 100644
--- a/code/modules/projectiles/guns/projectile/shotgun.dm
+++ b/code/modules/projectiles/guns/projectile/shotgun.dm
@@ -258,19 +258,40 @@
..()
guns_left = 0
+/obj/item/gun/projectile/shotgun/boltaction/enchanted/attack_self()
+ return
+
/obj/item/gun/projectile/shotgun/boltaction/enchanted/shoot_live_shot(mob/living/user, atom/target, pointblank = FALSE, message = TRUE)
..()
if(guns_left)
- var/obj/item/gun/projectile/shotgun/boltaction/enchanted/GUN = new
+ var/obj/item/gun/projectile/shotgun/boltaction/enchanted/GUN = new type
GUN.guns_left = guns_left - 1
- user.drop_item()
+ discard_gun(user)
user.swap_hand()
+ user.drop_item()
user.put_in_hands(GUN)
else
- user.drop_item()
- spawn(0)
- throw_at(pick(oview(7,get_turf(user))),1,1)
+ discard_gun(user)
+
+/obj/item/gun/projectile/shotgun/boltaction/enchanted/proc/discard_gun(mob/living/user)
user.visible_message("[user] tosses aside the spent rifle!")
+ user.throw_item(pick(oview(7, get_turf(user))))
+
+/obj/item/gun/projectile/shotgun/boltaction/enchanted/arcane_barrage
+ name = "arcane barrage"
+ desc = "Pew Pew Pew."
+ fire_sound = 'sound/weapons/emitter.ogg'
+ icon_state = "arcane_barrage"
+ item_state = "arcane_barrage"
+ slot_flags = null
+ flags = NOBLUDGEON | DROPDEL | ABSTRACT
+ mag_type = /obj/item/ammo_box/magazine/internal/boltaction/enchanted/arcane_barrage
+
+/obj/item/gun/projectile/shotgun/boltaction/enchanted/arcane_barrage/examine(mob/user)
+ . = desc // Override since magical hand lasers don't have chambers or bolts
+
+/obj/item/gun/projectile/shotgun/boltaction/enchanted/arcane_barrage/discard_gun(mob/living/user)
+ qdel(src)
// Automatic Shotguns//
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
index 080114554bb08..b29b6109d1c73 100644
--- a/code/modules/projectiles/projectile/magic.dm
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -346,3 +346,13 @@
M.Weaken(slip_weaken)
M.Stun(slip_stun)
. = ..()
+
+/obj/item/projectile/magic/arcane_barrage
+ name = "arcane bolt"
+ icon_state = "arcane_barrage"
+ damage = 20
+ damage_type = BURN
+ nodamage = FALSE
+ armour_penetration = 0
+ flag = "magic"
+ hitsound = 'sound/weapons/barragespellhit.ogg'
diff --git a/code/modules/reagents/chemistry/reagents/water.dm b/code/modules/reagents/chemistry/reagents/water.dm
index 861c48c7e0e1d..bd6efd5a5d786 100644
--- a/code/modules/reagents/chemistry/reagents/water.dm
+++ b/code/modules/reagents/chemistry/reagents/water.dm
@@ -241,9 +241,14 @@
if(current_cycle >= 30) // 12 units, 60 seconds @ metabolism 0.4 units & tick rate 2.0 sec
M.AdjustStuttering(4, bound_lower = 0, bound_upper = 20)
M.Dizzy(5)
- if(iscultist(M) && prob(5))
- M.AdjustCultSlur(5)//5 seems like a good number...
- M.say(pick("Av'te Nar'sie","Pa'lid Mors","INO INO ORA ANA","SAT ANA!","Daim'niodeis Arc'iai Le'eones","Egkau'haom'nai en Chaous","Ho Diak'nos tou Ap'iron","R'ge Na'sie","Diabo us Vo'iscum","Si gn'um Co'nu"))
+ if(iscultist(M))
+ for(var/datum/action/innate/cult/blood_magic/BM in M.actions)
+ for(var/datum/action/innate/cult/blood_spell/BS in BM.spells)
+ to_chat(M, "Your blood rites falter as holy water scours your body!")
+ qdel(BS)
+ if(prob(5))
+ M.AdjustCultSlur(5)//5 seems like a good number...
+ M.say(pick("Av'te Nar'sie","Pa'lid Mors","INO INO ORA ANA","SAT ANA!","Daim'niodeis Arc'iai Le'eones","Egkau'haom'nai en Chaous","Ho Diak'nos tou Ap'iron","R'ge Na'sie","Diabo us Vo'iscum","Si gn'um Co'nu"))
if(current_cycle >= 75 && prob(33)) // 30 units, 150 seconds
M.AdjustConfused(3)
if(isvampirethrall(M))
@@ -259,6 +264,11 @@
M.SetJitter(0)
M.SetStuttering(0)
M.SetConfused(0)
+ if(ishuman(M)) // Unequip all cult clothing
+ var/mob/living/carbon/human/H = M
+ for(var/I in H.contents - (H.bodyparts | H.internal_organs)) // Satanic liver NYI
+ if(is_type_in_list(I, CULT_CLOTHING))
+ H.unEquip(I)
return
if(ishuman(M) && M.mind && M.mind.vampire && !M.mind.vampire.get_ability(/datum/vampire_passive/full) && prob(80))
var/mob/living/carbon/V = M
diff --git a/icons/effects/32x64.dmi b/icons/effects/32x64.dmi
new file mode 100644
index 0000000000000..4d9f9911944c2
Binary files /dev/null and b/icons/effects/32x64.dmi differ
diff --git a/icons/effects/96x96.dmi b/icons/effects/96x96.dmi
index 95e9747f87820..124471265df78 100644
Binary files a/icons/effects/96x96.dmi and b/icons/effects/96x96.dmi differ
diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi
index 00d52a742dad6..a6afd27cc86cf 100644
Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ
diff --git a/icons/effects/crayondecal.dmi b/icons/effects/crayondecal.dmi
index 48388c18fd403..36d68514d44a0 100644
Binary files a/icons/effects/crayondecal.dmi and b/icons/effects/crayondecal.dmi differ
diff --git a/icons/effects/cult_effects.dmi b/icons/effects/cult_effects.dmi
index 3307e27d24907..cbab2398b3cd1 100644
Binary files a/icons/effects/cult_effects.dmi and b/icons/effects/cult_effects.dmi differ
diff --git a/icons/effects/cult_target.dmi b/icons/effects/cult_target.dmi
new file mode 100644
index 0000000000000..650feb3361343
Binary files /dev/null and b/icons/effects/cult_target.dmi differ
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index bedd145d30810..6079921620188 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/mob/actions/actions.dmi b/icons/mob/actions/actions.dmi
index e270095cbd6fa..e0ef6c5ba1877 100644
Binary files a/icons/mob/actions/actions.dmi and b/icons/mob/actions/actions.dmi differ
diff --git a/icons/mob/actions/actions_cult.dmi b/icons/mob/actions/actions_cult.dmi
new file mode 100644
index 0000000000000..0e5320a54edcf
Binary files /dev/null and b/icons/mob/actions/actions_cult.dmi differ
diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi
index 07593d09c0c60..eb62ce8138097 100644
Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.dmi differ
diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi
index 240558ebb80f1..f8195652dca2b 100644
Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ
diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi
index a3743efa33b4c..5520ac724b327 100644
Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ
diff --git a/icons/mob/mob.dmi b/icons/mob/mob.dmi
index ce6a3d96012d2..79a05bd3b8846 100644
Binary files a/icons/mob/mob.dmi and b/icons/mob/mob.dmi differ
diff --git a/icons/mob/species/skrell/held.dmi b/icons/mob/species/skrell/held.dmi
new file mode 100644
index 0000000000000..10680d5cba925
Binary files /dev/null and b/icons/mob/species/skrell/held.dmi differ
diff --git a/icons/obj/cult.dmi b/icons/obj/cult.dmi
index f592d142e87e5..7b29b64865d12 100644
Binary files a/icons/obj/cult.dmi and b/icons/obj/cult.dmi differ
diff --git a/icons/obj/guns/projectile.dmi b/icons/obj/guns/projectile.dmi
index d3566b2a65904..b1efd604cb5d9 100644
Binary files a/icons/obj/guns/projectile.dmi and b/icons/obj/guns/projectile.dmi differ
diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi
index 0aa0a1a1f009f..104d730143424 100644
Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ
diff --git a/icons/obj/rune.dmi b/icons/obj/rune.dmi
index 5df6a842ad528..c098b7d8cc26e 100644
Binary files a/icons/obj/rune.dmi and b/icons/obj/rune.dmi differ
diff --git a/icons/obj/wizard.dmi b/icons/obj/wizard.dmi
index 83a80d2f40805..01fe0b5e82cce 100644
Binary files a/icons/obj/wizard.dmi and b/icons/obj/wizard.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 537874351b276..f5a81db43629e 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -33,6 +33,7 @@
#include "code\__DEFINES\contracts.dm"
#include "code\__DEFINES\crafting.dm"
#include "code\__DEFINES\criminal_status.dm"
+#include "code\__DEFINES\cult.dm"
#include "code\__DEFINES\dna.dm"
#include "code\__DEFINES\error_handler.dm"
#include "code\__DEFINES\flags.dm"
@@ -540,15 +541,15 @@
#include "code\game\gamemodes\changeling\powers\swap_form.dm"
#include "code\game\gamemodes\changeling\powers\tiny_prick.dm"
#include "code\game\gamemodes\changeling\powers\transform.dm"
+#include "code\game\gamemodes\cult\blood_magic.dm"
#include "code\game\gamemodes\cult\cult.dm"
-#include "code\game\gamemodes\cult\cult_comms.dm"
+#include "code\game\gamemodes\cult\cult_actions.dm"
#include "code\game\gamemodes\cult\cult_datums.dm"
#include "code\game\gamemodes\cult\cult_items.dm"
#include "code\game\gamemodes\cult\cult_objectives.dm"
#include "code\game\gamemodes\cult\cult_structures.dm"
#include "code\game\gamemodes\cult\ritual.dm"
#include "code\game\gamemodes\cult\runes.dm"
-#include "code\game\gamemodes\cult\talisman.dm"
#include "code\game\gamemodes\devil\devil.dm"
#include "code\game\gamemodes\devil\devil_game_mode.dm"
#include "code\game\gamemodes\devil\devilinfo.dm"
diff --git a/sound/magic/cult_spell.ogg b/sound/magic/cult_spell.ogg
new file mode 100644
index 0000000000000..c107743d805dc
Binary files /dev/null and b/sound/magic/cult_spell.ogg differ
diff --git a/sound/weapons/barragespellhit.ogg b/sound/weapons/barragespellhit.ogg
new file mode 100644
index 0000000000000..a5e859a2aac33
Binary files /dev/null and b/sound/weapons/barragespellhit.ogg differ
diff --git a/sound/weapons/parry.ogg b/sound/weapons/parry.ogg
new file mode 100644
index 0000000000000..ff60f3c8b8fea
Binary files /dev/null and b/sound/weapons/parry.ogg differ
diff --git a/strings/tips.txt b/strings/tips.txt
index d28943913d3e8..c382968ef0029 100644
--- a/strings/tips.txt
+++ b/strings/tips.txt
@@ -153,8 +153,7 @@ As a Changeling, the Extract DNA sting counts for your genome absorb objective,
As a Changeling, you can absorb someone by strangling them and using the Absorb verb; this gives you the ability to rechoose your powers, the DNA of whoever you absorbed, the memory of the absorbed, and some samples of things the absorbed said.
As a Cultist, do not cause too much chaos before your objective is completed. If the shuttle gets called too soon, you may not have enough time to win.
As a Cultist, your team starts off very weak, but if necessary can quickly convert everything they have into raw power. Make sure you have the numbers and equipment to support going loud, or the cult will fall flat on its face.
-As a Cultist, the Blood Boil rune will deal massive amounts of brute damage to non-cultists, and some damage to fellow cultists nearby, but will create a fire where the rune stands on use.
-As a Cultist, you can create an army of manifested goons using a combination of the Manifest rune, which creates homunculi from ghosts, and the Blood Drain rune, which drains life from anyone standing on any blood drain rune.
+As a Cultist, the Blood Boil rune will deal massive amounts of burn damage to non-cultists and set them on fire. However, two cultists have to be standing at the rune for its duration.
You can deconvert Cultists by feeding them large amounts of holy water.
The Chaplain can bless any container with water by hitting it with their bible. Holy water has a myriad of uses against cults and large amounts of it are a great contributor to success against them.
As a Wizard, you can turn people to stone, then animate the resulting statue with a staff of animation to create an extremely powerful minion, for all of 5 minutes at least.