diff --git a/code/WorkInProgress/computer3/computers/camera.dm b/code/WorkInProgress/computer3/computers/camera.dm
index 17e639166d5..d5f1218151c 100644
--- a/code/WorkInProgress/computer3/computers/camera.dm
+++ b/code/WorkInProgress/computer3/computers/camera.dm
@@ -142,7 +142,7 @@
return null
var/list/L = list()
- for(var/obj/machinery/camera/C in cameranet.cameras)
+ for(var/obj/machinery/camera/C in cameranet.viewpoints)
var/list/temp = C.network & key.networks
if(temp.len)
L.Add(C)
diff --git a/code/datums/visibility_networks/dictionary.dm b/code/datums/visibility_networks/dictionary.dm
index 5f57ddd7a17..0ce800a8c04 100644
--- a/code/datums/visibility_networks/dictionary.dm
+++ b/code/datums/visibility_networks/dictionary.dm
@@ -1,6 +1,7 @@
var/datum/visibility_network/cameras/cameranet = new()
-var/datum/visibility_network/cult/cultNetwork = new()
-var/datum/visibility_network/list/visibility_networks = list("ALL_CAMERAS"=cameranet, "CULT" = cultNetwork)
+//var/datum/visibility_network/cult/cultNetwork = new()
+var/datum/visibility_network/list/visibility_networks = list("ALL_CAMERAS"=cameranet)
+//var/datum/visibility_network/list/visibility_networks = list("ALL_CAMERAS"=cameranet, "CULT" = cultNetwork)
// used by turfs and objects to update all visibility networks
diff --git a/code/datums/visibility_networks/update_triggers.dm b/code/datums/visibility_networks/update_triggers.dm
index f1835eeeb5a..9a366e7d642 100644
--- a/code/datums/visibility_networks/update_triggers.dm
+++ b/code/datums/visibility_networks/update_triggers.dm
@@ -57,18 +57,18 @@
for(var/turf/simulated/turf in locs)
update_heat_protection(turf)
- air_master.AddTurfToUpdate(turf)
+ air_master.mark_for_update(turf)
return 1
-
+
#define UPDATE_VISIBILITY_NETWORK_BUFFER 30
/mob
var/datum/visibility_network/list/visibilityNetworks=list()
var/updatingVisibilityNetworks=FALSE
-
+
/mob/Move(n,direct)
var/oldLoc = src.loc
. = ..()
@@ -86,9 +86,9 @@
/mob/proc/addToVisibilityNetwork(var/datum/visibility_network/network)
if(network)
src.visibilityNetworks+=network
-
+
/mob/proc/removeFromVisibilityNetwork(var/datum/visibility_network/network)
if(network)
src.visibilityNetworks|=network
-
+
#undef UPDATE_VISIBILITY_NETWORK_BUFFER
\ No newline at end of file
diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm
index 1db2ba3bee4..1eb60d3a301 100644
--- a/code/game/gamemodes/malfunction/Malf_Modules.dm
+++ b/code/game/gamemodes/malfunction/Malf_Modules.dm
@@ -149,11 +149,12 @@ rcd light flash thingy on matter drain
var/alert_msg = "There isn't enough room. Make sure you are placing the machine in a clear area and on a floor."
+/*
var/datum/camerachunk/C = cameranet.getCameraChunk(middle.x, middle.y, middle.z)
if(!C.visibleTurfs[middle])
alert(src, "We cannot get camera vision of this location.")
return
-
+*/
for(var/T in turfs)
// Make sure the turfs are clear and the correct type.
@@ -225,7 +226,7 @@ rcd light flash thingy on matter drain
set name = "Reactivate Camera"
set category = "Malfunction"
if (istype (C, /obj/machinery/camera))
- for(var/datum/AI_Module/small/reactivate_camera/camera in current_modules)
+ for(var/datum/AI_Module/small/reactivate_camera/camera in usr:current_modules)
if(camera.uses > 0)
if(!C.status)
C.deactivate(src)
@@ -248,7 +249,7 @@ rcd light flash thingy on matter drain
set name = "Upgrade Camera"
set category = "Malfunction"
if(istype(C))
- var/datum/AI_Module/small/upgrade_camera/UC = locate(/datum/AI_Module/small/upgrade_camera) in current_modules
+ var/datum/AI_Module/small/upgrade_camera/UC = locate(/datum/AI_Module/small/upgrade_camera) in usr:current_modules
if(UC)
if(UC.uses > 0)
if(C.assembly)
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 766aa7d663e..ef294831b38 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -263,16 +263,6 @@
/obj/machinery/door/proc/requiresID()
return 1
-/obj/machinery/door/proc/update_nearby_tiles(need_rebuild)
- if(!air_master)
- return 0
-
- for(var/turf/simulated/turf in locs)
- update_heat_protection(turf)
- air_master.mark_for_update(turf)
-
- return 1
-
/obj/machinery/door/proc/update_heat_protection(var/turf/simulated/source)
if(istype(source))
diff --git a/code/game/objects/items/devices/handtv.dm b/code/game/objects/items/devices/handtv.dm
index 48e22348f32..0406db85852 100644
--- a/code/game/objects/items/devices/handtv.dm
+++ b/code/game/objects/items/devices/handtv.dm
@@ -7,7 +7,7 @@
/obj/item/device/handtv/attack_self(mob/usr as mob)
var/list/cameras = new/list()
- for (var/obj/machinery/camera/C in cameranet.cameras)
+ for (var/obj/machinery/camera/C in cameranet.viewpoints)
if (C.hasbug && C.status)
cameras.Add(C)
if (length(cameras) == 0)
diff --git a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
index 31e4cae6a61..b0caf3b4a69 100644
--- a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
@@ -11,4 +11,15 @@
var/obj/machinery/camera/c = viewpoint
if (!c)
return FALSE
- return c.can_use()
\ No newline at end of file
+ return c.can_use()
+
+
+// adding some indirection so that I don't have to edit a ton of files
+/datum/visibility_network/cameras/proc/addCamera(var/camera)
+ return addViewpoint(camera)
+
+/datum/visibility_network/cameras/proc/removeCamera(var/camera)
+ return removeViewpoint(camera)
+
+/datum/visibility_network/cameras/proc/checkCameraVis(var/atom/target)
+ return checkCanSee(target)
diff --git a/code/modules/mob/spirit/cultnet.dm b/code/modules/mob/spirit/cultnet.dm
new file mode 100644
index 00000000000..df0cd57ff5d
--- /dev/null
+++ b/code/modules/mob/spirit/cultnet.dm
@@ -0,0 +1,80 @@
+/*
+
+This file contains the code necessary to do the display code for cult spirits.
+
+It reuses a lot of code from the AIEye cameraNetwork. In order to work properly, some of those files needed to be modified as well.
+
+*/
+
+
+/proc/isCultRune(var/viewpoint)
+ var/obj/effect/rune/test_rune = viewpoint
+ if (test_rune)
+ return TRUE
+ return FALSE
+
+
+/proc/isCultViewpoint(var/viewpoint)
+ var/obj/cult_viewpoint/vp = viewpoint
+ if (vp)
+ return TRUE
+ return FALSE
+
+
+/datum/visibility_chunk/cult/validViewpoint(var/atom/viewpoint)
+ var/turf/point = locate(src.x + 8, src.y + 8, src.z)
+ if(get_dist(point, viewpoint) > 24)
+ return FALSE
+
+ if (isCultRune(viewpoint) || isCultViewpoint(viewpoint))
+ return viewpoint:can_use()
+ return FALSE
+
+
+/datum/visibility_chunk/cult/getVisibleTurfsForViewpoint(var/viewpoint)
+ var/obj/effect/rune/rune = viewpoint
+ if (rune)
+ return rune.can_see()
+ var/obj/cult_viewpoint/cvp = viewpoint
+ if (cvp)
+ return cvp.can_see()
+ return null
+
+
+/datum/visibility_chunk/cult/findNearbyViewpoints()
+ for(var/obj/cult_viewpoint/vp in range(16, locate(x + 8, y + 8, z)))
+ if(vp.can_use())
+ viewpoints += vp
+ for(var/obj/effect/rune/rune in range(16, locate(x + 8, y + 8, z)))
+ viewpoints += rune
+
+
+/datum/visibility_network/cult
+ ChunkType = /datum/visibility_chunk/cult
+
+
+/datum/visibility_network/cult/validViewpoint(var/viewpoint)
+ if (isCultRune(viewpoint) || isCultViewpoint(viewpoint))
+ return viewpoint:can_use()
+ return FALSE
+
+/datum/visibility_network/cult/getViewpointFromMob(var/mob/currentMob)
+ for(var/obj/cult_viewpoint/currentView in currentMob)
+ return currentView
+ return FALSE
+
+
+/datum/visibility_interface/cult
+ chunk_type = /datum/visibility_chunk/cult
+
+
+/*
+RUNE JUNK
+*/
+
+/obj/effect/rune/proc/can_use()
+ return TRUE
+
+/obj/effect/rune/proc/can_see()
+ return hear(view_range, get_turf(src))
+
diff --git a/code/modules/mob/spirit/mask/mask.dm b/code/modules/mob/spirit/mask/mask.dm
new file mode 100644
index 00000000000..6b58f201cf2
--- /dev/null
+++ b/code/modules/mob/spirit/mask/mask.dm
@@ -0,0 +1,227 @@
+/mob/spirit/mask
+ icon = 'icons/mob/spirits/mask.dmi'
+ icon_state = "depressurized"
+
+/mob/spirit/mask/New()
+ ..()
+ spell_list += new /obj/effect/proc_holder/spell/aoe_turf/conjure/create_talisman(src)
+ spell_list += new /obj/effect/proc_holder/spell/aoe_turf/blood_speech(src)
+ spell_list += new /obj/effect/proc_holder/spell/aoe_turf/shatter_lights(src)
+
+
+/mob/spirit/mask/verb/go_to_follower()
+ set category = "Mask"
+ set name = "Go to follower"
+ set desc = "Select who you would like to go too."
+
+ var/obj/cult_viewpoint/cultist = pick_cultist()
+ if (cultist)
+ follow_cultist(cultist.owner)
+ cult_log("[key_name_admin(src)] started following [key_name_admin(cultist)].")
+ src << "You start following [cultist.get_display_name()]."
+
+
+/mob/spirit/mask/verb/urge_cultist()
+ set category = "Mask"
+ set name = "Urge cultist"
+ set desc = "Push your cultists to do something."
+
+ var/obj/cult_viewpoint/cultist = pick_cultist()
+ if (cultist)
+ if (cultist.owner)
+ var/newUrge = stripped_input(usr, "", "Set Urge", "")
+ cultist.set_urge(newUrge)
+ src << "You urge [cultist.owner.name] to [newUrge]."
+ cult_log("controlled by [key_name_admin(src)] has urged [key_name_admin(cultist.owner)] to [newUrge].")
+
+/mob/spirit/mask/verb/set_cult_name()
+ set category = "Mask"
+ set name = "Set Cult Name"
+ set desc = "Grant a cultist a name."
+
+ var/obj/cult_viewpoint/cultist = pick_cultist()
+ if (cultist)
+ var/newName = stripped_input(usr, "", "Set Cult Name", "")
+ if (!newName)
+ return
+ cultist.set_cult_name(newName)
+ src << "You grant [cultist.owner.name] the secret name of [newName]."
+ if (cultist.owner)
+ cult_log("[key_name_admin(src)] has set [key_name_admin(cultist.owner)] to \'[newName]\'")
+
+
+/mob/spirit/mask/verb/urge_cult()
+ set category = "Mask"
+ set name = "Urge Cult"
+ set desc = "Set urge on the entire cult."
+
+ var/newUrge = stripped_input(usr, "Please choose an urge.", "Set Urge", "")
+ for(var/obj/cult_viewpoint/viewpoint in cult_viewpoints)
+ viewpoint.set_urge(newUrge)
+ src << "You urge the entire cult to [newUrge]."
+ cult_log("[key_name_admin(src)] has urged the entire cult to [newUrge]")
+
+
+/mob/spirit/mask/verb/set_favor_for_cultist()
+ set category = "Mask"
+ set name = "Show your favor"
+ set desc = "Set the favor for a cultist"
+
+ var/obj/cult_viewpoint/cultist = pick_cultist()
+ if (cultist)
+ if (cultist.owner)
+ var/list/favor = list("Pleased", "Displeased", "Indifference")
+ var/emotion = input("Pick your emotion", "Mask", null, null) in favor
+ switch(emotion)
+ if("Pleased")
+ cultist.set_favor(1)
+ cult_log("[key_name_admin(src)] is pleased with [key_name_admin(cultist.owner)]")
+ if("Displeased")
+ cultist.set_favor(-1)
+ cult_log("[key_name_admin(src)] is displeased with [key_name_admin(cultist.owner)]")
+ if("Indifference")
+ cultist.set_favor(0)
+ cult_log("[key_name_admin(src)] is indifferent too [key_name_admin(cultist.owner)]")
+
+
+/mob/spirit/mask/proc/set_name()
+ spawn(0)
+ var/newName = stripped_input(src, "Please pick a name.", "Pick Name for Mask", "")
+ name = newName ? newName : "Mask of Nar'sie"
+ src << "You have set your name to [name]."
+
+
+/mob/spirit/mask/proc/pick_cultist()
+ var/list/cultists = list()
+ for(var/obj/cult_viewpoint/viewpoint in cult_viewpoints)
+ cultists[viewpoint.get_display_name()]=viewpoint
+ var/input = input("Please, select a cultist!", "Cult", null, null) as null|anything in cultists
+ var/obj/cult_viewpoint/result = cultists[input]
+ return result
+
+
+// this proc makes the mask visible very briefly
+/mob/spirit/mask/proc/flicker()
+ spawn(0)
+ alpha = 127
+ invisibility=0
+ sleep(5)
+ invisibility=initial(invisibility)
+ alpha = 255
+
+/proc/flicker_mask(mob/spirit/mask/target)
+ if(istype(target))
+ target.flicker()
+
+// SPELLS
+/obj/effect/proc_holder/spell/aoe_turf/blood_speech
+ name = "Speak to your Acolytes"
+ desc = "This spell allows you to speak to your flock."
+ school = "unknown evil"
+ charge_type = "recharge"
+ charge_max = 2000
+ clothes_req = 0
+ invocation = "none"
+ invocation_type = "none"
+ range = 0
+
+/obj/effect/proc_holder/spell/aoe_turf/blood_speech/cast(list/targets)
+ var/input = stripped_input(usr, "Please choose a message to tell your acolytes.", "Voice of Blood", "")
+ if(!input)
+ revert_cast(usr)
+ cult_log("[key_name_admin(usr)]says : [input]")
+ flicker_mask(usr)
+ for(var/datum/mind/H in ticker.mode.cult)
+ if (H.current)
+ H.current << "[usr.name]: [input]"
+ for(var/mob/spirit/spirit in spirits)
+ spirit << "[usr.name]: [input]"
+
+
+/obj/effect/proc_holder/spell/aoe_turf/shatter_lights
+ name = "Spread Shadows"
+ desc = "This spell breaks lights near the mask."
+ school = "unknown evil"
+ charge_type = "recharge"
+ charge_max = 1000
+ clothes_req = 0
+ invocation = "none"
+ invocation_type = "none"
+ range = 0
+
+/obj/effect/proc_holder/spell/aoe_turf/shatter_lights/cast(list/targets)
+ cult_log("[key_name_admin(usr)] used Spread Shadows.")
+ flicker_mask(usr)
+ spawn(0)
+ for(var/area/A in range(3,get_turf(usr)))
+ for(var/obj/machinery/light/L in A)
+ L.on = 1
+ L.broken()
+ sleep(1)
+ for(var/obj/item/device/flashlight/F in A)
+ F.on = 0
+
+
+/obj/effect/proc_holder/spell/aoe_turf/conjure/create_talisman
+ name = "Create Talisman"
+ desc = "This spell conjures a talisman"
+
+ school = "conjuration"
+ charge_type = "recharge"
+ charge_max = 3000
+ clothes_req = 0
+ invocation = "none"
+ invocation_type = "none"
+ range = 0
+ summon_type = list(/obj/item/weapon/paper/talisman)
+
+ var/list/talismans = list( "Armor"="armor",
+ "Blind"="blind",
+ "Conceal"="conceal",
+ "Communicate"="communicate",
+ "Deafen"="deafen",
+ "EMP"="emp",
+ "Teleport"="teleport",
+ "Tome"="newtome",
+ "Reveal Runes",
+ "Stun"="runestun",
+ "Soul Stone"="soulstone",
+ "Construct"="construct")
+
+
+/obj/effect/proc_holder/spell/aoe_turf/conjure/create_talisman/cast(list/targets)
+
+ var/talisman = input("Pick a talisman type", "Talisman", null, null) as null|anything in talismans
+ var/imbue_value = talismans[talisman]
+ if (!talisman)
+ usr << "You choose not to create a talisman."
+ revert_cast(usr)
+ return
+
+ cult_log("[key_name_admin(usr,0)] created a talisman of type [talisman].")
+ flicker_mask(usr)
+
+ switch(talisman)
+
+ if ("Teleport")
+ var/target_rune = input("Pick a teleport target", "Teleport Rune", null, null) as null|anything in engwords
+ if (!target_rune)
+ usr << "You choose not to create a talisman."
+ revert_cast(usr)
+ return
+ summon_type = list(/obj/item/weapon/paper/talisman)
+ newVars = list("imbue" = "[target_rune]", "info" = "[target_rune]")
+
+ if ("Soul Stone")
+ summon_type = list(/obj/item/device/soulstone)
+ newVars = list()
+
+ if ("Construct")
+ summon_type = list(/obj/structure/constructshell)
+ newVars = list()
+
+ else
+ summon_type = list(/obj/item/weapon/paper/talisman)
+ newVars = list("imbue" = "[imbue_value]")
+
+ ..()
\ No newline at end of file
diff --git a/code/modules/mob/spirit/mask/respawn.dm b/code/modules/mob/spirit/mask/respawn.dm
new file mode 100644
index 00000000000..acfb69073c2
--- /dev/null
+++ b/code/modules/mob/spirit/mask/respawn.dm
@@ -0,0 +1,20 @@
+/proc/there_can_be_only_one_mask(var/mob/spirit/mask/target)
+ if(!istype(target))
+ return
+ for(var/mob/spirit/mask/currentSpirit in spirits)
+ if(currentSpirit)
+ if(currentSpirit!=target)
+ // create the ghost
+ var/mob/dead/observer/ghost = currentSpirit.ghostize(TRUE)
+ // let the deposed mask respawn immediately, the poor dear
+ ghost.timeofdeath = world.time - 20000
+ ghost.newPlayerType = /mob/new_player/cultist
+ // remove old mask body
+ del(currentSpirit)
+
+
+/mob/new_player/cultist/AttemptLateSpawn(rank)
+ var/mob/newCharacter = ..(rank)
+ if(ticker.mode)
+ if(is_convertable_to_cult(newCharacter.mind))
+ ticker.mode.add_cultist(newCharacter.mind)
\ No newline at end of file
diff --git a/code/modules/mob/spirit/movement.dm b/code/modules/mob/spirit/movement.dm
new file mode 100644
index 00000000000..d8847cb480a
--- /dev/null
+++ b/code/modules/mob/spirit/movement.dm
@@ -0,0 +1,62 @@
+// spirits are not moved by airflow
+mob/spirit/Move()
+ return 0
+
+// this is the main move proc for spirits, it uses their 'setLoc' function to handle all the visibility shenanigans
+// this, like most movement code for these guys, is cribbed from the AIEye movement code
+mob/spirit/proc/Spirit_Move(direct)
+
+ var/initial = initial(sprint)
+ var/max_sprint = 50
+
+ // if we haven't moved in a while, we stop sprinting
+ if(cooldown && cooldown < world.timeofday) // 3 seconds
+ sprint = initial
+
+ for(var/i = 0; i < max(sprint, initial); i += 20)
+ var/turf/step = get_turf(get_step(src, direct))
+ if(step)
+ setLoc(step)
+
+ dir = direct // update our sprite
+
+ cooldown = world.timeofday + 5
+ if(acceleration)
+ sprint = min(sprint + 0.5, max_sprint)
+ else
+ sprint = initial
+
+ // if we're trying to move, we want to stop following our target
+ follow_target = null
+
+
+/mob/spirit/proc/follow_cultist(mob/living/target as mob)
+ if(!istype(target)) return
+ var/obj/cult_viewpoint/currentView = getCultViewpoint(target)
+ var/mob/spirit/U = usr
+
+ if (!currentView)
+ U << "As a spirit, you may only track cultists."
+
+ U.follow_target = target
+ U << "Now following [currentView.get_cult_name()]."
+
+ spawn (0)
+ while (U.follow_target == target)
+ if (U.follow_target == null)
+ return
+ U.setLoc(get_turf(target))
+ sleep(10)
+
+
+mob/spirit/proc/setLoc(var/T)
+ T = get_turf(T)
+ loc = T
+ cultNetwork.visibility(src)
+
+mob/spirit/verb/toggle_acceleration()
+ set category = "Spirit"
+ set name = "Toggle Acceleration"
+
+ acceleration = !acceleration
+ usr << "Acceleration has been toggled [acceleration ? "on" : "off"]."
\ No newline at end of file
diff --git a/code/modules/mob/spirit/spirit.dm b/code/modules/mob/spirit/spirit.dm
new file mode 100644
index 00000000000..ce85f4175f0
--- /dev/null
+++ b/code/modules/mob/spirit/spirit.dm
@@ -0,0 +1,76 @@
+/*
+This mob type is used for entities that exist within the Cult's spirit world. They share the same visibility network and are intangible.
+*/
+
+mob/spirit
+ name = "spirit"
+ desc = "A spirit"
+ icon = 'icons/mob/mob.dmi'
+ icon_state = "ghost"
+ layer = 4
+ stat = CONSCIOUS
+ status_flags = GODMODE // spirits cannot be killed
+ density = 0
+ canmove = 0
+ blinded = 0
+ anchored = 1
+ mouse_opacity = 0
+ invisibility = INVISIBILITY_SPIRIT
+ universal_speak = 1
+
+ // pseudo-movement values
+ var/sprint = 10
+ var/cooldown = 0
+ var/acceleration = 1
+ var/follow_target = null
+
+
+mob/spirit/is_active()
+ if (client && client.inactivity <= 10 * 60 * 10)
+ return TRUE
+ return FALSE
+
+
+mob/spirit/New()
+ sight |= SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF
+ see_invisible = SEE_SPIRITS
+ see_in_dark = 100
+
+ loc = pick(latejoin)
+
+ // hook them to the cult visibility network
+ visibility_interface = new /datum/visibility_interface/cult(src)
+
+ // no nameless spirits
+ if (!name)
+ name = "Boogyman"
+
+ spirits+=src
+
+ ..()
+
+mob/spirit/Del()
+ spirits-=src
+ ..()
+
+
+mob/spirit/Topic(href, href_list)
+ world << "Spirit Topic!"
+ usr << "Topic: usr [usr], src [src]"
+ src << "Topic: usr [usr], src [src]"
+
+ if(usr != src)
+ return
+ ..()
+
+ usr << "Spirit Href = [href]"
+ for (var/tempref in href_list)
+ usr << "Spirit href list [tempref] = [href_list[tempref]]"
+
+ if (href_list["track"])
+ usr << "Got to tracking."
+ var/mob/target = locate(href_list["track"]) in mob_list
+ var/mob/spirit/A = locate(href_list["track2"]) in spirits
+ if(A && target)
+ A.follow_cultist(target)
+ return
\ No newline at end of file
diff --git a/code/modules/mob/spirit/viewpoint.dm b/code/modules/mob/spirit/viewpoint.dm
new file mode 100644
index 00000000000..5e063561297
--- /dev/null
+++ b/code/modules/mob/spirit/viewpoint.dm
@@ -0,0 +1,202 @@
+#define FAVOR_PLEASED 1
+#define FAVOR_INDIFFERENT 0
+#define FAVOR_DISPLEASED -1
+
+
+var/obj/cult_viewpoint/list/cult_viewpoints = list()
+
+
+/obj/cult_viewpoint
+ var/view_range = 7
+ var/updating = 0
+ var/mob/owner = null
+ var/urge = ""
+ var/favor = FAVOR_INDIFFERENT
+ var/cult_name = null
+
+
+/obj/cult_viewpoint/New(var/mob/target)
+ owner = target
+ //src.loc = owner
+ owner.addToVisibilityNetwork(cultNetwork)
+ cultNetwork.viewpoints+=src
+ cultNetwork.addViewpoint(src)
+ cult_viewpoints+=src
+ //handle_missing_mask()
+ ..()
+
+
+/obj/cult_viewpoint/Del()
+ processing_objects.Remove(src)
+ cultNetwork.viewpoints-=src
+ cultNetwork.removeViewpoint(src)
+ cult_viewpoints-=src
+ owner.removeFromVisibilityNetwork(cultNetwork)
+ ..()
+ return
+
+
+// VERBS
+/obj/cult_viewpoint/verb/check_urge()
+ set category = "Cult"
+ set desc = "Discover what your god commands of you."
+ set name = "Check Urge"
+ set src in usr
+ if (src.urge)
+ owner << "\red \b You feel the urge to [src.urge]"
+ else
+ owner << "\b You feel no supernatural compulsions."
+
+
+/obj/cult_viewpoint/verb/reach_out()
+ set category = "Cult"
+ set desc = "Reach out for your gods presence."
+ set name = "Reach Out"
+ set src in usr
+
+ for(var/mob/spirit/mask/currentMask in spirits)
+ if (currentMask.is_active())
+ owner << "\red \b You feel the reassuring presence of your god."
+ currentMask << "[get_display_name()] has reached out to you."
+ return
+ owner << "\b You feel a chilling absence."
+ handle_missing_mask()
+
+
+/obj/cult_viewpoint/verb/check_favor()
+ set category = "Cult"
+ set desc = "Check your favor with your god."
+ set name = "Check Favor"
+ set src in usr
+ switch(favor)
+ if(FAVOR_PLEASED)
+ owner << "\red \b You bask in your gods favor."
+ if(FAVOR_INDIFFERENT)
+ owner << "\red \b You feel nothing."
+ if(FAVOR_DISPLEASED)
+ owner << "\red \b You cringe at your gods displeasure."
+
+
+/obj/cult_viewpoint/verb/pray_to_mask()
+ set category = "Cult"
+ set desc = "Pray to your god"
+ set name = "Pray to Nar'Sie"
+ set src in usr
+
+ var/input = stripped_input(usr, "Please choose a message to say to your god.", "Pray to Nar'Sie", "")
+ if(!input)
+ return
+
+ cult_log("[key_name(usr,0)](Pray):[input]")
+ owner << "You pray to Nar'Sie: [input]"
+
+ for(var/mob/spirit/spirit in spirits)
+ spirit << "[get_display_name()] prays : [input]"
+
+// PROCS
+/obj/cult_viewpoint/proc/set_favor(var/newFavor)
+ favor = newFavor
+ check_favor()
+
+
+/obj/cult_viewpoint/proc/set_urge(var/newUrge)
+ if (!newUrge)
+ src.urge = null
+ src.urge = copytext(newUrge, 1, MAX_MESSAGE_LEN)
+ check_urge()
+
+
+/obj/cult_viewpoint/proc/can_use()
+ if (owner.stat != DEAD)
+ return TRUE
+ return FALSE
+
+
+/obj/cult_viewpoint/proc/can_see()
+ return hear(view_range, get_turf(owner))
+
+
+/obj/cult_viewpoint/proc/get_cult_name()
+ if (cult_name)
+ return cult_name
+ return "An Unknown Servent"
+
+
+/obj/cult_viewpoint/proc/set_cult_name(var/newName)
+ if (!owner)
+ return FALSE
+ if (newName)
+ cult_name = newName
+ owner << "\red \b You have been blessed with the secret name of '[newName]'."
+ else
+ cult_name = null
+ owner << "\red \b Your god has taken your secret name."
+
+
+/obj/cult_viewpoint/proc/get_display_name()
+ if (!owner)
+ return
+ if (cult_name)
+ return cult_name
+ return owner.name
+
+
+/obj/cult_viewpoint/proc/become_mask()
+ set category = "Cult"
+ set name = "Become Mask"
+ set desc = "Sacrifice your life and become a Mask of Nar'sie."
+ set src in usr
+
+ cult_log("[key_name(usr,0)] has tried to become a Mask of Nar'sie.")
+
+ if (!active_mask())
+ var/transformation_type = alert(owner.client, "You are about to become a Mask. Do you want it to be subtle or violent?", "Mask", "Subtle", "Violent")
+ if(!active_mask())
+ cult_log("[key_name(usr,0)] has become a Mask of Nar'sie.")
+ if (transformation_type=="Subtle")
+ log_admin("[key_name_admin(owner)] has subtly become a Mask of Nar'sie")
+ owner.make_into_mask(0,0)
+ else
+ log_admin("[key_name_admin(owner)] has violently become a Mask of Nar'sie")
+ owner.make_into_mask(1,1)
+ else
+ owner << "\b You cannot become a mask of Nar'Sie because a Mask already exists."
+ mask_has_been_found()
+ return
+
+
+/obj/cult_viewpoint/proc/active_mask()
+ for(var/mob/spirit/mask/currentMask in spirits)
+ if (currentMask.is_active())
+ return TRUE
+ return FALSE
+
+
+/obj/cult_viewpoint/proc/handle_missing_mask()
+ if (active_mask())
+ mask_has_been_found()
+ else
+ mask_is_missing()
+
+
+/obj/cult_viewpoint/proc/mask_has_been_found()
+ for(var/obj/cult_viewpoint/viewpoint in cult_viewpoints)
+ if (viewpoint.verbs.Find(/obj/cult_viewpoint/proc/become_mask))
+ viewpoint.verbs-=/obj/cult_viewpoint/proc/become_mask
+
+
+/obj/cult_viewpoint/proc/mask_is_missing()
+ for(var/obj/cult_viewpoint/viewpoint in cult_viewpoints)
+ if (!viewpoint.verbs.Find(/obj/cult_viewpoint/proc/become_mask))
+ viewpoint.verbs+=/obj/cult_viewpoint/proc/become_mask
+
+
+/proc/getCultViewpoint(var/mob/currentMob)
+ for(var/obj/cult_viewpoint/currentView in currentMob)
+ return currentView
+ return FALSE
+
+
+#undef FAVOR_PLEASED
+#undef FAVOR_INDIFFERENT
+#undef FAVOR_DISPLEASED
\ No newline at end of file