mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-18 11:36:24 +01:00
Makes cult leader handling work off of the Cult datum (#76556)
## About The Pull Request Removes Cult master's datum, it's not handled by the Cultist itself, using a helper to promote/demote people to leader. In practice, the only way someone would be demoted is through Admins, so this adds support for Admins to intervene in this Cult stuff if necessary. Moves cult objectives and cult team to their own files Removes the cult master's status effect that constantly processes to send a deathrattle, and instead moves it to a signal hooked to stat change. Also moves some things from ``get_antag_minds`` to checking the team, which doesn't change anything in-game but it does help add the currently non-functional support for several cult teams. Iunno. https://github.com/tgstation/tgstation/assets/53777086/573a4f13-35e1-4f34-9952-62fed10b49c9 ## Why It's Good For The Game Having the cult leader be its own datum has actually been handled like shit. To promote someone to cult leader, we currently make their current cult datum silent, then remove it, and finally add the cult leader datum. This means they lose their spells unless manually given back post-promotion, which sucks (and also, no one has done yet, meaning they just lose all their spells). It also means there's a lot more snowflake things, did you know there's a var to bypass converting mindshielded people? That's so cult masters can be promoted by Cultists who were mindshielded, and they have to be "ownable", that var is to bypass the check for mindshield to "convert" them to leader cultist. ## Changelog 🆑 fix: Cultists promoted to Leader no longer lose their spells (rip whoever tried saving up blood rites) admin: Admins can now force promote/demote people from Cult Leader if necessary. /🆑
This commit is contained in:
@@ -155,13 +155,13 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/proc/can_be_owned(datum/mind/new_owner)
|
||||
. = TRUE
|
||||
var/datum/mind/tested = new_owner || owner
|
||||
if(tested.has_antag_datum(type))
|
||||
return FALSE
|
||||
for(var/datum/antagonist/badguy as anything in tested.antag_datums)
|
||||
if(is_type_in_typecache(src, badguy.typecache_datum_blacklist))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
//This will be called in add_antag_datum before owner assignment.
|
||||
//Should return antag datum without owner.
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
#define SUMMON_POSSIBILITIES 3
|
||||
#define CULT_VICTORY 1
|
||||
#define CULT_LOSS 0
|
||||
#define CULT_NARSIE_KILLED -1
|
||||
|
||||
/datum/antagonist/cult
|
||||
name = "Cultist"
|
||||
roundend_category = "cultists"
|
||||
@@ -10,47 +5,21 @@
|
||||
antag_moodlet = /datum/mood_event/cult
|
||||
suicide_cry = "FOR NAR'SIE!!"
|
||||
preview_outfit = /datum/outfit/cultist
|
||||
var/datum/action/innate/cult/comm/communion = new
|
||||
var/datum/action/innate/cult/mastervote/vote = new
|
||||
var/datum/action/innate/cult/blood_magic/magic = new
|
||||
job_rank = ROLE_CULTIST
|
||||
antag_hud_name = "cult"
|
||||
var/ignore_implant = FALSE
|
||||
|
||||
///The vote ability Cultists have to elect someone to be the leader.
|
||||
var/datum/action/innate/cult/mastervote/vote_ability = new
|
||||
|
||||
///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.
|
||||
var/datum/team/cult/cult_team
|
||||
|
||||
|
||||
/datum/antagonist/cult/get_team()
|
||||
return cult_team
|
||||
|
||||
/datum/antagonist/cult/create_team(datum/team/cult/new_team)
|
||||
if(!new_team)
|
||||
//todo remove this and allow admin buttons to create more than one cult
|
||||
for(var/datum/antagonist/cult/H in GLOB.antagonists)
|
||||
if(!H.owner)
|
||||
continue
|
||||
if(H.cult_team)
|
||||
cult_team = H.cult_team
|
||||
return
|
||||
cult_team = new /datum/team/cult
|
||||
cult_team.setup_objectives()
|
||||
return
|
||||
if(!istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
cult_team = new_team
|
||||
|
||||
/datum/antagonist/cult/proc/add_objectives()
|
||||
objectives |= cult_team.objectives
|
||||
|
||||
/datum/antagonist/cult/Destroy()
|
||||
QDEL_NULL(communion)
|
||||
QDEL_NULL(vote)
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/cult/can_be_owned(datum/mind/new_owner)
|
||||
. = ..()
|
||||
if(. && !ignore_implant)
|
||||
. = is_convertable_to_cult(new_owner.current,cult_team)
|
||||
if(!is_convertable_to_cult(new_owner.current, cult_team))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/cult/greet()
|
||||
. = ..()
|
||||
@@ -58,7 +27,7 @@
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/cult/on_gain()
|
||||
add_objectives()
|
||||
objectives |= cult_team.objectives
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
if(give_equipment)
|
||||
@@ -81,67 +50,21 @@
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/cult/get_preview_icon()
|
||||
var/icon/icon = render_preview_outfit(preview_outfit)
|
||||
|
||||
// The longsword is 64x64, but getFlatIcon crunches to 32x32.
|
||||
// So I'm just going to add it in post, screw it.
|
||||
|
||||
// Center the dude, because item icon states start from the center.
|
||||
// This makes the image 64x64.
|
||||
icon.Crop(-15, -15, 48, 48)
|
||||
|
||||
var/obj/item/melee/cultblade/longsword = new
|
||||
icon.Blend(icon(longsword.lefthand_file, longsword.inhand_icon_state), ICON_OVERLAY)
|
||||
qdel(longsword)
|
||||
|
||||
// Move the guy back to the bottom left, 32x32.
|
||||
icon.Crop(17, 17, 48, 48)
|
||||
|
||||
return finish_preview_icon(icon)
|
||||
|
||||
/datum/antagonist/cult/proc/equip_cultist(metal=TRUE)
|
||||
var/mob/living/carbon/H = owner.current
|
||||
if(!istype(H))
|
||||
return
|
||||
. += cult_give_item(/obj/item/melee/cultblade/dagger, H)
|
||||
if(metal)
|
||||
. += cult_give_item(/obj/item/stack/sheet/runed_metal/ten, H)
|
||||
to_chat(owner, "These will help you start the cult on this station. Use them well, and remember - you are not the only one.</span>")
|
||||
|
||||
|
||||
/datum/antagonist/cult/proc/cult_give_item(obj/item/item_path, mob/living/carbon/human/mob)
|
||||
var/list/slots = list(
|
||||
"backpack" = ITEM_SLOT_BACKPACK,
|
||||
"left pocket" = ITEM_SLOT_LPOCKET,
|
||||
"right pocket" = ITEM_SLOT_RPOCKET
|
||||
)
|
||||
|
||||
var/T = new item_path(mob)
|
||||
var/item_name = initial(item_path.name)
|
||||
var/where = mob.equip_in_one_of_slots(T, slots, indirect_action = TRUE)
|
||||
if(!where)
|
||||
to_chat(mob, span_userdanger("Unfortunately, you weren't able to get a [item_name]. This is very bad and you should adminhelp immediately (press F1)."))
|
||||
return FALSE
|
||||
else
|
||||
to_chat(mob, span_danger("You have a [item_name] in your [where]."))
|
||||
if(where == "backpack")
|
||||
mob.back.atom_storage?.show_contents(mob)
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/cult/apply_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
if(mob_override)
|
||||
current = mob_override
|
||||
var/mob/living/current = owner.current || mob_override
|
||||
handle_clown_mutation(current, mob_override ? null : "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
|
||||
current.faction |= FACTION_CULT
|
||||
current.grant_language(/datum/language/narsie, TRUE, TRUE, LANGUAGE_CULTIST)
|
||||
if(!cult_team.cult_master)
|
||||
vote.Grant(current)
|
||||
|
||||
var/datum/action/innate/cult/comm/communion = new
|
||||
communion.Grant(current)
|
||||
if(isnull(cult_team.cult_leader_datum))
|
||||
vote_ability.Grant(current)
|
||||
if(ishuman(current))
|
||||
var/datum/action/innate/cult/blood_magic/magic = new
|
||||
magic.Grant(current)
|
||||
|
||||
current.throw_alert("bloodsense", /atom/movable/screen/alert/bloodsense)
|
||||
if(cult_team.cult_risen)
|
||||
current.AddElement(/datum/element/cult_eyes, initial_delay = 0 SECONDS)
|
||||
@@ -152,15 +75,16 @@
|
||||
|
||||
/datum/antagonist/cult/remove_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
if(mob_override)
|
||||
current = mob_override
|
||||
var/mob/living/current = owner.current || mob_override
|
||||
handle_clown_mutation(current, removing = FALSE)
|
||||
current.faction -= FACTION_CULT
|
||||
current.remove_language(/datum/language/narsie, TRUE, TRUE, LANGUAGE_CULTIST)
|
||||
vote.Remove(current)
|
||||
communion.Remove(current)
|
||||
magic.Remove(current)
|
||||
|
||||
QDEL_NULL(vote_ability)
|
||||
for(var/datum/action/innate/cult/cult_buttons in owner.current.actions)
|
||||
qdel(cult_buttons)
|
||||
current.update_mob_action_buttons()
|
||||
|
||||
current.clear_alert("bloodsense")
|
||||
if (HAS_TRAIT(current, TRAIT_UNNATURAL_RED_GLOWY_EYES))
|
||||
current.RemoveElement(/datum/element/cult_eyes)
|
||||
@@ -188,12 +112,66 @@
|
||||
.["Dagger and Metal"] = CALLBACK(src, PROC_REF(admin_give_metal))
|
||||
.["Remove Dagger and Metal"] = CALLBACK(src, PROC_REF(admin_take_all))
|
||||
|
||||
if(is_cult_leader())
|
||||
.["Demote From Leader"] = CALLBACK(src, PROC_REF(demote_from_leader))
|
||||
else if(isnull(cult_team.cult_leader_datum))
|
||||
.["Make Cult Leader"] = CALLBACK(src, PROC_REF(make_cult_leader))
|
||||
|
||||
/datum/antagonist/cult/get_team()
|
||||
return cult_team
|
||||
|
||||
/datum/antagonist/cult/create_team(datum/team/cult/new_team)
|
||||
if(!new_team)
|
||||
//todo remove this and allow admin buttons to create more than one cult
|
||||
for(var/datum/antagonist/cult/H in GLOB.antagonists)
|
||||
if(!H.owner)
|
||||
continue
|
||||
if(H.cult_team)
|
||||
cult_team = H.cult_team
|
||||
return
|
||||
cult_team = new /datum/team/cult
|
||||
cult_team.setup_objectives()
|
||||
return
|
||||
if(!istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
cult_team = new_team
|
||||
|
||||
///Equips the cultist with a dagger and runed metal.
|
||||
/datum/antagonist/cult/proc/equip_cultist(metal = TRUE)
|
||||
var/mob/living/carbon/H = owner.current
|
||||
if(!istype(H))
|
||||
return
|
||||
. += cult_give_item(/obj/item/melee/cultblade/dagger, H)
|
||||
if(metal)
|
||||
. += cult_give_item(/obj/item/stack/sheet/runed_metal/ten, H)
|
||||
to_chat(owner, "These will help you start the cult on this station. Use them well, and remember - you are not the only one.</span>")
|
||||
|
||||
///Attempts to make a new item and put it in a potential inventory slot in the provided mob.
|
||||
/datum/antagonist/cult/proc/cult_give_item(obj/item/item_path, mob/living/carbon/human/mob)
|
||||
var/list/slots = list(
|
||||
"backpack" = ITEM_SLOT_BACKPACK,
|
||||
"left pocket" = ITEM_SLOT_LPOCKET,
|
||||
"right pocket" = ITEM_SLOT_RPOCKET,
|
||||
)
|
||||
|
||||
var/T = new item_path(mob)
|
||||
var/item_name = initial(item_path.name)
|
||||
var/where = mob.equip_in_one_of_slots(T, slots)
|
||||
if(!where)
|
||||
to_chat(mob, span_userdanger("Unfortunately, you weren't able to get a [item_name]. This is very bad and you should adminhelp immediately (press F1)."))
|
||||
return FALSE
|
||||
else
|
||||
to_chat(mob, span_danger("You have a [item_name] in your [where]."))
|
||||
if(where == "backpack")
|
||||
mob.back.atom_storage?.show_contents(mob)
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/cult/proc/admin_give_dagger(mob/admin)
|
||||
if(!equip_cultist(metal=FALSE))
|
||||
if(!equip_cultist(metal = FALSE))
|
||||
to_chat(admin, span_danger("Spawning dagger failed!"))
|
||||
|
||||
/datum/antagonist/cult/proc/admin_give_metal(mob/admin)
|
||||
if (!equip_cultist(metal=TRUE))
|
||||
if (!equip_cultist(metal = TRUE))
|
||||
to_chat(admin, span_danger("Spawning runed metal failed!"))
|
||||
|
||||
/datum/antagonist/cult/proc/admin_take_all(mob/admin)
|
||||
@@ -202,295 +180,120 @@
|
||||
if(istype(o, /obj/item/melee/cultblade/dagger) || istype(o, /obj/item/stack/sheet/runed_metal))
|
||||
qdel(o)
|
||||
|
||||
/datum/antagonist/cult/master
|
||||
ignore_implant = TRUE
|
||||
show_in_antagpanel = FALSE //Feel free to add this later
|
||||
///Returns whether or not this datum is its team's leader.
|
||||
/datum/antagonist/cult/proc/is_cult_leader()
|
||||
return (cult_team.cult_leader_datum == src)
|
||||
|
||||
///Turns this antag datum into its team's leader, assigning them their unique abilities, hud, and deathrattle.
|
||||
/datum/antagonist/cult/proc/make_cult_leader()
|
||||
if(cult_team.cult_leader_datum)
|
||||
return FALSE
|
||||
cult_team.cult_leader_datum = src
|
||||
|
||||
antag_hud_name = "cultmaster"
|
||||
var/datum/action/innate/cult/master/finalreck/reckoning = new
|
||||
add_team_hud(owner.current)
|
||||
RegisterSignal(owner.current, COMSIG_MOB_STATCHANGE, PROC_REF(deathrattle))
|
||||
|
||||
if(!cult_team.reckoning_complete)
|
||||
var/datum/action/innate/cult/master/finalreck/reckoning = new
|
||||
reckoning.Grant(owner.current)
|
||||
var/datum/action/innate/cult/master/cultmark/bloodmark = new
|
||||
var/datum/action/innate/cult/master/pulse/throwing = new
|
||||
bloodmark.Grant(owner.current)
|
||||
throwing.Grant(owner.current)
|
||||
owner.current.update_mob_action_buttons()
|
||||
|
||||
/datum/antagonist/cult/master/Destroy()
|
||||
QDEL_NULL(reckoning)
|
||||
QDEL_NULL(bloodmark)
|
||||
QDEL_NULL(throwing)
|
||||
return ..()
|
||||
for(var/datum/mind/cult_mind as anything in cult_team.members)
|
||||
vote_ability.Remove(cult_mind.current)
|
||||
to_chat(cult_mind.current, span_cultlarge("[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!"))
|
||||
|
||||
/datum/antagonist/cult/master/greet()
|
||||
to_chat(owner.current, "<span class='warningplain'><span class='cultlarge'>You are the cult's Master</span>. As the cult's Master, you have a unique title and loud voice when communicating, are capable of marking \
|
||||
targets, such as a location or a noncultist, to direct the cult to them, and, finally, you are capable of summoning the entire living cult to your location <b><i>once</i></b>. Use these abilities to direct the cult to victory at any cost.</span>")
|
||||
to_chat(owner.current, span_cultlarge("<span class='warningplain'>You are the cult's Master</span>. \
|
||||
As the cult's Master, you have a unique title and loud voice when communicating, are capable of marking \
|
||||
targets, such as a location or a noncultist, to direct the cult to them, and, finally, you are capable of \
|
||||
summoning the entire living cult to your location <b><i>once</i></b>. Use these abilities to direct the cult \
|
||||
to victory at any cost."))
|
||||
|
||||
/datum/antagonist/cult/master/apply_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
if(mob_override)
|
||||
current = mob_override
|
||||
if(!cult_team.reckoning_complete)
|
||||
reckoning.Grant(current)
|
||||
bloodmark.Grant(current)
|
||||
throwing.Grant(current)
|
||||
current.update_mob_action_buttons()
|
||||
current.apply_status_effect(/datum/status_effect/cult_master)
|
||||
if(cult_team.cult_risen)
|
||||
current.AddElement(/datum/element/cult_eyes, initial_delay = 0 SECONDS)
|
||||
if(cult_team.cult_ascendent)
|
||||
current.AddElement(/datum/element/cult_halo, initial_delay = 0 SECONDS)
|
||||
add_team_hud(current, /datum/antagonist/cult)
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/cult/master/remove_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
if(mob_override)
|
||||
current = mob_override
|
||||
reckoning.Remove(current)
|
||||
bloodmark.Remove(current)
|
||||
throwing.Remove(current)
|
||||
current.update_mob_action_buttons()
|
||||
current.remove_status_effect(/datum/status_effect/cult_master)
|
||||
///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
|
||||
cult_team.cult_leader_datum = null
|
||||
|
||||
/datum/team/cult
|
||||
name = "\improper Cult"
|
||||
antag_hud_name = initial(antag_hud_name)
|
||||
add_team_hud(owner.current)
|
||||
UnregisterSignal(owner.current, COMSIG_MOB_STATCHANGE)
|
||||
|
||||
///The blood mark target
|
||||
var/atom/blood_target
|
||||
///Image of the blood mark target
|
||||
var/image/blood_target_image
|
||||
///Timer for the blood mark expiration
|
||||
var/blood_target_reset_timer
|
||||
var/datum/action/innate/cult/master/finalreck/reckoning = locate() in owner.current.actions
|
||||
if(reckoning)
|
||||
reckoning.Remove(owner.current)
|
||||
var/datum/action/innate/cult/master/cultmark/bloodmark = locate() in owner.current.actions
|
||||
if(bloodmark)
|
||||
bloodmark.Remove(owner.current)
|
||||
var/datum/action/innate/cult/master/pulse/throwing = locate() in owner.current.actions
|
||||
if(throwing)
|
||||
throwing.Remove(owner.current)
|
||||
owner.current.update_mob_action_buttons()
|
||||
for(var/datum/mind/cult_mind as anything in cult_team.members)
|
||||
vote_ability.Grant(cult_mind.current)
|
||||
|
||||
///Has a vote been called for a leader?
|
||||
var/cult_vote_called = FALSE
|
||||
///The cult leader
|
||||
var/mob/living/cult_master
|
||||
///Has the mass teleport been used yet?
|
||||
var/reckoning_complete = FALSE
|
||||
///Has the cult risen, and gotten red eyes?
|
||||
var/cult_risen = FALSE
|
||||
///Has the cult asceneded, and gotten halos?
|
||||
var/cult_ascendent = FALSE
|
||||
to_chat(owner.current, span_cultlarge("You have been demoted from being the cult's Master, you are now an acolyte once more!"))
|
||||
|
||||
///Has narsie been summoned yet?
|
||||
var/narsie_summoned = FALSE
|
||||
///How large were we at max size.
|
||||
var/size_at_maximum = 0
|
||||
///list of cultists just before summoning Narsie
|
||||
var/list/true_cultists = list()
|
||||
return TRUE
|
||||
|
||||
/datum/team/cult/proc/check_size()
|
||||
if(cult_ascendent)
|
||||
return
|
||||
|
||||
// This proc is unnecessary clutter whilst running cult related unit tests
|
||||
// Remove this if, at some point, someone decides to test that halos and eyes are added at expected ratios
|
||||
#ifndef UNIT_TESTS
|
||||
var/alive = 0
|
||||
var/cultplayers = 0
|
||||
for(var/I in GLOB.player_list)
|
||||
var/mob/M = I
|
||||
if(M.stat != DEAD)
|
||||
if(IS_CULTIST(M))
|
||||
++cultplayers
|
||||
else
|
||||
++alive
|
||||
|
||||
ASSERT(cultplayers) //we shouldn't be here.
|
||||
var/ratio = alive ? cultplayers/alive : 1
|
||||
if(ratio > CULT_RISEN && !cult_risen)
|
||||
for(var/datum/mind/mind as anything in members)
|
||||
if(mind.current)
|
||||
SEND_SOUND(mind.current, 'sound/hallucinations/i_see_you2.ogg')
|
||||
to_chat(mind.current, span_cultlarge(span_warning("The veil weakens as your cult grows, your eyes begin to glow...")))
|
||||
mind.current.AddElement(/datum/element/cult_eyes)
|
||||
cult_risen = TRUE
|
||||
log_game("The blood cult has risen with [cultplayers] players.")
|
||||
|
||||
if(ratio > CULT_ASCENDENT && !cult_ascendent)
|
||||
for(var/datum/mind/mind as anything in members)
|
||||
if(mind.current)
|
||||
SEND_SOUND(mind.current, 'sound/hallucinations/im_here1.ogg')
|
||||
to_chat(mind.current, span_cultlarge(span_warning("Your cult is ascendent and the red harvest approaches - you cannot hide your true nature for much longer!!")))
|
||||
mind.current.AddElement(/datum/element/cult_halo)
|
||||
cult_ascendent = TRUE
|
||||
log_game("The blood cult has ascended with [cultplayers] players.")
|
||||
#endif
|
||||
|
||||
/datum/team/cult/add_member(datum/mind/new_member)
|
||||
. = ..()
|
||||
// A little hacky, but this checks that cult ghosts don't contribute to the size at maximum value.
|
||||
if(is_unassigned_job(new_member.assigned_role))
|
||||
return
|
||||
size_at_maximum++
|
||||
|
||||
/datum/team/cult/proc/make_image(datum/objective/sacrifice/sac_objective)
|
||||
var/datum/job/job_of_sacrifice = sac_objective.target.assigned_role
|
||||
var/datum/preferences/prefs_of_sacrifice = sac_objective.target.current.client.prefs
|
||||
var/icon/reshape = get_flat_human_icon(null, job_of_sacrifice, prefs_of_sacrifice, list(SOUTH))
|
||||
reshape.Shift(SOUTH, 4)
|
||||
reshape.Shift(EAST, 1)
|
||||
reshape.Crop(7,4,26,31)
|
||||
reshape.Crop(-5,-3,26,30)
|
||||
sac_objective.sac_image = reshape
|
||||
|
||||
/datum/team/cult/proc/setup_objectives()
|
||||
var/datum/objective/sacrifice/sacrifice_objective = new
|
||||
sacrifice_objective.team = src
|
||||
sacrifice_objective.find_target()
|
||||
objectives += sacrifice_objective
|
||||
|
||||
var/datum/objective/eldergod/summon_objective = new
|
||||
summon_objective.team = src
|
||||
objectives += summon_objective
|
||||
|
||||
/datum/objective/sacrifice
|
||||
var/sacced = FALSE
|
||||
var/sac_image
|
||||
|
||||
/// Unregister signals from the old target so it doesn't cause issues when sacrificed of when a new target is found.
|
||||
/datum/objective/sacrifice/proc/clear_sacrifice()
|
||||
if(!target)
|
||||
return
|
||||
UnregisterSignal(target, COMSIG_MIND_TRANSFERRED)
|
||||
if(target.current)
|
||||
UnregisterSignal(target.current, list(COMSIG_QDELETING, COMSIG_MOB_MIND_TRANSFERRED_INTO))
|
||||
target = null
|
||||
|
||||
/datum/objective/sacrifice/find_target(dupe_search_range, list/blacklist)
|
||||
clear_sacrifice()
|
||||
if(!istype(team, /datum/team/cult))
|
||||
return
|
||||
var/datum/team/cult/cult = team
|
||||
var/list/target_candidates = list()
|
||||
for(var/mob/living/carbon/human/player in GLOB.player_list)
|
||||
if(player.mind && !player.mind.has_antag_datum(/datum/antagonist/cult) && !is_convertable_to_cult(player) && player.stat != DEAD)
|
||||
target_candidates += player.mind
|
||||
if(target_candidates.len == 0)
|
||||
message_admins("Cult Sacrifice: Could not find unconvertible target, checking for convertible target.")
|
||||
for(var/mob/living/carbon/human/player in GLOB.player_list)
|
||||
if(player.mind && !player.mind.has_antag_datum(/datum/antagonist/cult) && player.stat != DEAD)
|
||||
target_candidates += player.mind
|
||||
list_clear_nulls(target_candidates)
|
||||
if(LAZYLEN(target_candidates))
|
||||
target = pick(target_candidates)
|
||||
update_explanation_text()
|
||||
// Register a bunch of signals to both the target mind and its body
|
||||
// to stop cult from softlocking everytime the target is deleted before being actually sacrificed.
|
||||
RegisterSignal(target, COMSIG_MIND_TRANSFERRED, PROC_REF(on_mind_transfer))
|
||||
RegisterSignal(target.current, COMSIG_QDELETING, PROC_REF(on_target_body_del))
|
||||
RegisterSignal(target.current, COMSIG_MOB_MIND_TRANSFERRED_INTO, PROC_REF(on_possible_mindswap))
|
||||
else
|
||||
message_admins("Cult Sacrifice: Could not find unconvertible or convertible target. WELP!")
|
||||
sacced = TRUE // Prevents another hypothetical softlock. This basically means every PC is a cultist.
|
||||
if(!sacced)
|
||||
cult.make_image(src)
|
||||
for(var/datum/mind/mind in cult.members)
|
||||
if(mind.current)
|
||||
mind.current.clear_alert("bloodsense")
|
||||
mind.current.throw_alert("bloodsense", /atom/movable/screen/alert/bloodsense)
|
||||
|
||||
/datum/objective/sacrifice/proc/on_target_body_del()
|
||||
///If dead (and Narsie isn't summoned), will alert all Cultists of their death, sending their location out.
|
||||
/datum/antagonist/cult/proc/deathrattle(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
INVOKE_ASYNC(src, PROC_REF(find_target))
|
||||
|
||||
/datum/objective/sacrifice/proc/on_mind_transfer(datum/source, mob/previous_body)
|
||||
SIGNAL_HANDLER
|
||||
//If, for some reason, the mind was transferred to a ghost (better safe than sorry), find a new target.
|
||||
if(!isliving(target.current))
|
||||
INVOKE_ASYNC(src, PROC_REF(find_target))
|
||||
if(owner.current.stat != DEAD)
|
||||
return
|
||||
UnregisterSignal(previous_body, list(COMSIG_QDELETING, COMSIG_MOB_MIND_TRANSFERRED_INTO))
|
||||
RegisterSignal(target.current, COMSIG_QDELETING, PROC_REF(on_target_body_del))
|
||||
RegisterSignal(target.current, COMSIG_MOB_MIND_TRANSFERRED_INTO, PROC_REF(on_possible_mindswap))
|
||||
|
||||
/datum/objective/sacrifice/proc/on_possible_mindswap(mob/source)
|
||||
SIGNAL_HANDLER
|
||||
UnregisterSignal(target.current, list(COMSIG_QDELETING, COMSIG_MOB_MIND_TRANSFERRED_INTO))
|
||||
//we check if the mind is bodyless only after mindswap shenanigeans to avoid issues.
|
||||
addtimer(CALLBACK(src, PROC_REF(do_we_have_a_body)), 0 SECONDS)
|
||||
|
||||
/datum/objective/sacrifice/proc/do_we_have_a_body()
|
||||
if(!target.current) //The player was ghosted and the mind isn't probably going to be transferred to another mob at this point.
|
||||
find_target()
|
||||
if(!QDELETED(GLOB.cult_narsie))
|
||||
return
|
||||
if(!is_cult_leader())
|
||||
return
|
||||
RegisterSignal(target.current, COMSIG_QDELETING, PROC_REF(on_target_body_del))
|
||||
RegisterSignal(target.current, COMSIG_MOB_MIND_TRANSFERRED_INTO, PROC_REF(on_possible_mindswap))
|
||||
|
||||
/datum/objective/sacrifice/check_completion()
|
||||
return sacced || completed
|
||||
var/area/current_area = get_area(owner.current)
|
||||
for(var/datum/mind/cult_mind as anything in cult_team.members)
|
||||
SEND_SOUND(cult_mind, sound('sound/hallucinations/veryfar_noise.ogg'))
|
||||
to_chat(cult_mind, span_cultlarge("The Cult's Master, [owner.current.name], has fallen in \the [current_area]!"))
|
||||
|
||||
/datum/objective/sacrifice/update_explanation_text()
|
||||
if(target)
|
||||
explanation_text = "Sacrifice [target], the [target.assigned_role.title] via invoking an Offer rune with [target.p_them()] on it and three acolytes around it."
|
||||
else
|
||||
explanation_text = "The veil has already been weakened here, proceed to the final objective."
|
||||
/datum/antagonist/cult/get_preview_icon()
|
||||
var/icon/icon = render_preview_outfit(preview_outfit)
|
||||
|
||||
/datum/objective/eldergod
|
||||
var/summoned = FALSE
|
||||
var/killed = FALSE
|
||||
var/list/summon_spots = list()
|
||||
// The longsword is 64x64, but getFlatIcon crunches to 32x32.
|
||||
// So I'm just going to add it in post, screw it.
|
||||
|
||||
/datum/objective/eldergod/New()
|
||||
..()
|
||||
var/sanity = 0
|
||||
while(summon_spots.len < SUMMON_POSSIBILITIES && sanity < 100)
|
||||
var/area/summon_area = pick(GLOB.areas - summon_spots)
|
||||
if(summon_area && is_station_level(summon_area.z) && (summon_area.area_flags & VALID_TERRITORY))
|
||||
summon_spots += summon_area
|
||||
sanity++
|
||||
update_explanation_text()
|
||||
// Center the dude, because item icon states start from the center.
|
||||
// This makes the image 64x64.
|
||||
icon.Crop(-15, -15, 48, 48)
|
||||
|
||||
/datum/objective/eldergod/update_explanation_text()
|
||||
explanation_text = "Summon Nar'Sie by invoking the rune 'Summon Nar'Sie'. The summoning can only be accomplished in [english_list(summon_spots)] - where the veil is weak enough for the ritual to begin."
|
||||
var/obj/item/melee/cultblade/longsword = new
|
||||
icon.Blend(icon(longsword.lefthand_file, longsword.inhand_icon_state), ICON_OVERLAY)
|
||||
qdel(longsword)
|
||||
|
||||
/datum/objective/eldergod/check_completion()
|
||||
if(killed)
|
||||
return CULT_NARSIE_KILLED // You failed so hard that even the code went backwards.
|
||||
return summoned || completed
|
||||
// Move the guy back to the bottom left, 32x32.
|
||||
icon.Crop(17, 17, 48, 48)
|
||||
|
||||
/datum/team/cult/proc/check_cult_victory()
|
||||
for(var/datum/objective/O in objectives)
|
||||
if(O.check_completion() == CULT_NARSIE_KILLED)
|
||||
return CULT_NARSIE_KILLED
|
||||
else if(!O.check_completion())
|
||||
return CULT_LOSS
|
||||
return CULT_VICTORY
|
||||
return finish_preview_icon(icon)
|
||||
|
||||
/datum/team/cult/roundend_report()
|
||||
var/list/parts = list()
|
||||
var/victory = check_cult_victory()
|
||||
/datum/outfit/cultist
|
||||
name = "Cultist (Preview only)"
|
||||
|
||||
if(victory == CULT_NARSIE_KILLED) // Epic failure, you summoned your god and then someone killed it.
|
||||
parts += "<span class='redtext big'>Nar'sie has been killed! The cult will haunt the universe no longer!</span>"
|
||||
else if(victory)
|
||||
parts += "<span class='greentext big'>The cult has succeeded! Nar'Sie has snuffed out another torch in the void!</span>"
|
||||
else
|
||||
parts += "<span class='redtext big'>The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!</span>"
|
||||
uniform = /obj/item/clothing/under/color/black
|
||||
suit = /obj/item/clothing/suit/hooded/cultrobes/alt
|
||||
head = /obj/item/clothing/head/hooded/cult_hoodie/alt
|
||||
shoes = /obj/item/clothing/shoes/cult/alt
|
||||
r_hand = /obj/item/melee/blood_magic/stun
|
||||
|
||||
if(objectives.len)
|
||||
parts += "<b>The cultists' objectives were:</b>"
|
||||
var/count = 1
|
||||
for(var/datum/objective/objective in objectives)
|
||||
if(objective.check_completion())
|
||||
parts += "<b>Objective #[count]</b>: [objective.explanation_text] [span_greentext("Success!")]"
|
||||
else
|
||||
parts += "<b>Objective #[count]</b>: [objective.explanation_text] [span_redtext("Fail.")]"
|
||||
count++
|
||||
/datum/outfit/cultist/post_equip(mob/living/carbon/human/equipped, visualsOnly)
|
||||
equipped.eye_color_left = BLOODCULT_EYE
|
||||
equipped.eye_color_right = BLOODCULT_EYE
|
||||
equipped.update_body()
|
||||
|
||||
if(members.len)
|
||||
parts += "<span class='header'>The cultists were:</span>"
|
||||
if(length(true_cultists))
|
||||
parts += printplayerlist(true_cultists)
|
||||
else
|
||||
parts += printplayerlist(members)
|
||||
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
|
||||
/datum/team/cult/proc/is_sacrifice_target(datum/mind/mind)
|
||||
for(var/datum/objective/sacrifice/sac_objective in objectives)
|
||||
if(mind == sac_objective.target)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/// Returns whether the given mob is convertable to the blood cult
|
||||
///Returns whether the given mob is convertable to the blood cult
|
||||
/proc/is_convertable_to_cult(mob/living/target, datum/team/cult/specific_cult)
|
||||
if(!istype(target))
|
||||
return FALSE
|
||||
@@ -510,82 +313,3 @@
|
||||
if(HAS_TRAIT(target, TRAIT_MINDSHIELD) || issilicon(target) || isbot(target) || isdrone(target))
|
||||
return FALSE //can't convert machines, shielded, or braindead
|
||||
return TRUE
|
||||
|
||||
/// Sets a blood target for the cult.
|
||||
/datum/team/cult/proc/set_blood_target(atom/new_target, mob/marker, duration = 90 SECONDS)
|
||||
if(QDELETED(new_target))
|
||||
CRASH("A null or invalid target was passed to set_blood_target.")
|
||||
|
||||
if(duration != INFINITY && blood_target_reset_timer)
|
||||
return FALSE
|
||||
|
||||
deltimer(blood_target_reset_timer)
|
||||
blood_target = new_target
|
||||
RegisterSignal(blood_target, COMSIG_QDELETING, PROC_REF(unset_blood_target_and_timer))
|
||||
var/area/target_area = get_area(new_target)
|
||||
|
||||
blood_target_image = image('icons/effects/mouse_pointers/cult_target.dmi', new_target, "glow", ABOVE_MOB_LAYER)
|
||||
blood_target_image.appearance_flags = RESET_COLOR
|
||||
blood_target_image.pixel_x = -new_target.pixel_x
|
||||
blood_target_image.pixel_y = -new_target.pixel_y
|
||||
|
||||
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_cultlarge("[marker] has marked [blood_target] in the [target_area.name] as the cult's top priority, get there immediately!")))
|
||||
SEND_SOUND(cultist.current, sound(pick('sound/hallucinations/over_here2.ogg','sound/hallucinations/over_here3.ogg'), 0, 1, 75))
|
||||
cultist.current.client.images += blood_target_image
|
||||
|
||||
if(duration != INFINITY)
|
||||
blood_target_reset_timer = addtimer(CALLBACK(src, PROC_REF(unset_blood_target)), duration, TIMER_STOPPABLE)
|
||||
return TRUE
|
||||
|
||||
/// Unsets out blood target, clearing the images from all the cultists.
|
||||
/datum/team/cult/proc/unset_blood_target()
|
||||
blood_target_reset_timer = null
|
||||
|
||||
for(var/datum/mind/cultist as anything in members)
|
||||
if(!cultist.current)
|
||||
continue
|
||||
if(cultist.current.stat == DEAD || !cultist.current.client)
|
||||
continue
|
||||
|
||||
if(QDELETED(blood_target))
|
||||
to_chat(cultist.current, span_bold(span_cultlarge("The blood mark's target is lost!")))
|
||||
else
|
||||
to_chat(cultist.current, span_bold(span_cultlarge("The blood mark has expired!")))
|
||||
cultist.current.client.images -= blood_target_image
|
||||
|
||||
UnregisterSignal(blood_target, COMSIG_QDELETING)
|
||||
blood_target = null
|
||||
|
||||
QDEL_NULL(blood_target_image)
|
||||
|
||||
/// Unsets our blood target when they get deleted.
|
||||
/datum/team/cult/proc/unset_blood_target_and_timer(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
deltimer(blood_target_reset_timer)
|
||||
unset_blood_target()
|
||||
|
||||
/datum/outfit/cultist
|
||||
name = "Cultist (Preview only)"
|
||||
|
||||
uniform = /obj/item/clothing/under/color/black
|
||||
suit = /obj/item/clothing/suit/hooded/cultrobes/alt
|
||||
head = /obj/item/clothing/head/hooded/cult_hoodie/alt
|
||||
shoes = /obj/item/clothing/shoes/cult/alt
|
||||
r_hand = /obj/item/melee/blood_magic/stun
|
||||
|
||||
/datum/outfit/cultist/post_equip(mob/living/carbon/human/equipped, visualsOnly)
|
||||
equipped.eye_color_left = BLOODCULT_EYE
|
||||
equipped.eye_color_right = BLOODCULT_EYE
|
||||
equipped.update_body()
|
||||
|
||||
#undef CULT_LOSS
|
||||
#undef CULT_NARSIE_KILLED
|
||||
#undef CULT_VICTORY
|
||||
#undef SUMMON_POSSIBILITIES
|
||||
|
||||
@@ -46,20 +46,20 @@
|
||||
|
||||
/datum/action/innate/cult/comm/proc/cultist_commune(mob/living/user, message)
|
||||
var/my_message
|
||||
if(!message)
|
||||
if(!message || !user.mind)
|
||||
return
|
||||
user.whisper("O bidai nabora se[pick("'","`")]sma!", language = /datum/language/common)
|
||||
user.whisper(html_decode(message), filterproof = TRUE)
|
||||
var/title = "Acolyte"
|
||||
var/span = "cult italic"
|
||||
if(user.mind && user.mind.has_antag_datum(/datum/antagonist/cult/master))
|
||||
var/datum/antagonist/cult/cult_datum = user.mind.has_antag_datum(/datum/antagonist/cult)
|
||||
if(cult_datum.is_cult_leader())
|
||||
span = "cultlarge"
|
||||
title = "Master"
|
||||
else if(!ishuman(user))
|
||||
title = "Construct"
|
||||
my_message = "<span class='[span]'><b>[title] [findtextEx(user.name, user.real_name) ? user.name : "[user.real_name] (as [user.name])"]:</b> [message]</span>"
|
||||
for(var/i in GLOB.player_list)
|
||||
var/mob/M = i
|
||||
for(var/mob/M as anything in GLOB.player_list)
|
||||
if(IS_CULTIST(M))
|
||||
to_chat(M, my_message)
|
||||
else if(M in GLOB.dead_mob_list)
|
||||
@@ -108,7 +108,7 @@
|
||||
var/datum/antagonist/cult/C = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
|
||||
pollCultists(owner,C.cult_team)
|
||||
|
||||
/proc/pollCultists(mob/living/Nominee,datum/team/cult/team) //Cult Master Poll
|
||||
/proc/pollCultists(mob/living/Nominee, datum/team/cult/team) //Cult Master Poll
|
||||
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
|
||||
@@ -150,22 +150,16 @@
|
||||
if(!B.current.incapacitated())
|
||||
to_chat(B.current, span_cultlarge("[Nominee] could not win the cult's support and shall continue to serve as an acolyte."))
|
||||
return FALSE
|
||||
team.cult_master = Nominee
|
||||
var/datum/antagonist/cult/cultist = Nominee.mind.has_antag_datum(/datum/antagonist/cult)
|
||||
if (cultist)
|
||||
cultist.silent = TRUE
|
||||
cultist.on_removal()
|
||||
Nominee.mind.add_antag_datum(/datum/antagonist/cult/master)
|
||||
for(var/datum/mind/B in team.members)
|
||||
if(B.current)
|
||||
for(var/datum/action/innate/cult/mastervote/vote in B.current.actions)
|
||||
vote.Remove(B.current)
|
||||
if(!B.current.incapacitated())
|
||||
to_chat(B.current,span_cultlarge("[Nominee] has won the cult's support and is now their master. Follow [Nominee.p_their()] orders to the best of your ability!"))
|
||||
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 || !owner.mind.has_antag_datum(/datum/antagonist/cult/master) || GLOB.cult_narsie)
|
||||
if(!owner.mind || GLOB.cult_narsie)
|
||||
return FALSE
|
||||
var/datum/antagonist/cult/cult_datum = owner.mind.has_antag_datum(/datum/antagonist/cult)
|
||||
if(!cult_datum.is_cult_leader())
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
#define SUMMON_POSSIBILITIES 3
|
||||
|
||||
/datum/objective/sacrifice
|
||||
var/sacced = FALSE
|
||||
var/sac_image
|
||||
|
||||
/// Unregister signals from the old target so it doesn't cause issues when sacrificed of when a new target is found.
|
||||
/datum/objective/sacrifice/proc/clear_sacrifice()
|
||||
if(!target)
|
||||
return
|
||||
UnregisterSignal(target, COMSIG_MIND_TRANSFERRED)
|
||||
if(target.current)
|
||||
UnregisterSignal(target.current, list(COMSIG_QDELETING, COMSIG_MOB_MIND_TRANSFERRED_INTO))
|
||||
target = null
|
||||
|
||||
/datum/objective/sacrifice/find_target(dupe_search_range, list/blacklist)
|
||||
clear_sacrifice()
|
||||
if(!istype(team, /datum/team/cult))
|
||||
return
|
||||
var/datum/team/cult/cult = team
|
||||
var/list/target_candidates = list()
|
||||
for(var/mob/living/carbon/human/player in GLOB.player_list)
|
||||
if(player.mind && !player.mind.has_antag_datum(/datum/antagonist/cult) && !is_convertable_to_cult(player) && player.stat != DEAD)
|
||||
target_candidates += player.mind
|
||||
if(target_candidates.len == 0)
|
||||
message_admins("Cult Sacrifice: Could not find unconvertible target, checking for convertible target.")
|
||||
for(var/mob/living/carbon/human/player in GLOB.player_list)
|
||||
if(player.mind && !player.mind.has_antag_datum(/datum/antagonist/cult) && player.stat != DEAD)
|
||||
target_candidates += player.mind
|
||||
list_clear_nulls(target_candidates)
|
||||
if(LAZYLEN(target_candidates))
|
||||
target = pick(target_candidates)
|
||||
update_explanation_text()
|
||||
// Register a bunch of signals to both the target mind and its body
|
||||
// to stop cult from softlocking everytime the target is deleted before being actually sacrificed.
|
||||
RegisterSignal(target, COMSIG_MIND_TRANSFERRED, PROC_REF(on_mind_transfer))
|
||||
RegisterSignal(target.current, COMSIG_QDELETING, PROC_REF(on_target_body_del))
|
||||
RegisterSignal(target.current, COMSIG_MOB_MIND_TRANSFERRED_INTO, PROC_REF(on_possible_mindswap))
|
||||
else
|
||||
message_admins("Cult Sacrifice: Could not find unconvertible or convertible target. WELP!")
|
||||
sacced = TRUE // Prevents another hypothetical softlock. This basically means every PC is a cultist.
|
||||
if(!sacced)
|
||||
cult.make_image(src)
|
||||
for(var/datum/mind/mind in cult.members)
|
||||
if(mind.current)
|
||||
mind.current.clear_alert("bloodsense")
|
||||
mind.current.throw_alert("bloodsense", /atom/movable/screen/alert/bloodsense)
|
||||
|
||||
/datum/objective/sacrifice/proc/on_target_body_del()
|
||||
SIGNAL_HANDLER
|
||||
INVOKE_ASYNC(src, PROC_REF(find_target))
|
||||
|
||||
/datum/objective/sacrifice/proc/on_mind_transfer(datum/source, mob/previous_body)
|
||||
SIGNAL_HANDLER
|
||||
//If, for some reason, the mind was transferred to a ghost (better safe than sorry), find a new target.
|
||||
if(!isliving(target.current))
|
||||
INVOKE_ASYNC(src, PROC_REF(find_target))
|
||||
return
|
||||
UnregisterSignal(previous_body, list(COMSIG_QDELETING, COMSIG_MOB_MIND_TRANSFERRED_INTO))
|
||||
RegisterSignal(target.current, COMSIG_QDELETING, PROC_REF(on_target_body_del))
|
||||
RegisterSignal(target.current, COMSIG_MOB_MIND_TRANSFERRED_INTO, PROC_REF(on_possible_mindswap))
|
||||
|
||||
/datum/objective/sacrifice/proc/on_possible_mindswap(mob/source)
|
||||
SIGNAL_HANDLER
|
||||
UnregisterSignal(target.current, list(COMSIG_QDELETING, COMSIG_MOB_MIND_TRANSFERRED_INTO))
|
||||
//we check if the mind is bodyless only after mindswap shenanigeans to avoid issues.
|
||||
addtimer(CALLBACK(src, PROC_REF(do_we_have_a_body)), 0 SECONDS)
|
||||
|
||||
/datum/objective/sacrifice/proc/do_we_have_a_body()
|
||||
if(!target.current) //The player was ghosted and the mind isn't probably going to be transferred to another mob at this point.
|
||||
find_target()
|
||||
return
|
||||
RegisterSignal(target.current, COMSIG_QDELETING, PROC_REF(on_target_body_del))
|
||||
RegisterSignal(target.current, COMSIG_MOB_MIND_TRANSFERRED_INTO, PROC_REF(on_possible_mindswap))
|
||||
|
||||
/datum/objective/sacrifice/check_completion()
|
||||
return sacced || completed
|
||||
|
||||
/datum/objective/sacrifice/update_explanation_text()
|
||||
if(target)
|
||||
explanation_text = "Sacrifice [target], the [target.assigned_role.title] via invoking an Offer rune with [target.p_them()] on it and three acolytes around it."
|
||||
else
|
||||
explanation_text = "The veil has already been weakened here, proceed to the final objective."
|
||||
|
||||
/datum/objective/eldergod
|
||||
var/summoned = FALSE
|
||||
var/killed = FALSE
|
||||
var/list/summon_spots = list()
|
||||
|
||||
/datum/objective/eldergod/New()
|
||||
..()
|
||||
var/sanity = 0
|
||||
while(summon_spots.len < SUMMON_POSSIBILITIES && sanity < 100)
|
||||
var/area/summon_area = pick(GLOB.areas - summon_spots)
|
||||
if(summon_area && is_station_level(summon_area.z) && (summon_area.area_flags & VALID_TERRITORY))
|
||||
summon_spots += summon_area
|
||||
sanity++
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/eldergod/update_explanation_text()
|
||||
explanation_text = "Summon Nar'Sie by invoking the rune 'Summon Nar'Sie'. The 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 CULT_NARSIE_KILLED // You failed so hard that even the code went backwards.
|
||||
return summoned || completed
|
||||
|
||||
#undef SUMMON_POSSIBILITIES
|
||||
@@ -0,0 +1,196 @@
|
||||
/datum/team/cult
|
||||
name = "\improper Cult"
|
||||
|
||||
///The blood mark target
|
||||
var/atom/blood_target
|
||||
///Image of the blood mark target
|
||||
var/image/blood_target_image
|
||||
///Timer for the blood mark expiration
|
||||
var/blood_target_reset_timer
|
||||
|
||||
///Has a vote been called for a leader?
|
||||
var/cult_vote_called = FALSE
|
||||
///The cult leader
|
||||
var/datum/antagonist/cult/cult_leader_datum
|
||||
///Has the mass teleport been used yet?
|
||||
var/reckoning_complete = FALSE
|
||||
///Has the cult risen, and gotten red eyes?
|
||||
var/cult_risen = FALSE
|
||||
///Has the cult asceneded, and gotten halos?
|
||||
var/cult_ascendent = FALSE
|
||||
|
||||
///Has narsie been summoned yet?
|
||||
var/narsie_summoned = FALSE
|
||||
///How large were we at max size.
|
||||
var/size_at_maximum = 0
|
||||
///list of cultists just before summoning Narsie
|
||||
var/list/true_cultists = list()
|
||||
|
||||
/datum/team/cult/proc/check_size()
|
||||
if(cult_ascendent)
|
||||
return
|
||||
|
||||
// This proc is unnecessary clutter whilst running cult related unit tests
|
||||
// Remove this if, at some point, someone decides to test that halos and eyes are added at expected ratios
|
||||
#ifndef UNIT_TESTS
|
||||
var/alive = 0
|
||||
var/cultplayers = 0
|
||||
for(var/I in GLOB.player_list)
|
||||
var/mob/M = I
|
||||
if(M.stat != DEAD)
|
||||
if(IS_CULTIST(M))
|
||||
++cultplayers
|
||||
else
|
||||
++alive
|
||||
|
||||
ASSERT(cultplayers) //we shouldn't be here.
|
||||
var/ratio = alive ? cultplayers/alive : 1
|
||||
if(ratio > CULT_RISEN && !cult_risen)
|
||||
for(var/datum/mind/mind as anything in members)
|
||||
if(mind.current)
|
||||
SEND_SOUND(mind.current, 'sound/hallucinations/i_see_you2.ogg')
|
||||
to_chat(mind.current, span_cultlarge(span_warning("The veil weakens as your cult grows, your eyes begin to glow...")))
|
||||
mind.current.AddElement(/datum/element/cult_eyes)
|
||||
cult_risen = TRUE
|
||||
log_game("The blood cult has risen with [cultplayers] players.")
|
||||
|
||||
if(ratio > CULT_ASCENDENT && !cult_ascendent)
|
||||
for(var/datum/mind/mind as anything in members)
|
||||
if(mind.current)
|
||||
SEND_SOUND(mind.current, 'sound/hallucinations/im_here1.ogg')
|
||||
to_chat(mind.current, span_cultlarge(span_warning("Your cult is ascendent and the red harvest approaches - you cannot hide your true nature for much longer!!")))
|
||||
mind.current.AddElement(/datum/element/cult_halo)
|
||||
cult_ascendent = TRUE
|
||||
log_game("The blood cult has ascended with [cultplayers] players.")
|
||||
#endif
|
||||
|
||||
/datum/team/cult/add_member(datum/mind/new_member)
|
||||
. = ..()
|
||||
// A little hacky, but this checks that cult ghosts don't contribute to the size at maximum value.
|
||||
if(is_unassigned_job(new_member.assigned_role))
|
||||
return
|
||||
size_at_maximum++
|
||||
|
||||
/datum/team/cult/proc/make_image(datum/objective/sacrifice/sac_objective)
|
||||
var/datum/job/job_of_sacrifice = sac_objective.target.assigned_role
|
||||
var/datum/preferences/prefs_of_sacrifice = sac_objective.target.current.client.prefs
|
||||
var/icon/reshape = get_flat_human_icon(null, job_of_sacrifice, prefs_of_sacrifice, list(SOUTH))
|
||||
reshape.Shift(SOUTH, 4)
|
||||
reshape.Shift(EAST, 1)
|
||||
reshape.Crop(7,4,26,31)
|
||||
reshape.Crop(-5,-3,26,30)
|
||||
sac_objective.sac_image = reshape
|
||||
|
||||
/datum/team/cult/proc/setup_objectives()
|
||||
var/datum/objective/sacrifice/sacrifice_objective = new
|
||||
sacrifice_objective.team = src
|
||||
sacrifice_objective.find_target()
|
||||
objectives += sacrifice_objective
|
||||
|
||||
var/datum/objective/eldergod/summon_objective = new
|
||||
summon_objective.team = src
|
||||
objectives += summon_objective
|
||||
|
||||
/datum/team/cult/proc/check_cult_victory()
|
||||
for(var/datum/objective/O in objectives)
|
||||
if(O.check_completion() == CULT_NARSIE_KILLED)
|
||||
return CULT_NARSIE_KILLED
|
||||
else if(!O.check_completion())
|
||||
return CULT_LOSS
|
||||
return CULT_VICTORY
|
||||
|
||||
/datum/team/cult/roundend_report()
|
||||
var/list/parts = list()
|
||||
var/victory = check_cult_victory()
|
||||
|
||||
if(victory == CULT_NARSIE_KILLED) // Epic failure, you summoned your god and then someone killed it.
|
||||
parts += "<span class='redtext big'>Nar'sie has been killed! The cult will haunt the universe no longer!</span>"
|
||||
else if(victory)
|
||||
parts += "<span class='greentext big'>The cult has succeeded! Nar'Sie has snuffed out another torch in the void!</span>"
|
||||
else
|
||||
parts += "<span class='redtext big'>The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!</span>"
|
||||
|
||||
if(objectives.len)
|
||||
parts += "<b>The cultists' objectives were:</b>"
|
||||
var/count = 1
|
||||
for(var/datum/objective/objective in objectives)
|
||||
if(objective.check_completion())
|
||||
parts += "<b>Objective #[count]</b>: [objective.explanation_text] [span_greentext("Success!")]"
|
||||
else
|
||||
parts += "<b>Objective #[count]</b>: [objective.explanation_text] [span_redtext("Fail.")]"
|
||||
count++
|
||||
|
||||
if(members.len)
|
||||
parts += "<span class='header'>The cultists were:</span>"
|
||||
if(length(true_cultists))
|
||||
parts += printplayerlist(true_cultists)
|
||||
else
|
||||
parts += printplayerlist(members)
|
||||
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
|
||||
/datum/team/cult/proc/is_sacrifice_target(datum/mind/mind)
|
||||
for(var/datum/objective/sacrifice/sac_objective in objectives)
|
||||
if(mind == sac_objective.target)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/// Sets a blood target for the cult.
|
||||
/datum/team/cult/proc/set_blood_target(atom/new_target, mob/marker, duration = 90 SECONDS)
|
||||
if(QDELETED(new_target))
|
||||
CRASH("A null or invalid target was passed to set_blood_target.")
|
||||
|
||||
if(duration != INFINITY && blood_target_reset_timer)
|
||||
return FALSE
|
||||
|
||||
deltimer(blood_target_reset_timer)
|
||||
blood_target = new_target
|
||||
RegisterSignal(blood_target, COMSIG_QDELETING, PROC_REF(unset_blood_target_and_timer))
|
||||
var/area/target_area = get_area(new_target)
|
||||
|
||||
blood_target_image = image('icons/effects/mouse_pointers/cult_target.dmi', new_target, "glow", ABOVE_MOB_LAYER)
|
||||
blood_target_image.appearance_flags = RESET_COLOR
|
||||
blood_target_image.pixel_x = -new_target.pixel_x
|
||||
blood_target_image.pixel_y = -new_target.pixel_y
|
||||
|
||||
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_cultlarge("[marker] has marked [blood_target] in the [target_area.name] as the cult's top priority, get there immediately!")))
|
||||
SEND_SOUND(cultist.current, sound(pick('sound/hallucinations/over_here2.ogg','sound/hallucinations/over_here3.ogg'), 0, 1, 75))
|
||||
cultist.current.client.images += blood_target_image
|
||||
|
||||
if(duration != INFINITY)
|
||||
blood_target_reset_timer = addtimer(CALLBACK(src, PROC_REF(unset_blood_target)), duration, TIMER_STOPPABLE)
|
||||
return TRUE
|
||||
|
||||
/// Unsets out blood target, clearing the images from all the cultists.
|
||||
/datum/team/cult/proc/unset_blood_target()
|
||||
blood_target_reset_timer = null
|
||||
|
||||
for(var/datum/mind/cultist as anything in members)
|
||||
if(!cultist.current)
|
||||
continue
|
||||
if(cultist.current.stat == DEAD || !cultist.current.client)
|
||||
continue
|
||||
|
||||
if(QDELETED(blood_target))
|
||||
to_chat(cultist.current, span_bold(span_cultlarge("The blood mark's target is lost!")))
|
||||
else
|
||||
to_chat(cultist.current, span_bold(span_cultlarge("The blood mark has expired!")))
|
||||
cultist.current.client.images -= blood_target_image
|
||||
|
||||
UnregisterSignal(blood_target, COMSIG_QDELETING)
|
||||
blood_target = null
|
||||
|
||||
QDEL_NULL(blood_target_image)
|
||||
|
||||
/// Unsets our blood target when they get deleted.
|
||||
/datum/team/cult/proc/unset_blood_target_and_timer(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
deltimer(blood_target_reset_timer)
|
||||
unset_blood_target()
|
||||
@@ -236,7 +236,7 @@ structure_check() searches for nearby cultist structures required for the invoca
|
||||
var/mob/living/F = invokers[1]
|
||||
var/datum/antagonist/cult/C = F.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
|
||||
var/datum/team/cult/Cult_team = C.cult_team
|
||||
var/is_convertable = is_convertable_to_cult(L,C.cult_team)
|
||||
var/is_convertable = is_convertable_to_cult(L, C.cult_team)
|
||||
if(L.stat != DEAD && is_convertable)
|
||||
invocation = "Mah'weyh pleggh at e'ntrath!"
|
||||
..()
|
||||
|
||||
@@ -22,14 +22,13 @@
|
||||
var/victory_message = "The revolution has overpowered the command staff! Viva la revolution! Execute any head of staff and security should you find them alive."
|
||||
|
||||
/datum/antagonist/rev/can_be_owned(datum/mind/new_owner)
|
||||
. = ..()
|
||||
if(.)
|
||||
if(new_owner.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
|
||||
return FALSE
|
||||
if(new_owner.unconvertable)
|
||||
return FALSE
|
||||
if(new_owner.current && HAS_TRAIT(new_owner.current, TRAIT_MINDSHIELD))
|
||||
return FALSE
|
||||
if(new_owner.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
|
||||
return FALSE
|
||||
if(new_owner.unconvertable)
|
||||
return FALSE
|
||||
if(new_owner.current && HAS_TRAIT(new_owner.current, TRAIT_MINDSHIELD))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/rev/admin_add(datum/mind/new_owner, mob/admin)
|
||||
// No revolution exists which means admin adding this will create a new revolution team
|
||||
|
||||
Reference in New Issue
Block a user