diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index d510078414b..4cb444fb21f 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -1,6 +1,3 @@
-///Time before being allowed to select a new cult leader again
-#define CULT_POLL_WAIT (240 SECONDS)
-
/// Returns either the error landmark or the location of the room. Needless to say, if this is used, it means things have gone awry.
#define GET_ERROR_ROOM ((locate(/obj/effect/landmark/error) in GLOB.landmarks_list) || locate(4,4,1))
diff --git a/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm
index 862b3307b4f..f560b07e149 100644
--- a/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm
@@ -364,6 +364,11 @@ GLOBAL_VAR_INIT(revolutionary_win, FALSE)
M.add_antag_datum(new_cultist)
GLOB.pre_setup_antags -= M
main_cult.setup_objectives()
+ var/datum/mind/most_experienced = get_most_experienced(assigned, antag_flag)
+ if(!most_experienced)
+ most_experienced = assigned[1]
+ var/datum/antagonist/cult/leader = most_experienced.has_antag_datum(/datum/antagonist/cult)
+ leader.make_cult_leader()
return TRUE
/datum/dynamic_ruleset/roundstart/bloodcult/round_result()
diff --git a/code/modules/antagonists/cult/cult_comms.dm b/code/modules/antagonists/cult/cult_comms.dm
index 2dd0191d0ee..cf81acfcf10 100644
--- a/code/modules/antagonists/cult/cult_comms.dm
+++ b/code/modules/antagonists/cult/cult_comms.dm
@@ -54,7 +54,7 @@
var/span = "cult italic"
var/datum/antagonist/cult/cult_datum = user.mind.has_antag_datum(/datum/antagonist/cult)
if(cult_datum.is_cult_leader())
- span = "cultlarge"
+ span = "cult_large"
title = "Master"
else if(!ishuman(user))
title = "Construct"
@@ -89,115 +89,6 @@
var/link = FOLLOW_LINK(player_list, user)
to_chat(player_list, "[link] [my_message]")
-/datum/action/innate/cult/mastervote
- name = "Assert Leadership"
- button_icon_state = "cultvote"
- // So you can use it while your hands are cuffed or you are bucked
- // If you want to assert your leadership while handcuffed to a chair, be my guest
- check_flags = AB_CHECK_INCAPACITATED|AB_CHECK_CONSCIOUS|AB_CHECK_HANDS_BLOCKED
-
-/datum/action/innate/cult/mastervote/IsAvailable(feedback = FALSE)
- if(!owner || !owner.mind)
- return FALSE
- var/datum/antagonist/cult/mind_cult_datum = owner.mind.has_antag_datum(/datum/antagonist/cult)
- if(!mind_cult_datum || mind_cult_datum.cult_team.cult_leader_datum || mind_cult_datum.cult_team.cult_vote_called || !ishuman(owner))
- return FALSE
- return ..()
-
-/datum/action/innate/cult/mastervote/Activate()
- var/choice = tgui_alert(owner, "The mantle of leadership is heavy. Success in this role requires an expert level of communication and experience. Are you sure?",, list("Yes", "No"))
- if(choice == "Yes" && IsAvailable())
- var/datum/antagonist/cult/mind_cult_datum = owner.mind.has_antag_datum(/datum/antagonist/cult)
- start_poll_cultists_for_leader(owner, mind_cult_datum.cult_team)
-
-///Start the poll for Cult Leaeder.
-/proc/start_poll_cultists_for_leader(mob/living/nominee, datum/team/cult/team)
- if(world.time < CULT_POLL_WAIT)
- to_chat(nominee, "It would be premature to select a leader while everyone is still settling in, try again in [DisplayTimeText(CULT_POLL_WAIT-world.time)].")
- return
- team.cult_vote_called = TRUE
- for(var/datum/mind/team_member as anything in team.members)
- if(!team_member.current)
- continue
- team_member.current.update_mob_action_buttons()
- if(team_member.current.incapacitated)
- continue
- SEND_SOUND(team_member.current, 'sound/effects/hallucinations/im_here1.ogg')
- to_chat(team_member.current, span_cult_large("Acolyte [nominee] has asserted that [nominee.p_theyre()] worthy of leading the cult. A vote will be called shortly."))
-
- addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(poll_cultists_for_leader), nominee, team), 10 SECONDS)
-
-///Polls all Cultists on whether the person putting themselves forward should be made the Cult Leader, if they can actually be such.
-/proc/poll_cultists_for_leader(mob/living/nominee, datum/team/cult/team)
- if(QDELETED(nominee) || nominee.incapacitated)
- team.cult_vote_called = FALSE
- for(var/datum/mind/team_member as anything in team.members)
- if(!team_member.current)
- continue
- team_member.current.update_mob_action_buttons()
- if(team_member.current.incapacitated)
- continue
- to_chat(team_member.current,span_cult_large("[nominee] has died in the process of attempting to start a vote!"))
- return FALSE
- var/list/mob/living/asked_cultists = list()
- for(var/datum/mind/team_member as anything in team.members)
- if(!team_member.current || team_member.current == nominee || team_member.current.incapacitated)
- continue
- SEND_SOUND(team_member.current, 'sound/effects/magic/exit_blood.ogg')
- asked_cultists += team_member.current
-
- var/list/yes_voters = SSpolling.poll_candidates(
- question = "[span_notice(nominee.name)] seeks to lead your cult, do you support [nominee.p_them()]?",
- poll_time = 30 SECONDS,
- group = asked_cultists,
- alert_pic = nominee,
- role_name_text = "cult master nomination",
- custom_response_messages = list(
- POLL_RESPONSE_SIGNUP = "You have pledged your allegience to [nominee].",
- POLL_RESPONSE_ALREADY_SIGNED = "You have already pledged your allegience!",
- POLL_RESPONSE_NOT_SIGNED = "You aren't nominated for this.",
- POLL_RESPONSE_TOO_LATE_TO_UNREGISTER = "It's too late to unregister yourself, voting has already begun!",
- POLL_RESPONSE_UNREGISTERED = "You have been removed your pledge to [nominee].",
- chat_text_border_icon = mutable_appearance('icons/effects/effects.dmi', "cult_master_logo")
- )
- )
- if(QDELETED(nominee) || nominee.incapacitated)
- team.cult_vote_called = FALSE
- for(var/datum/mind/team_member as anything in team.members)
- if(!team_member.current)
- continue
- team_member.current.update_mob_action_buttons()
- if(team_member.current.incapacitated)
- continue
- to_chat(team_member.current,span_cult_large("[nominee] has died in the process of attempting to win the cult's support!"))
- return FALSE
- if(!nominee.mind)
- team.cult_vote_called = FALSE
- for(var/datum/mind/team_member as anything in team.members)
- if(!team_member.current)
- continue
- team_member.current.update_mob_action_buttons()
- if(team_member.current.incapacitated)
- continue
- to_chat(team_member.current,span_cult_large("[nominee] has gone catatonic in the process of attempting to win the cult's support!"))
- return FALSE
- if(LAZYLEN(yes_voters) <= LAZYLEN(asked_cultists) * 0.5)
- team.cult_vote_called = FALSE
- for(var/datum/mind/team_member as anything in team.members)
- if(!team_member.current)
- continue
- team_member.current.update_mob_action_buttons()
- if(team_member.current.incapacitated)
- continue
- to_chat(team_member.current, span_cult_large("[nominee] could not win the cult's support and shall continue to serve as an acolyte."))
- return FALSE
-
- team.cult_vote_called = FALSE
- var/datum/antagonist/cult/cult_datum = nominee.mind.has_antag_datum(/datum/antagonist/cult)
- if(!cult_datum.make_cult_leader())
- CRASH("[cult_datum.owner.current] was supposed to turn into the leader, but they didn't for some reason. This isn't supposed to happen unless an Admin messed with it.")
- return TRUE
-
/datum/action/innate/cult/master/IsAvailable(feedback = FALSE)
if(!owner.mind || GLOB.cult_narsie)
return FALSE
@@ -206,6 +97,63 @@
return FALSE
return ..()
+/datum/action/innate/cult/master/pass_role
+ name = "Pass the Mantle"
+ desc = "Pass the Master role onto another willing cultist. This can only be done once!"
+ button_icon_state = "cultvote"
+ check_flags = AB_CHECK_INCAPACITATED|AB_CHECK_CONSCIOUS|AB_CHECK_HANDS_BLOCKED
+
+/datum/action/innate/cult/master/pass_role/IsAvailable(feedback = FALSE)
+ . = ..()
+ if (!.)
+ return
+ var/datum/antagonist/cult/mind_cult_datum = owner.mind.has_antag_datum(/datum/antagonist/cult)
+ if(!mind_cult_datum || mind_cult_datum.cult_team.leader_passed_on)
+ return FALSE
+
+/datum/action/innate/cult/master/pass_role/Activate()
+ var/list/choices = list()
+ var/datum/antagonist/cult/owner_datum = owner.mind.has_antag_datum(/datum/antagonist/cult)
+ for(var/datum/mind/team_member as anything in owner_datum.cult_team.members)
+ if(!team_member.current || !ishuman(team_member.current) || team_member.current == owner || team_member.current.stat == DEAD)
+ continue
+
+ choices[team_member.current.real_name] = team_member
+
+ var/new_master = tgui_input_list(owner, "Select another cult member to pass the cult's Master role onto.", "Pass the Mantle", choices)
+ if (!new_master || !IsAvailable())
+ return
+
+ var/confirmation = tgui_alert(owner, "Are you sure that you want to make [new_master] the new Master? This can only be done once!", "Pass the Mantle", list("Yes", "No"))
+ if (confirmation != "Yes" || !IsAvailable())
+ return
+
+ var/datum/mind/master_mind = choices[new_master]
+ var/mob/living/carbon/human/master = master_mind.current
+ if (!master || master.stat == DEAD || !master.client)
+ to_chat(owner, span_cult("[new_master] can no longer take on the role of a Master."))
+ return
+
+ var/datum/antagonist/cult/target_datum = master_mind.has_antag_datum(/datum/antagonist/cult)
+ if(!target_datum || owner_datum.cult_team != target_datum?.cult_team)
+ to_chat(owner, span_cult("[new_master] can no longer take on the role of a Master."))
+ return
+
+ SEND_SOUND(master, sound('sound/effects/magic.ogg', volume = 33))
+ confirmation = tgui_alert(master, "[owner.real_name] is offering their role as the cult's Master to you! Do you wish to accept it?", "Take the Mantle", list("Yes", "No"))
+
+ if (confirmation != "Yes")
+ to_chat(owner, span_cult("[new_master] has declined your offer."))
+ return
+
+ if (!IsAvailable() || !master_mind.has_antag_datum(/datum/antagonist/cult) || !master.client)
+ to_chat(owner, span_cult("[new_master] can no longer take on the role of a Master."))
+ return
+
+ target_datum.cult_team.leader_passed_on = TRUE
+ owner_datum.demote_from_leader()
+ target_datum.make_cult_leader()
+
/datum/action/innate/cult/master/finalreck
name = "Final Reckoning"
desc = "A single-use spell that brings the entire cult to the master's location."
@@ -438,8 +386,7 @@
/datum/action/innate/cult/master/pulse/do_ability(mob/living/clicker, atom/clicked_on)
var/atom/throwee = throwee_ref?.resolve()
-
- if(QDELETED(throwee))
+ if(throwee && QDELING(throwee))
to_chat(clicker, span_cult("You lost your target!"))
throwee = null
throwee_ref = null
@@ -483,19 +430,17 @@
return TRUE
- else
- if(isliving(clicked_on))
- var/mob/living/living_clicked = clicked_on
- if(!IS_CULTIST(living_clicked))
- return FALSE
- SEND_SOUND(clicker, sound('sound/items/weapons/thudswoosh.ogg'))
- to_chat(clicker, span_cult_bold("You reach through the veil with your mind's eye and seize [clicked_on]! Click anywhere nearby to teleport [clicked_on.p_them()]!"))
- throwee_ref = WEAKREF(clicked_on)
- return TRUE
-
- if(istype(clicked_on, /obj/structure/destructible/cult))
- to_chat(clicker, span_cult_bold("You reach through the veil with your mind's eye and lift [clicked_on]! Click anywhere nearby to teleport it!"))
- throwee_ref = WEAKREF(clicked_on)
- return TRUE
+ if(isliving(clicked_on))
+ var/mob/living/living_clicked = clicked_on
+ if(!IS_CULTIST(living_clicked))
+ return FALSE
+ SEND_SOUND(clicker, sound('sound/items/weapons/thudswoosh.ogg'))
+ to_chat(clicker, span_cult_bold("You reach through the veil with your mind's eye and seize [clicked_on]! Click anywhere nearby to teleport [clicked_on.p_them()]!"))
+ throwee_ref = WEAKREF(clicked_on)
+ return TRUE
+ if(istype(clicked_on, /obj/structure/destructible/cult))
+ to_chat(clicker, span_cult_bold("You reach through the veil with your mind's eye and lift [clicked_on]! Click anywhere nearby to teleport it!"))
+ throwee_ref = WEAKREF(clicked_on)
+ return TRUE
return FALSE
diff --git a/code/modules/antagonists/cult/datums/cult_team.dm b/code/modules/antagonists/cult/datums/cult_team.dm
index 8606a6f82d9..130683a9046 100644
--- a/code/modules/antagonists/cult/datums/cult_team.dm
+++ b/code/modules/antagonists/cult/datums/cult_team.dm
@@ -8,8 +8,8 @@
///Timer for the blood mark expiration
var/blood_target_reset_timer
- ///Has a vote been called for a leader?
- var/cult_vote_called = FALSE
+ ///Has the cult leader passed on their responsibilities to someone else?
+ var/leader_passed_on = FALSE
///The cult leader
var/datum/antagonist/cult/cult_leader_datum
///Has the mass teleport been used yet?
@@ -161,12 +161,15 @@
for(var/datum/mind/cultist as anything in members)
if(!cultist.current)
continue
+
if(cultist.current.stat == DEAD || !cultist.current.client)
continue
to_chat(cultist.current, span_bold(span_cult_large("[marker] has marked [blood_target] in \the [target_area] as the cult's top priority, get there immediately!")))
SEND_SOUND(cultist.current, sound(SFX_HALLUCINATION_OVER_HERE, 0, 1, 75))
cultist.current.client.images += blood_target_image
+ if (cultist.current.hud_used)
+ new /atom/movable/screen/navigate_arrow(null, cultist.current.hud_used, get_turf(new_target), COLOR_CULT_RED)
if(duration != INFINITY)
blood_target_reset_timer = addtimer(CALLBACK(src, PROC_REF(unset_blood_target)), duration, TIMER_STOPPABLE)
diff --git a/code/modules/antagonists/cult/datums/cultist.dm b/code/modules/antagonists/cult/datums/cultist.dm
index 433c9ebedbe..eaa5150808c 100644
--- a/code/modules/antagonists/cult/datums/cultist.dm
+++ b/code/modules/antagonists/cult/datums/cultist.dm
@@ -9,9 +9,6 @@
antag_hud_name = "cult"
stinger_sound = 'sound/music/antag/bloodcult/bloodcult_gain.ogg'
- ///The vote ability Cultists have to elect someone to be the leader.
- var/datum/action/innate/cult/mastervote/vote_ability
-
///Boolean on whether the starting equipment should be given to their inventory.
var/give_equipment = FALSE
///Reference to the Blood cult team they are part of.
@@ -35,9 +32,6 @@
var/datum/action/innate/cult/comm/communion = new(owner)
communion.Grant(current)
- if(isnull(cult_team.cult_leader_datum))
- vote_ability = new(owner)
- vote_ability.Grant(current)
if(ishuman(current))
var/datum/action/innate/cult/blood_magic/magic = new(owner)
magic.Grant(current)
@@ -50,8 +44,6 @@
to_chat(owner.current, span_userdanger("An unfamiliar white light flashes through your mind, cleansing the taint of the Geometer and all your memories as her servant."))
owner.current.log_message("has renounced the cult of Nar'Sie!", LOG_ATTACK, color=COLOR_CULT_RED)
- if(vote_ability)
- QDEL_NULL(vote_ability)
for(var/datum/action/innate/cult/cult_buttons in owner.current.actions)
qdel(cult_buttons)
@@ -185,8 +177,8 @@
/datum/antagonist/cult/proc/make_cult_leader()
if(cult_team.cult_leader_datum)
return FALSE
- cult_team.cult_leader_datum = src
+ cult_team.cult_leader_datum = src
antag_hud_name = "cultmaster"
add_team_hud(owner.current)
RegisterSignal(owner.current, COMSIG_MOB_STATCHANGE, PROC_REF(deathrattle))
@@ -198,13 +190,15 @@
var/datum/action/innate/cult/master/pulse/throwing = new
bloodmark.Grant(owner.current)
throwing.Grant(owner.current)
+ if(!cult_team.leader_passed_on)
+ var/datum/action/innate/cult/master/pass_role/pass_role = new
+ pass_role.Grant(owner.current)
owner.current.update_mob_action_buttons()
for(var/datum/mind/cult_mind as anything in cult_team.members)
- var/datum/antagonist/cult/cult_datum = cult_mind.has_antag_datum(/datum/antagonist/cult)
- cult_datum.vote_ability.Remove(cult_mind.current)
- to_chat(cult_mind.current, span_cult_large("[owner.current] has won the cult's support and is now their master. \
- Follow [owner.current.p_their()] orders to the best of your ability!"))
+ if (cult_mind != owner)
+ to_chat(cult_mind.current, span_cult_large("[owner.current] is your cult's Master! \
+ Follow [owner.current.p_their()] orders to the best of your ability!"))
to_chat(owner.current, span_cult_large("You are the cult's Master. \
As the cult's Master, you have a unique title and loud voice when communicating, are capable of marking \
@@ -215,7 +209,6 @@
return TRUE
///Admin-only helper to demote someone from Cult leader, taking away their HUD, abilities, and deathrattle
-///And gives all cultists from their team back their ability to vote for a new leader.
/datum/antagonist/cult/proc/demote_from_leader()
if(!cult_team.cult_leader_datum)
return FALSE
@@ -234,13 +227,11 @@
var/datum/action/innate/cult/master/pulse/throwing = locate() in owner.current.actions
if(throwing)
throwing.Remove(owner.current)
+ var/datum/action/innate/cult/master/pass_role/pass_role = locate() in owner.current.actions
+ if(pass_role)
+ pass_role.Remove(owner.current)
owner.current.update_mob_action_buttons()
- for(var/datum/mind/cult_mind as anything in cult_team.members)
- var/datum/antagonist/cult/cult_datum = cult_mind.has_antag_datum(/datum/antagonist/cult)
- cult_datum.vote_ability.Grant(cult_mind.current)
-
- to_chat(owner.current, span_cult_large("You have been demoted from being the cult's Master, you are now an acolyte once more!"))
-
+ to_chat(owner.current, span_cult_large("You have been demoted from being the cult's Master, you are now a mere acolyte!"))
return TRUE
///If dead (and Narsie isn't summoned), will alert all Cultists of their death, sending their location out.
diff --git a/code/modules/antagonists/heretic/heretic_living_heart.dm b/code/modules/antagonists/heretic/heretic_living_heart.dm
index a79831d9dc6..443ea0603fd 100644
--- a/code/modules/antagonists/heretic/heretic_living_heart.dm
+++ b/code/modules/antagonists/heretic/heretic_living_heart.dm
@@ -180,26 +180,6 @@
StartCooldown()
return TRUE
-/datum/action/cooldown/track_target/proc/make_navigate_arrow(turf/tracked_turf, arrow_color)
- var/datum/hud/user_hud = owner.hud_used
- if(!user_hud)
- return
- var/atom/movable/screen/heretic_arrow/arrow = new /atom/movable/screen/heretic_arrow(null, user_hud)
- animate(arrow, transform = matrix(dir2angle(get_dir(owner, tracked_turf)), MATRIX_ROTATE), 0.2 SECONDS)
- arrow.screen_loc = around_player
- arrow.color = arrow_color
- user_hud.infodisplay += arrow
- user_hud.show_hud(user_hud.hud_version)
- addtimer(CALLBACK(src, PROC_REF(end_effect), user_hud, arrow), 1.6 SECONDS)
-
-/datum/action/cooldown/track_target/proc/end_effect(datum/hud/user_hud, atom/movable/screen/heretic_arrow/arrow)
- arrow.icon_state = "heretic_arrow_disappear"
- addtimer(CALLBACK(src, PROC_REF(null_arrow), user_hud, arrow), 0.4 SECONDS)
-
-/datum/action/cooldown/track_target/proc/null_arrow(datum/hud/user_hud, atom/movable/screen/heretic_arrow/arrow)
- user_hud.infodisplay -= arrow
- user_hud.show_hud(user_hud.hud_version)
-
/// Callback for the radial to ensure it's closed when not allowed.
/datum/action/cooldown/track_target/proc/check_menu()
if(QDELETED(src))
@@ -268,7 +248,8 @@
balloon_message = "very far!"
arrow_color = COLOR_RED
- make_navigate_arrow(their_turf, arrow_color)
+ if(owner.hud_used)
+ new /atom/movable/screen/navigate_arrow(null, owner.hud_used, their_turf, arrow_color)
if(ismob(tracked_thing))
var/mob/tracked_mob = tracked_thing
@@ -277,9 +258,32 @@
return balloon_message
-/atom/movable/screen/heretic_arrow
+/atom/movable/screen/navigate_arrow
icon = 'icons/effects/96x96.dmi'
- name = "heretic arrow"
- icon_state = "heretic_arrow_appear"
+ name = "farsight arrow"
+ icon_state = "navigate_arrow_appear"
pixel_x = -32
pixel_y = -32
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+
+/atom/movable/screen/navigate_arrow/Initialize(mapload, datum/hud/hud_owner, turf/tracked_turf, arrow_color)
+ . = ..()
+ var/mob/owner = get_mob()
+ if (owner)
+ animate(src, transform = matrix(get_angle(owner, tracked_turf), MATRIX_ROTATE), 0.2 SECONDS)
+ screen_loc = around_player
+ color = arrow_color
+ if (hud)
+ hud.infodisplay += src
+ hud.show_hud(hud.hud_version)
+ addtimer(CALLBACK(src, PROC_REF(end_effect)), 1.6 SECONDS)
+
+/atom/movable/screen/navigate_arrow/proc/end_effect()
+ icon_state = "navigate_arrow_disappear"
+ addtimer(CALLBACK(src, PROC_REF(null_arrow)), 0.4 SECONDS)
+
+/atom/movable/screen/navigate_arrow/proc/null_arrow()
+ if (hud)
+ hud.infodisplay -= src
+ hud.show_hud(hud.hud_version)
+ qdel(src)
diff --git a/icons/effects/96x96.dmi b/icons/effects/96x96.dmi
index 9942d0a5398..ed4a5b64d3c 100644
Binary files a/icons/effects/96x96.dmi and b/icons/effects/96x96.dmi differ