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