diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 6bc79fb7d5..ec78eb49b2 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -6,6 +6,7 @@ ) #define Z_TURFS(ZLEVEL) block(locate(1,1,ZLEVEL), locate(world.maxx, world.maxy, ZLEVEL)) +#define CULT_POLL_WAIT 2400 /proc/get_area(atom/A) if (!istype(A)) @@ -313,10 +314,10 @@ // Will return a list of active candidates. It increases the buffer 5 times until it finds a candidate which is active within the buffer. -/proc/get_candidates(be_special_type, afk_bracket = config.inactivity_period, jobbanType) +/proc/get_candidates(be_special_type, afk_bracket = config.inactivity_period, jobbanType) var/list/candidates = list() // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000)) - while(!candidates.len && afk_bracket < config.afk_period) + while(!candidates.len && afk_bracket < config.afk_period) for(var/mob/dead/observer/G in GLOB.player_list) if(G.client != null) if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) @@ -485,6 +486,66 @@ ++i return L +/proc/pollCultists(var/mob/living/Nominee) // Cult Master Poll + var/time_passed = world.time + var/list/yes_voters = new + var/list/cult_total = new + if(world.time < CULT_POLL_WAIT) + Nominee << "It would be premature to select a leader while everyone is still settling in, try again in [round((CULT_POLL_WAIT-world.time)/10)] seconds" + return + for(var/datum/mind/B in SSticker.mode.cult) + var/mob/living/M = B.current + if(!M.incapacitated()) + M << 'sound/hallucinations/im_here1.ogg' + M.verbs -= /mob/living/proc/cult_master + to_chat(M, "Acolyte [Nominee] has asserted that they are worthy of leading the cult. A vote will be called shortly.") + sleep(250) + for(var/datum/mind/B in SSticker.mode.cult) + var/mob/living/M = B.current + if(!M.incapacitated()) + M << 'sound/magic/exit_blood.ogg' + switch(askuser(M,"[Nominee] seeks to lead your cult, do you support them?","Please answer in 20 seconds!","Yes","No","Abstain", StealFocus=0, Timeout=200)) + if(1) + if((world.time-time_passed)>500) + to_chat(M, "Sorry, your vote came too late!") + M << 'sound/machines/buzz-sigh.ogg' + else + M << "Choice registered: Yes." + yes_voters += M + cult_total += M + if(2) + if((world.time-time_passed)>500) + to_chat(M, "Sorry, your vote came too late!") + M << 'sound/machines/buzz-sigh.ogg' + else + to_chat(M, "Choice registered: No.") + cult_total += M + if(3) + to_chat(M, "Choice registered: Abstain.") + sleep(300) + if(yes_voters.len > (cult_total.len/2)) + var/datum/action/innate/cultmast/finalreck/FinalReckoning = new() + var/datum/action/innate/cultmast/cultmark/Mark = new() + FinalReckoning.Grant(Nominee) + Mark.Grant(Nominee) + Nominee.mind.special_role = "Cult Master" + Nominee.update_action_buttons_icon() + Nominee.apply_status_effect(/datum/status_effect/cult_master) + SSticker.mode.set_antag_hud(Nominee,"cultmaster") + GLOB.cult_mastered = TRUE + for(var/datum/mind/B in SSticker.mode.cult) + if(!B.current.incapacitated()) + to_chat(B.current,"[Nominee] has the cult's support and is now their master. Follow their orders to the best of your ability!") + return TRUE + else + for(var/datum/mind/B in SSticker.mode.cult) + if(!B.current.incapacitated()) + to_chat(B.current, "[Nominee] could not win the cult's support and shall continue to serve as an acolyte.") + B.current.verbs += /mob/living/proc/cult_master + return FALSE + +/proc/poll_helper(var/mob/living/M) + /proc/makeBody(mob/dead/observer/G_found) // Uses stripped down and bastardized code from respawn character if(!G_found || !G_found.key) return @@ -513,7 +574,7 @@ winset(C, "mainwindow", "flash=5") /proc/AnnounceArrival(var/mob/living/carbon/human/character, var/rank) - if(!SSticker.IsRoundInProgress() || !character) + if(!SSticker.IsRoundInProgress() || !character) return var/area/A = get_area(character) var/message = "\ diff --git a/code/__HELPERS/game.dm.rej b/code/__HELPERS/game.dm.rej new file mode 100644 index 0000000000..d64ec0418b --- /dev/null +++ b/code/__HELPERS/game.dm.rej @@ -0,0 +1,66 @@ +diff a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm (rejected hunks) +@@ -489,41 +490,38 @@ + var/time_passed = world.time + var/list/yes_voters = new + var/list/cult_total = new +- if(world.time<1800) +- Nominee << "It would be premature to select a leader while everyone is still settling in, try again in [round((1800-world.time)/10)] seconds" ++ if(world.time < CULT_POLL_WAIT) ++ Nominee << "It would be premature to select a leader while everyone is still settling in, try again in [round((CULT_POLL_WAIT-world.time)/10)] seconds" + return + for(var/datum/mind/B in SSticker.mode.cult) +- if(!isliving(B.current)) +- continue + var/mob/living/M = B.current +- if(isliving(M)) ++ if(!M.incapacitated()) + M << 'sound/hallucinations/im_here1.ogg' + M.verbs -= /mob/living/proc/cult_master + to_chat(M, "Acolyte [Nominee] has asserted that they are worthy of leading the cult. A vote will be called shortly.") + sleep(250) + for(var/datum/mind/B in SSticker.mode.cult) +- if(!isliving(B.current)) +- continue + var/mob/living/M = B.current +- M << 'sound/magic/exit_blood.ogg' +- switch(askuser(M,"[Nominee] seeks to lead your cult, do you support them?","Please answer in 20 seconds!","Yes","No","Abstain", StealFocus=0, Timeout=200)) +- if(1) +- if((world.time-time_passed)>500) +- M << "Sorry, your vote came too late!" +- M << 'sound/machines/buzz-sigh.ogg' +- else +- M << "Choice registered: Yes." +- yes_voters += M +- cult_total += M +- if(2) +- if((world.time-time_passed)>500) +- M << "Sorry, your vote came too late!" +- M << 'sound/machines/buzz-sigh.ogg' +- else +- M << "Choice registered: No." +- cult_total += M +- if(3) +- M << "Choice registered: Abstain." ++ if(!M.incapacitated()) ++ M << 'sound/magic/exit_blood.ogg' ++ switch(askuser(M,"[Nominee] seeks to lead your cult, do you support them?","Please answer in 20 seconds!","Yes","No","Abstain", StealFocus=0, Timeout=200)) ++ if(1) ++ if((world.time-time_passed)>500) ++ to_chat(M, "Sorry, your vote came too late!") ++ M << 'sound/machines/buzz-sigh.ogg' ++ else ++ M << "Choice registered: Yes." ++ yes_voters += M ++ cult_total += M ++ if(2) ++ if((world.time-time_passed)>500) ++ to_chat(M, "Sorry, your vote came too late!") ++ M << 'sound/machines/buzz-sigh.ogg' ++ else ++ to_chat(M, "Choice registered: No.") ++ cult_total += M ++ if(3) ++ to_chat(M, "Choice registered: Abstain.") + sleep(300) + if(yes_voters.len > (cult_total.len/2)) + var/datum/action/innate/cultmast/finalreck/FinalReckoning = new() diff --git a/code/__HELPERS/matrices.dm b/code/__HELPERS/matrices.dm index 2e34624df5..8b77b170e0 100644 --- a/code/__HELPERS/matrices.dm +++ b/code/__HELPERS/matrices.dm @@ -24,7 +24,6 @@ //doesn't have an object argument because this is "Stacking" with the animate call above //3 billion% intentional - //Dumps the matrix data in format a-f /matrix/proc/tolist() . = list() diff --git a/code/_globalvars/game_modes.dm b/code/_globalvars/game_modes.dm index 153ebbf089..912ae78a04 100644 --- a/code/_globalvars/game_modes.dm +++ b/code/_globalvars/game_modes.dm @@ -3,3 +3,10 @@ GLOBAL_VAR_INIT(secret_force_mode, "secret") // if this is anything but "secret" GLOBAL_VAR_INIT(wavesecret, 0) // meteor mode, delays wave progression, terrible name GLOBAL_DATUM(start_state, /datum/station_state) // Used in round-end report + +// Cult, needs to be global so admin cultists are functional +GLOBAL_VAR_INIT(blood_target, null) // Cult Master's target or Construct's Master +GLOBAL_DATUM(sac_mind, /datum/mind) +GLOBAL_VAR_INIT(sac_image, null) +GLOBAL_VAR_INIT(cult_mastered, FALSE) +GLOBAL_VAR_INIT(sac_complete, FALSE) \ No newline at end of file diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index b376706a42..b95750009f 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -42,6 +42,7 @@ thealert.override_alerts = override if(override) thealert.timeout = null + thealert.mob_viewer = src if(new_master) var/old_layer = new_master.layer @@ -96,6 +97,7 @@ var/severity = 0 var/alerttooltipstyle = "" var/override_alerts = FALSE //If it is overriding other alerts of the same type + var/mob/mob_viewer //the mob viewing this alert /obj/screen/alert/MouseEntered(location,control,params) @@ -256,6 +258,103 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." icon_state = "blobbernaut_nofactory" alerttooltipstyle = "blob" +// BLOODCULT + +/obj/screen/alert/bloodsense + name = "Blood Sense" + desc = "Allows you to sense blood that is manipulated by dark magicks." + icon_state = "cult_sense" + alerttooltipstyle = "cult" + var/image/sacimage + var/angle = 0 + var/mob/living/simple_animal/hostile/construct/Cviewer = null + +/obj/screen/alert/bloodsense/Initialize() + ..() + sacimage = GLOB.sac_image + START_PROCESSING(SSprocessing, src) + +/obj/screen/alert/bloodsense/Destroy() + sacimage = null + Cviewer = null + STOP_PROCESSING(SSprocessing, src) + return ..() + +/obj/screen/alert/bloodsense/process() + var/atom/blood_target + if(GLOB.blood_target) + if(!get_turf(GLOB.blood_target)) + GLOB.blood_target = null + else + blood_target = GLOB.blood_target + if(Cviewer) + if(Cviewer.seeking && Cviewer.master) + blood_target = Cviewer.master + if(!blood_target && !GLOB.sac_complete) + if(icon_state == "runed_sense0") + return + animate(src, transform = null, time = 1, loop = 0) + angle = 0 + cut_overlays() + icon_state = "runed_sense0" + desc = "Nar-Sie demands that [GLOB.sac_mind] be sacrificed before the summoning ritual can begin." + add_overlay(sacimage) + return + if(!blood_target && GLOB.sac_complete) + if(icon_state == "runed_sense1") + return + animate(src, transform = null, time = 1, loop = 0) + angle = 0 + cut_overlays() + icon_state = "runed_sense1" + var/image/narnar = new('icons/mob/screen_alert.dmi', "mini_nar") + desc = "The sacrifice is complete, prepare to summon Nar-Sie!" + add_overlay(narnar) + return + if(!blood_target) + return + var/turf/P = get_turf(blood_target) + var/turf/Q = get_turf(mob_viewer) + var/area/A = get_area(P) + if(P.z != Q.z) //The target is on a different Z level, we cannot sense that far. + return + desc = "You are currently tracking [blood_target] in [A.name]" + var/target_angle = Get_Angle(mob_viewer, blood_target) + var/target_dist = get_dist(P, Q) + cut_overlays() + switch(target_dist) + if(0 to 1) + icon_state = "runed_sense2" + if(2 to 8) + icon_state = "arrow8" + if(9 to 15) + icon_state = "arrow7" + if(16 to 22) + icon_state = "arrow6" + if(23 to 29) + icon_state = "arrow5" + if(30 to 36) + icon_state = "arrow4" + if(37 to 43) + icon_state = "arrow3" + if(44 to 50) + icon_state = "arrow2" + if(51 to 57) + icon_state = "arrow1" + if(58 to 64) + icon_state = "arrow0" + if(65 to 400) + icon_state = "arrow" + var/difference = target_angle - angle + angle = target_angle + if(!difference) + return + var/matrix/final = matrix(transform) + final.Turn(difference) + animate(src, transform = final, time = 5, loop = 0) + + + // CLOCKCULT /obj/screen/alert/clockwork alerttooltipstyle = "clockcult" diff --git a/code/datums/antagonists/datum_cult.dm b/code/datums/antagonists/datum_cult.dm index 30c0aa8450..59f165ff37 100644 --- a/code/datums/antagonists/datum_cult.dm +++ b/code/datums/antagonists/datum_cult.dm @@ -5,6 +5,51 @@ qdel(communion) return ..() +/datum/antagonist/cult/proc/add_objectives() + var/list/target_candidates = list() + for(var/mob/living/carbon/human/player in GLOB.player_list) + if(player.mind && !is_convertable_to_cult(player) && (player != owner) && isliving(player)) + target_candidates += player.mind + if(target_candidates.len == 0) + message_admins("Cult Sacrifice: Could not find unconvertable target, checking for convertable target.") + for(var/mob/living/carbon/human/player in GLOB.player_list) + if(player.mind && (player != owner) && isliving(player)) + target_candidates += player.mind + if(target_candidates.len > 0) + GLOB.sac_mind = pick(target_candidates) + if(!GLOB.sac_mind) + message_admins("Cult Sacrifice: ERROR - Null target chosen!") + else + var/datum/job/sacjob = SSjob.GetJob(GLOB.sac_mind.assigned_role) + var/datum/preferences/sacface = GLOB.sac_mind.current.client.prefs + var/icon/reshape = get_flat_human_icon(null, sacjob, sacface) + reshape.Shift(SOUTH, 4) + reshape.Shift(EAST, 1) + reshape.Crop(7,4,26,31) + reshape.Crop(-5,-3,26,30) + GLOB.sac_image = reshape + else + message_admins("Cult Sacrifice: Could not find unconvertable or convertable target. WELP!") + GLOB.sac_complete = TRUE + SSticker.mode.cult_objectives += "sacrifice" + SSticker.mode.cult_objectives += "eldergod" + on_gain() + +/datum/antagonist/cult/proc/cult_memorization(datum/mind/cult_mind) + var/mob/living/current = cult_mind.current + for(var/obj_count = 1,obj_count <= SSticker.mode.cult_objectives.len,obj_count++) + var/explanation + switch(SSticker.mode.cult_objectives[obj_count]) + if("sacrifice") + if(GLOB.sac_mind) + explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role] via invoking a Sacrifice rune with them on it and three acolytes around it." + else + explanation = "The veil has already been weakened here, proceed to the final objective." + if("eldergod") + explanation = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie' with nine acolytes on it. You must do this after sacrificing your target." + to_chat(current, "Objective #[obj_count]: [explanation]") + cult_mind.memory += "Objective #[obj_count]: [explanation]
" + /datum/antagonist/cult/can_be_owned(datum/mind/new_owner) . = ..() if(.) @@ -12,14 +57,17 @@ /datum/antagonist/cult/on_gain() . = ..() - SSticker.mode.cult += owner + var/mob/living/current = owner.current + if(SSticker.mode.cult_objectives.len == 0) + add_objectives() + return + SSticker.mode.cult += owner // Only add after they've been given objectives + cult_memorization(owner) + if(jobban_isbanned(current, ROLE_CULTIST)) + addtimer(CALLBACK(SSticker.mode, /datum/game_mode.proc/replace_jobbaned_player, current, ROLE_CULTIST, ROLE_CULTIST), 0) SSticker.mode.update_cult_icons_added(owner) - if(istype(SSticker.mode, /datum/game_mode/cult)) - var/datum/game_mode/cult/C = SSticker.mode - C.memorize_cult_objectives(owner) - if(jobban_isbanned(owner.current, ROLE_CULTIST)) - addtimer(CALLBACK(SSticker.mode, /datum/game_mode.proc/replace_jobbaned_player, owner.current, ROLE_CULTIST, ROLE_CULTIST), 0) - owner.current.log_message("Has been converted to the cult of Nar'Sie!", INDIVIDUAL_ATTACK_LOG) + current.throw_alert("bloodsense", /obj/screen/alert/bloodsense) + current.log_message("Has been converted to the cult of Nar'Sie!", INDIVIDUAL_ATTACK_LOG) /datum/antagonist/cult/apply_innate_effects(mob/living/mob_override) . = ..() @@ -28,6 +76,8 @@ current = mob_override current.faction |= "cult" current.verbs += /mob/living/proc/cult_help + if(!GLOB.cult_mastered) + current.verbs += /mob/living/proc/cult_master communion.Grant(current) /datum/antagonist/cult/remove_innate_effects(mob/living/mob_override) @@ -38,6 +88,10 @@ current.faction -= "cult" current.verbs -= /mob/living/proc/cult_help communion.Remove(current) + owner.current.verbs -= /mob/living/proc/cult_master + for(var/datum/action/innate/cultmast/H in owner.current.actions) + qdel(H) + current.clear_alert("bloodsense") /datum/antagonist/cult/on_removal() owner.wipe_memory() diff --git a/code/datums/antagonists/datum_cult.dm.rej b/code/datums/antagonists/datum_cult.dm.rej new file mode 100644 index 0000000000..137565bd5f --- /dev/null +++ b/code/datums/antagonists/datum_cult.dm.rej @@ -0,0 +1,9 @@ +diff a/code/datums/antagonists/datum_cult.dm b/code/datums/antagonists/datum_cult.dm (rejected hunks) +@@ -65,6 +65,7 @@ + cult_memorization(owner) + if(jobban_isbanned(current, ROLE_CULTIST)) + addtimer(CALLBACK(SSticker.mode, /datum/game_mode.proc/replace_jobbaned_player, current, ROLE_CULTIST, ROLE_CULTIST), 0) ++ SSticker.mode.update_cult_icons_added(owner) + current.throw_alert("bloodsense", /obj/screen/alert/bloodsense) + current.log_message("Has been converted to the cult of Nar'Sie!", INDIVIDUAL_ATTACK_LOG) + diff --git a/code/datums/mind.dm b/code/datums/mind.dm index f4eff12f38..aee8b80c31 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -52,7 +52,7 @@ var/linglink var/miming = 0 // Mime's vow of silence - var/list/antag_datums + var/list/antag_datums var/antag_hud_icon_state = null //this mind's ANTAG_HUD should have this icon_state var/datum/atom_hud/antag/antag_hud = null //this mind's antag HUD var/datum/gang/gang_datum //Which gang this mind belongs to, if any @@ -69,10 +69,10 @@ /datum/mind/Destroy() SSticker.minds -= src - if(islist(antag_datums)) - for(var/i in antag_datums) - qdel(i) - antag_datums = null + if(islist(antag_datums)) + for(var/i in antag_datums) + qdel(i) + antag_datums = null return ..() /datum/mind/proc/transfer_to(mob/new_character, var/force_key_move = 0) @@ -90,12 +90,12 @@ new_character.mind.current = null var/datum/atom_hud/antag/hud_to_transfer = antag_hud//we need this because leave_hud() will clear this list - var/mob/living/old_current = current + var/mob/living/old_current = current current = new_character //associate ourself with our new body new_character.mind = src //and associate our new body with ourself - for(var/a in antag_datums) //Makes sure all antag datums effects are applied in the new body - var/datum/antagonist/A = a - A.on_body_transfer(old_current, current) + for(var/a in antag_datums) //Makes sure all antag datums effects are applied in the new body + var/datum/antagonist/A = a + A.on_body_transfer(old_current, current) if(iscarbon(new_character)) var/mob/living/carbon/C = new_character C.last_mind = src @@ -111,42 +111,42 @@ /datum/mind/proc/wipe_memory() memory = null -// Datum antag mind procs -/datum/mind/proc/add_antag_datum(datum_type) - if(!datum_type) - return - var/datum/antagonist/A = new datum_type(src) - if(!A.can_be_owned(src)) - qdel(A) - return - LAZYADD(antag_datums, A) - A.on_gain() - return A - -/datum/mind/proc/remove_antag_datum(datum_type) - if(!datum_type) - return - var/datum/antagonist/A = has_antag_datum(datum_type) - if(A) - A.on_removal() - return TRUE - -/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us. - for(var/a in antag_datums) - var/datum/antagonist/A = a - A.on_removal() - -/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE) - if(!datum_type) - return - . = FALSE - for(var/a in antag_datums) - var/datum/antagonist/A = a - if(check_subtypes && istype(A, datum_type)) - return A - else if(A.type == datum_type) - return A - +// Datum antag mind procs +/datum/mind/proc/add_antag_datum(datum_type) + if(!datum_type) + return + var/datum/antagonist/A = new datum_type(src) + if(!A.can_be_owned(src)) + qdel(A) + return + LAZYADD(antag_datums, A) + A.on_gain() + return A + +/datum/mind/proc/remove_antag_datum(datum_type) + if(!datum_type) + return + var/datum/antagonist/A = has_antag_datum(datum_type) + if(A) + A.on_removal() + return TRUE + +/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us. + for(var/a in antag_datums) + var/datum/antagonist/A = a + A.on_removal() + +/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE) + if(!datum_type) + return + . = FALSE + for(var/a in antag_datums) + var/datum/antagonist/A = a + if(check_subtypes && istype(A, datum_type)) + return A + else if(A.type == datum_type) + return A + /* Removes antag type's references from a mind. objectives, uplinks, powers etc are all handled. @@ -293,7 +293,7 @@ to_chat(recipient, "[output]") /datum/mind/proc/edit_memory() - if(!SSticker.HasRoundStarted()) + if(!SSticker.HasRoundStarted()) alert("Not before round-start!", "Alert") return @@ -1181,7 +1181,7 @@ log_admin("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") return if(!ishuman(current) && !iscyborg(current)) - to_chat(usr, "This only works on humans and cyborgs!") + to_chat(usr, "This only works on humans and cyborgs!") return SSticker.mode.devils += src special_role = "devil" @@ -1230,17 +1230,17 @@ log_admin("[key_name(usr)] turned [current] into abductor.") SSticker.mode.update_abductor_icons_added(src) if("equip") - if(!ishuman(current)) - to_chat(usr, "This only works on humans!") - return - - var/mob/living/carbon/human/H = current + if(!ishuman(current)) + to_chat(usr, "This only works on humans!") + return + + var/mob/living/carbon/human/H = current var/gear = alert("Agent or Scientist Gear","Gear","Agent","Scientist") if(gear) if(gear=="Agent") - H.equipOutfit(/datum/outfit/abductor/agent) + H.equipOutfit(/datum/outfit/abductor/agent) else - H.equipOutfit(/datum/outfit/abductor/scientist) + H.equipOutfit(/datum/outfit/abductor/scientist) else if (href_list["monkey"]) var/mob/living/L = current @@ -1437,15 +1437,8 @@ special_role = "Cultist" to_chat(current, "You catch a glimpse of the Realm of Nar-Sie, The Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of Nar-Sie.") to_chat(current, "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.") - var/datum/game_mode/cult/cult = SSticker.mode - - if (istype(cult)) - cult.memorize_cult_objectives(src) - else - var/explanation = "Summon Nar-Sie via the use of the appropriate rune (Hell join self). It will only work if nine cultists stand on and around it." - to_chat(current, "Objective #1: [explanation]") - memory += "Objective #1: [explanation]
" - + var/datum/antagonist/cult/C + C.cult_memorization(src) var/mob/living/carbon/human/H = current if (!SSticker.mode.equip_cultist(current)) to_chat(H, "Spawning an amulet from your Master failed.") @@ -1509,8 +1502,8 @@ H.set_species(/datum/species/abductor) var/datum/species/abductor/S = H.dna.species - if(role == "Scientist") - S.scientist = TRUE + if(role == "Scientist") + S.scientist = TRUE S.team = team var/list/obj/effect/landmark/abductor/agent_landmarks = new @@ -1529,8 +1522,8 @@ if("Agent") L = agent_landmarks[team] if("Scientist") - L = scientist_landmarks[team] - H.forceMove(L.loc) + L = scientist_landmarks[team] + H.forceMove(L.loc) /datum/mind/proc/AddSpell(obj/effect/proc_holder/spell/S) spell_list += S diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index 7b00a17e41..a0338f72b0 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -228,3 +228,32 @@ name = "Wish Granter's Immortality" desc = "You are being resurrected!" icon_state = "wish_granter" + +/datum/status_effect/cult_master + id = "The Cult Master" + duration = -1 + tick_interval = 100 + alert_type = null + var/alive = TRUE + +/datum/status_effect/cult_master/proc/deathrattle() + var/area/A = get_area(owner) + for(var/datum/mind/B in SSticker.mode.cult) + if(isliving(B.current)) + var/mob/living/M = B.current + M << 'sound/hallucinations/veryfar_noise.ogg' + to_chat(M, "The Cult's Master, [owner], has fallen in the [A]!") + + +/datum/status_effect/cult_master/tick() + if(owner.stat != DEAD && !alive) + alive = TRUE + return + if(owner.stat == DEAD && alive) + alive = FALSE + deathrattle() + +/datum/status_effect/cult_master/on_remove() + deathrattle() + . = ..() + diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index 7780ae5db9..3b30d3e44a 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -1,34 +1,31 @@ - - /datum/game_mode var/list/datum/mind/cult = list() var/list/cult_objectives = list() + var/eldergod = 1 //for the summon god objective /proc/iscultist(mob/living/M) return istype(M) && M.mind && M.mind.has_antag_datum(ANTAG_DATUM_CULT) /proc/is_sacrifice_target(datum/mind/mind) - if(SSticker.mode.name == "cult") - var/datum/game_mode/cult/cult_mode = SSticker.mode - if(mind == cult_mode.sacrifice_target) - return 1 - return 0 + if(mind == GLOB.sac_mind) + return TRUE + return FALSE /proc/is_convertable_to_cult(mob/living/M) if(!istype(M)) - return 0 + return FALSE if(M.mind) if(ishuman(M) && (M.mind.assigned_role in list("Captain", "Chaplain"))) - return 0 + return FALSE if(is_sacrifice_target(M.mind)) - return 0 + return FALSE if(M.mind.enslaved_to && !iscultist(M.mind.enslaved_to)) - return 0 + return FALSE else - return 0 + return FALSE if(M.isloyal() || issilicon(M) || isbot(M) || isdrone(M) || is_servant_of_ratvar(M)) - return 0 //can't convert machines, shielded, or ratvar's dogs - return 1 + return FALSE //can't convert machines, shielded, or ratvar's dogs + return TRUE /datum/game_mode/cult name = "cult" @@ -47,14 +44,13 @@ Crew: Prevent the cult from expanding and drive it out." var/finished = 0 - var/eldergod = 1 //for the summon god objective var/acolytes_needed = 10 //for the survive objective var/acolytes_survived = 0 - var/datum/mind/sacrifice_target = null//The target to be sacrificed var/list/cultists_to_cult = list() //the cultists we'll convert + /datum/game_mode/cult/pre_setup() cult_objectives += "sacrifice" cult_objectives += "eldergod" @@ -82,22 +78,6 @@ return (cultists_to_cult.len>=required_enemies) -/datum/game_mode/cult/proc/memorize_cult_objectives(datum/mind/cult_mind) - for(var/obj_count = 1,obj_count <= cult_objectives.len,obj_count++) - var/explanation - switch(cult_objectives[obj_count]) - if("survive") - explanation = "Our knowledge must live on. Make sure at least [acolytes_needed] acolytes escape on the shuttle to spread their work on an another station." - if("sacrifice") - if(sacrifice_target) - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role] via invoking a Sacrifice rune with them on it and three acolytes around it." - else - explanation = "Free objective." - if("eldergod") - explanation = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie' with nine acolytes on it. You must do this after sacrificing your target." - to_chat(cult_mind.current, "Objective #[obj_count]: [explanation]") - cult_mind.memory += "Objective #[obj_count]: [explanation]
" - /datum/game_mode/cult/post_setup() modePlayer += cultists_to_cult if("sacrifice" in cult_objectives) @@ -108,9 +88,18 @@ if(player.mind && !(player.mind in cultists_to_cult)) possible_targets += player.mind if(possible_targets.len > 0) - sacrifice_target = pick(possible_targets) - if(!sacrifice_target) + GLOB.sac_mind = pick(possible_targets) + if(!GLOB.sac_mind) message_admins("Cult Sacrifice: ERROR - Null target chosen!") + else + var/datum/job/sacjob = SSjob.GetJob(GLOB.sac_mind.assigned_role) + var/datum/preferences/sacface = GLOB.sac_mind.current.client.prefs + var/icon/reshape = get_flat_human_icon(null, sacjob, sacface) + reshape.Shift(SOUTH, 4) + reshape.Shift(EAST, 1) + reshape.Crop(7,4,26,31) + reshape.Crop(-5,-3,26,30) + GLOB.sac_image = reshape else message_admins("Cult Sacrifice: Could not find unconvertable or convertable target. WELP!") for(var/datum/mind/cult_mind in cultists_to_cult) @@ -198,7 +187,7 @@ if(cult_objectives.Find("eldergod")) cult_fail += eldergod //1 by default, 0 if the elder god has been summoned at least once if(cult_objectives.Find("sacrifice")) - if(sacrifice_target && !GLOB.sacrificed.Find(sacrifice_target)) //if the target has been GLOB.sacrificed, ignore this step. otherwise, add 1 to cult_fail + if(GLOB.sac_mind && GLOB.sac_complete) //if the target has been GLOB.sacrificed, ignore this step. otherwise, add 1 to cult_fail cult_fail++ return cult_fail //if any objectives aren't met, failure @@ -243,16 +232,12 @@ SSblackbox.add_details("cult_objective","cult_survive|FAIL|[acolytes_needed]") SSticker.news_report = CULT_FAILURE if("sacrifice") - if(sacrifice_target) - if(sacrifice_target in GLOB.sacrificed) - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Success!" - SSblackbox.add_details("cult_objective","cult_sacrifice|SUCCESS") - else if(sacrifice_target && sacrifice_target.current) - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail." - SSblackbox.add_details("cult_objective","cult_sacrifice|FAIL") - else - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail (Gibbed)." - SSblackbox.add_details("cult_objective","cult_sacrifice|FAIL|GIBBED") + if(GLOB.sac_complete) + explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Success!" + SSblackbox.add_details("cult_objective","cult_sacrifice|SUCCESS") + else + explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Fail." + SSblackbox.add_details("cult_objective","cult_sacrifice|FAIL") if("eldergod") if(!eldergod) explanation = "Summon Nar-Sie. Success!" @@ -269,12 +254,45 @@ return 1 -/datum/game_mode/proc/auto_declare_completion_cult() - if( cult.len || (SSticker && istype(SSticker.mode,/datum/game_mode/cult)) ) - var/text = "
The cultists were:" - for(var/datum/mind/cultist in cult) - text += printplayer(cultist) - - text += "
" - - to_chat(world, text) +/datum/game_mode/proc/datum_cult_completion() + var/text = "" + var/acolytes_survived = 0 + for(var/datum/mind/cult_mind in cult) + if (cult_mind.current && cult_mind.current.stat != DEAD) + if(cult_mind.current.onCentcom() || cult_mind.current.onSyndieBase()) + acolytes_survived++ + var/cult_fail = 0 + cult_fail += eldergod + if(!GLOB.sac_complete) + cult_fail++ + if(!cult_fail) + SSblackbox.set_details("round_end_result","win - cult win") + SSblackbox.set_val("round_end_result",acolytes_survived) + to_chat(world, "The cult has succeeded! Nar-sie has snuffed out another torch in the void!") + else + SSblackbox.set_details("round_end_result","loss - staff stopped the cult") + SSblackbox.set_val("round_end_result",acolytes_survived) + to_chat(world, "The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!") + if(cult_objectives.len) + text += "
The cultists' objectives were:" + for(var/obj_count in 1 to 2) + var/explanation + switch(cult_objectives[obj_count]) + if("sacrifice") + if(GLOB.sac_complete) + explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Success!" + SSblackbox.add_details("cult_objective","cult_sacrifice|SUCCESS") + else + explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Fail." + SSblackbox.add_details("cult_objective","cult_sacrifice|FAIL") + if("eldergod") + if(!eldergod) + explanation = "Summon Nar-Sie. Success!" + SSblackbox.add_details("cult_objective","cult_narsie|SUCCESS") + SSticker.news_report = CULT_SUMMON + else + explanation = "Summon Nar-Sie. Fail." + SSblackbox.add_details("cult_objective","cult_narsie|FAIL") + SSticker.news_report = CULT_FAILURE + text += "
Objective #[obj_count]: [explanation]" + to_chat(world, text) diff --git a/code/game/gamemodes/cult/cult.dm.rej b/code/game/gamemodes/cult/cult.dm.rej new file mode 100644 index 0000000000..fb9b30c910 --- /dev/null +++ b/code/game/gamemodes/cult/cult.dm.rej @@ -0,0 +1,33 @@ +diff a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm (rejected hunks) +@@ -4,7 +4,7 @@ + var/eldergod = 1 //for the summon god objective + + /proc/iscultist(mob/living/M) +- return istype(M) && M.has_antag_datum(/datum/antagonist/cultist, TRUE) ++ return istype(M) && M.mind && M.mind.has_antag_datum(ANTAG_DATUM_CULT) + + /proc/is_sacrifice_target(datum/mind/mind) + if(mind == GLOB.sac_mind) +@@ -163,18 +163,18 @@ + /datum/game_mode/proc/add_cultist(datum/mind/cult_mind, stun) //BASE + if (!istype(cult_mind)) + return 0 +- if(cult_mind.current.gain_antag_datum(/datum/antagonist/cultist)) ++ if(cult_mind.add_antag_datum(ANTAG_DATUM_CULT)) + if(stun) + cult_mind.current.Paralyse(5) + return 1 + + /datum/game_mode/proc/remove_cultist(datum/mind/cult_mind, show_message = 1, stun) + if(cult_mind.current) +- var/datum/antagonist/cultist/cult_datum = cult_mind.current.has_antag_datum(/datum/antagonist/cultist, TRUE) ++ var/datum/antagonist/cult/cult_datum = cult_mind.has_antag_datum(ANTAG_DATUM_CULT) + if(!cult_datum) + return FALSE +- cult_datum.silent_update = show_message +- cult_datum.on_remove() ++ cult_datum.silent = show_message ++ cult_datum.on_removal() + if(stun) + cult_mind.current.Paralyse(5) + return TRUE diff --git a/code/game/gamemodes/cult/cult_comms.dm b/code/game/gamemodes/cult/cult_comms.dm index 40c7bbece8..ff8ee97de7 100644 --- a/code/game/gamemodes/cult/cult_comms.dm +++ b/code/game/gamemodes/cult/cult_comms.dm @@ -1,3 +1,5 @@ +// Contains cult communion, guide, and cult master abilities +#define MARK_COOLDOWN /datum/action/innate/cultcomm name = "Communion" @@ -19,11 +21,15 @@ cultist_commune(usr, input) /proc/cultist_commune(mob/living/user, message) + var/my_message if(!message) return user.whisper("O bidai nabora se[pick("'","`")]sma!") user.whisper(html_decode(message)) - var/my_message = "[(ishuman(user) ? "Acolyte" : "Construct")] [findtextEx(user.name, user.real_name) ? user.name : "[user.real_name] (as [user.name])"]: [message]" + if (user.mind.special_role == "Cult Master") + my_message = "[(ishuman(user) ? "Master" : "Lord")] [findtextEx(user.name, user.real_name) ? user.name : "[user.real_name] (as [user.name])"]: [message]" + else + my_message = "[(ishuman(user) ? "Acolyte" : "Construct")] [findtextEx(user.name, user.real_name) ? user.name : "[user.real_name] (as [user.name])"]: [message]" for(var/mob/M in GLOB.mob_list) if(iscultist(M)) to_chat(M, my_message) @@ -65,3 +71,149 @@ popup.set_content(text) popup.open() return 1 + +/mob/living/proc/cult_master() + set category = "Cultist" + set name = "Assert Leadership" + pollCultists(src) // This proc handles the distribution of cult master actions + +/datum/action/innate/cultmast + background_icon_state = "bg_demon" + buttontooltipstyle = "cult" + check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_CONSCIOUS + +/datum/action/innate/cultmast/IsAvailable() + if(owner.mind.special_role != "Cult Master") + return 0 + return ..() + +/datum/action/innate/cultmast/finalreck + name = "Final Reckoning" + desc = "A single-use spell that brings the entire cult to the master's location" + button_icon_state = "sintouch" + +/datum/action/innate/cultmast/finalreck/Activate() + var/list/destinations = list() + for(var/turf/T in orange(1,owner)) + if(istype(T, /turf/open)) + destinations += T + for(var/i in 1 to 4) + owner.chant(i) + if(do_after(owner, 30, target = owner)) + for(var/datum/mind/B in SSticker.mode.cult) + var/mob/living/M = B.current + if(isliving(M) && M.stat != DEAD) + var/turf/mobloc = get_turf(M) + switch(i) + if (1) + new /obj/effect/overlay/temp/cult/sparks(mobloc, M.dir) + playsound(mobloc, "sparks", 50, 1) + if (2) + new /obj/effect/overlay/temp/dir_setting/cult/phase/out(mobloc, M.dir) + playsound(mobloc, "sparks", 75, 1) + if (3) + new /obj/effect/overlay/temp/dir_setting/cult/phase(mobloc, M.dir) + playsound(mobloc, "sparks", 100, 1) + if (4) + playsound(mobloc, 'sound/magic/exit_blood.ogg', 100, 1) + if(M != owner) + var/turf/final = pick(destinations) + new /obj/effect/overlay/temp/cult/blood(final) + addtimer(CALLBACK(M, /mob/.proc/reckon, final), 10) + else + for(var/datum/action/innate/cultmast/finalreck/H in owner.actions) + qdel(H) + else + return + +/mob/proc/reckon(var/turf/final) + new /obj/effect/overlay/temp/cult/blood/out(get_turf(src)) + forceMove(final) + +/mob/proc/chant(var/i) + switch(i) + if (1) + say("C'arta Forbici!") + if (2) + say("Pleggh E'ntrath!") + playsound(get_turf(src),'sound/magic/clockwork/narsie_attack.ogg', 50, 1) + if (3) + say("Barhah hra Zar'garis!") + playsound(get_turf(src),'sound/magic/clockwork/narsie_attack.ogg', 75, 1) + if (4) + say("N'ath reth Sh'yro eth D'rekkathnor!!!") + playsound(get_turf(src),'sound/magic/clockwork/narsie_attack.ogg', 100, 1) + +/datum/action/innate/cultmast/cultmark + name = "Mark Target" + desc = "Marks a target for the cult" + button_icon_state = "cult_mark" + var/obj/effect/proc_holder/cultmark/CM + var/time = 0 + +/datum/action/innate/cultmast/cultmark/New() + CM = new() + ..() + +/datum/action/innate/cultmast/cultmark/IsAvailable() + if(owner.mind.special_role != "Cult Master") + return 0 + if((world.time - time)<1200 && !CM.active) + owner << "You need to wait [round((1200-(world.time-time))/10)] seconds before you can mark another target!" + return 0 + return ..() + +/datum/action/innate/cultmast/cultmark/Destroy() + QDEL_NULL(CM) + return ..() + +/datum/action/innate/cultmast/cultmark/Activate() + CM.toggle(owner) //the important bit + if(!active) + time = world.time + else + time = 0 + return TRUE + +/obj/effect/proc_holder/cultmark + active = FALSE + ranged_mousepointer = 'icons/effects/cult_target.dmi' + + +/obj/effect/proc_holder/cultmark/proc/toggle(mob/user) + if(active) + remove_ranged_ability("You cease the marking ritual...") + else + add_ranged_ability(user, "You prepare to mark a target for your cult...") + +/obj/effect/proc_holder/cultmark/InterceptClickOn(mob/living/caller, params, atom/target) + if(..()) + return + if(ranged_ability_user.incapacitated()) + remove_ranged_ability() + return + var/turf/T = get_turf(ranged_ability_user) + if(!isturf(T)) + return FALSE + if(target in view(7, get_turf(ranged_ability_user))) + remove_ranged_ability(caller, "The marking rite is complete! It will last for 90 seconds.") + GLOB.blood_target = target + var/area/A = get_area(target) + for(var/datum/mind/B in SSticker.mode.cult) + var/mob/living/M = B.current + if(M.stat != DEAD) + to_chat(M, "Master [ranged_ability_user] has marked [GLOB.blood_target] in the [A.name] as the cult's top priority, get there immediately!") + M << pick(sound('sound/hallucinations/over_here2.ogg',0,1,75), sound('sound/hallucinations/over_here3.ogg',0,1,75)) + var/image/cult_marker = image('icons/effects/cult_target.dmi', target, "glow", ABOVE_MOB_LAYER) + M.client.images |= cult_marker + addtimer(CALLBACK(M, /mob/living/proc/reset_blood_image, cult_marker), 900, TIMER_OVERRIDE) + return TRUE + return FALSE + +/mob/living/proc/reset_blood_image(var/image/cult_marker) + if(GLOB.blood_target && src.stat!=DEAD) + to_chat(src,"The blood mark has expired!") + if(client) + client.images.Remove(cult_marker) + QDEL_NULL(cult_marker) + GLOB.blood_target = null \ No newline at end of file diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index a90aeb78bc..6058e8f605 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -213,7 +213,7 @@ flags_inv = HIDEJUMPSUIT allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade) body_parts_covered = CHEST|GROIN|LEGS|ARMS - armor = list(melee = -50, bullet = -50, laser = -100,energy = -50, bomb = -50, bio = -50, rad = -50, fire = 0, acid = 0) + armor = list(melee = -50, bullet = -50, laser = -50,energy = -50, bomb = -50, bio = -50, rad = -50, fire = 0, acid = 0) slowdown = -1 hoodtype = /obj/item/clothing/head/hooded/berserkerhood diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm index ce4abced72..f393929463 100644 --- a/code/game/gamemodes/cult/ritual.dm +++ b/code/game/gamemodes/cult/ritual.dm @@ -197,35 +197,30 @@ This file contains the arcane tome files. if(!src || QDELETED(src) || !Adjacent(user) || user.incapacitated() || !check_rune_turf(Turf, user)) return if(ispath(rune_to_scribe, /obj/effect/rune/narsie)) - if(SSticker.mode.name == "cult") - var/datum/game_mode/cult/cult_mode = SSticker.mode - if(!("eldergod" in cult_mode.cult_objectives)) - to_chat(user, "Nar-Sie does not wish to be summoned!") - return - if(cult_mode.sacrifice_target && !(cult_mode.sacrifice_target in GLOB.sacrificed)) - to_chat(user, "The sacrifice is not complete. The portal would lack the power to open if you tried!") - return - if(!cult_mode.eldergod) - to_chat(user, "\"I am already here. There is no need to try to summon me now.\"") - return - if((loc.z && loc.z != ZLEVEL_STATION) || !A.blob_allowed) - to_chat(user, "The Geometer is not interested in lesser locations; the station is the prize!") - return - var/confirm_final = alert(user, "This is the FINAL step to summon Nar-Sie, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for Nar-Sie!", "No") - if(confirm_final == "No") - to_chat(user, "You decide to prepare further before scribing the rune.") - return - Turf = get_turf(user) - A = get_area(src) - if(!check_rune_turf(Turf, user) || (loc.z && loc.z != ZLEVEL_STATION)|| !A.blob_allowed) - return - priority_announce("Figments from an eldritch god are being summoned by [user] into [A.map_name] from an unknown dimension. Disrupt the ritual at all costs!","Central Command Higher Dimensionsal Affairs", 'sound/AI/spanomalies.ogg') - for(var/B in spiral_range_turfs(1, user, 1)) - var/obj/structure/emergency_shield/sanguine/N = new(B) - shields += N - else + if(!("eldergod" in SSticker.mode.cult_objectives)) to_chat(user, "Nar-Sie does not wish to be summoned!") return + if(GLOB.sac_complete) + to_chat(user, "The sacrifice is not complete. The portal would lack the power to open if you tried!") + return + if(!SSticker.mode.eldergod) + to_chat(user, "\"I am already here. There is no need to try to summon me now.\"") + return + if((loc.z && loc.z != ZLEVEL_STATION) || !A.blob_allowed) + to_chat(user, "The Geometer is not interested in lesser locations; the station is the prize!") + return + var/confirm_final = alert(user, "This is the FINAL step to summon Nar-Sie, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for Nar-Sie!", "No") + if(confirm_final == "No") + to_chat(user, "You decide to prepare further before scribing the rune.") + return + Turf = get_turf(user) + A = get_area(src) + if(!check_rune_turf(Turf, user) || (loc.z && loc.z != ZLEVEL_STATION)|| !A.blob_allowed) + return + priority_announce("Figments from an eldritch god are being summoned by [user] into [A.map_name] from an unknown dimension. Disrupt the ritual at all costs!","Central Command Higher Dimensional Affairs", 'sound/AI/spanomalies.ogg') + for(var/B in spiral_range_turfs(1, user, 1)) + var/obj/structure/emergency_shield/sanguine/N = new(B) + shields += N user.visible_message("[user] [user.blood_volume ? "cuts open their arm and begins writing in their own blood":"begins sketching out a strange design"]!", \ "You [user.blood_volume ? "slice open your arm and ":""]begin drawing a sigil of the Geometer.") if(user.blood_volume) diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index 8eb08cfa84..4573b460da 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -398,23 +398,23 @@ structure_check() searches for nearby cultist structures required for the invoca return 1 /obj/effect/rune/convert/proc/do_sacrifice(mob/living/sacrificial, list/invokers) + var/big_sac = FALSE if((((ishuman(sacrificial) || iscyborg(sacrificial)) && sacrificial.stat != DEAD) || is_sacrifice_target(sacrificial.mind)) && invokers.len < 3) for(var/M in invokers) to_chat(M, "[sacrificial] is too greatly linked to the world! You need three acolytes!") log_game("Offer rune failed - not enough acolytes and target is living or sac target") return FALSE - var/sacrifice_fulfilled = FALSE - if(sacrificial.mind) GLOB.sacrificed += sacrificial.mind if(is_sacrifice_target(sacrificial.mind)) - sacrifice_fulfilled = TRUE + GLOB.sac_complete = TRUE + big_sac = TRUE else GLOB.sacrificed += sacrificial new /obj/effect/overlay/temp/cult/sac(get_turf(src)) for(var/M in invokers) - if(sacrifice_fulfilled) + if(big_sac) to_chat(M, "\"Yes! This is the one I desire! You have done well.\"") else if(ishuman(sacrificial) || iscyborg(sacrificial)) @@ -451,7 +451,7 @@ structure_check() searches for nearby cultist structures required for the invoca scribe_delay = 450 //how long the rune takes to create scribe_damage = 40.1 //how much damage you take doing it var/used - var/ignore_gamemode = FALSE + var/ignore_gamemode = TRUE /obj/effect/rune/narsie/Initialize(mapload, set_keyword) . = ..() @@ -490,7 +490,7 @@ structure_check() searches for nearby cultist structures required for the invoca //BEGIN THE SUMMONING used = 1 ..() - send_to_playing_players('sound/effects/dimensional_rend.ogg') //There used to be a message for this but every time it was changed it got edgier so I removed it + send_to_playing_players('sound/effects/dimensional_rend.ogg') var/turf/T = get_turf(src) sleep(40) if(src) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 6029ec2c6d..700bfe5f3a 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -257,6 +257,8 @@ if(escaped_total > 0) SSblackbox.set_val("escaped_total",escaped_total) send2irc("Server", "Round just ended.") + if(cult.len && !istype(SSticker.mode,/datum/game_mode/cult)) + datum_cult_completion() return 0 diff --git a/code/game/gamemodes/game_mode.dm.rej b/code/game/gamemodes/game_mode.dm.rej new file mode 100644 index 0000000000..a8a4a7ca6c --- /dev/null +++ b/code/game/gamemodes/game_mode.dm.rej @@ -0,0 +1,10 @@ +diff a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm (rejected hunks) +@@ -258,6 +258,8 @@ + if(escaped_total > 0) + feedback_set("escaped_total",escaped_total) + send2irc("Server", "Round just ended.") ++ if(cult.len && !istype(SSticker.mode,/datum/game_mode/cult)) ++ datum_cult_completion() + return 0 + + diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm index 7ccf7a6b7e..b840b64c93 100644 --- a/code/game/gamemodes/wizard/soulstone.dm +++ b/code/game/gamemodes/wizard/soulstone.dm @@ -54,7 +54,7 @@ if(!ishuman(M))//If target is not a human. return ..() if(iscultist(M)) - to_chat(user, "\"Come now, do not capture your fellow's soul.\"") + to_chat(user, "\"Come now, do not capture your bretheren's soul.\"") return add_logs(user, M, "captured [M.name]'s soul", src) @@ -132,11 +132,11 @@ if("VICTIM") var/mob/living/carbon/human/T = target - if(SSticker.mode.name == "cult" && T.mind == SSticker.mode:sacrifice_target) + if(is_sacrifice_target(T.mind)) if(iscultist(user)) to_chat(user, "\"This soul is mine. SACRIFICE THEM!\"") else - to_chat(user, "The soulstone doesn't work for no apparent reason.") + to_chat(user, "The soulstone seems to reject this soul.") return 0 if(contents.len) to_chat(user, "Capture failed!: The soulstone is full! Free an existing soul to make room.") @@ -188,7 +188,10 @@ else makeNewConstruct(/mob/living/simple_animal/hostile/construct/builder/noncult, A, user, 0, T.loc) - + for(var/datum/mind/B in SSticker.mode.cult) + if(B == A.mind) + SSticker.mode.cult -= A.mind + SSticker.mode.update_cult_icons_removed(A.mind) qdel(T) user.drop_item() qdel(src) @@ -200,6 +203,9 @@ var/mob/living/simple_animal/hostile/construct/newstruct = new ctype((loc_override) ? (loc_override) : (get_turf(target))) if(stoner) newstruct.faction |= "\ref[stoner]" + newstruct.master = stoner + var/datum/action/innate/seek_master/SM = new() + SM.Grant(newstruct) newstruct.key = target.key if(newstruct.mind && ((stoner && iscultist(stoner)) || cultoverride) && SSticker && SSticker.mode) SSticker.mode.add_cultist(newstruct.mind, 0) @@ -207,6 +213,9 @@ to_chat(newstruct, "You are still bound to serve the cult[stoner ? " and [stoner]":""], follow their orders and help them complete their goals at all costs.") else if(stoner) to_chat(newstruct, "You are still bound to serve your creator, [stoner], follow their orders and help them complete their goals at all costs.") + newstruct.throw_alert("bloodsense", /obj/screen/alert/bloodsense) + var/obj/screen/alert/bloodsense/BS = newstruct.alerts["bloodsense"] + BS.Cviewer = newstruct newstruct.cancel_camera() diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm index f7faa0e5e5..12225321d7 100644 --- a/code/game/objects/effects/overlays.dm +++ b/code/game/objects/effects/overlays.dm @@ -230,7 +230,15 @@ name = "blood sparks" icon_state = "bloodsparkles" -/obj/effect/overlay/temp/dir_setting/cult/phase +/obj/effect/overlay/temp/cult/blood // The traditional teleport + name = "blood jaunt" + duration = 12 + icon_state = "bloodin" + +/obj/effect/overlay/temp/cult/blood/out + icon_state = "bloodout" + +/obj/effect/overlay/temp/dir_setting/cult/phase // The veil shifter teleport name = "phase glow" duration = 7 icon_state = "cultin" diff --git a/code/game/objects/effects/overlays.dm.rej b/code/game/objects/effects/overlays.dm.rej new file mode 100644 index 0000000000..7bfce2cb62 --- /dev/null +++ b/code/game/objects/effects/overlays.dm.rej @@ -0,0 +1,10 @@ +diff a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm (rejected hunks) +@@ -214,7 +214,7 @@ + icon = 'icons/effects/fire.dmi' + icon_state = "3" + duration = 20 +- ++ + /obj/effect/overlay/temp/cult + randomdir = 0 + duration = 10 diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index 0734670468..d908ed547d 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -49,8 +49,8 @@ timeofdeath = world.time tod = worldtime2text() var/turf/T = get_turf(src) + var/area/A = get_area(T) if(mind && mind.name && mind.active && (!(T.flags & NO_DEATHRATTLE))) - var/area/A = get_area(T) var/rendered = "[mind.name] has died at [A.name]." deadchat_broadcast(rendered, follow_target = src, turf_target = T, message_type=DEADCHAT_DEATHRATTLE) if(mind) diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index b879f1baf6..7285c2fa75 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -14,8 +14,8 @@ stop_automated_movement = 1 status_flags = CANPUSH attack_sound = 'sound/weapons/punch1.ogg' - see_in_dark = 7 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + see_in_dark = 7 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 @@ -31,12 +31,20 @@ deathmessage = "collapses in a shattered heap." var/list/construct_spells = list() var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this." + var/master = null + var/seeking = FALSE /mob/living/simple_animal/hostile/construct/Initialize() - . = ..() + . = ..() for(var/spell in construct_spells) AddSpell(new spell(null)) +/mob/living/simple_animal/hostile/construct/Destroy() + for(var/X in actions) + var/datum/action/A = X + qdel(A) + ..() + /mob/living/simple_animal/hostile/construct/Login() ..() to_chat(src, playstyle_string) @@ -84,11 +92,11 @@ /mob/living/simple_animal/hostile/construct/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE) return 0 - -/mob/living/simple_animal/hostile/construct/adjustHealth(amount, updating_health = TRUE, forced = FALSE) - . = ..() - if(updating_health) - update_health_hud() + +/mob/living/simple_animal/hostile/construct/adjustHealth(amount, updating_health = TRUE, forced = FALSE) + . = ..() + if(updating_health) + update_health_hud() /////////////////Juggernaut/////////////// /mob/living/simple_animal/hostile/construct/armored @@ -119,6 +127,38 @@ AIStatus = AI_ON environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP + +///////////////////////Master-Tracker/////////////////////// + +/datum/action/innate/seek_master + name = "Seek your Master" + desc = "You and your master share a soul-link that informs you of their location" + background_icon_state = "bg_demon" + buttontooltipstyle = "cult" + button_icon_state = "cult_mark" + var/tracking = FALSE + var/mob/living/simple_animal/hostile/construct/the_construct + +/datum/action/innate/seek_master/Grant(var/mob/living/C) + the_construct = C + ..() + +/datum/action/innate/seek_master/Activate() + if(!the_construct.master) + to_chat(the_construct, "You have no master to seek!") + the_construct.seeking = FALSE + return + if(tracking) + tracking = FALSE + the_construct.seeking = FALSE + to_chat(the_construct, "You are no longer tracking your master.") + return + else + tracking = TRUE + the_construct.seeking = TRUE + to_chat(the_construct, "You are now tracking your master.") + + /mob/living/simple_animal/hostile/construct/armored/bullet_act(obj/item/projectile/P) if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) var/reflectchance = 80 - round(P.damage/3) @@ -273,22 +313,22 @@ /mob/living/simple_animal/hostile/construct/harvester/hostile //actually hostile, will move around, hit things AIStatus = AI_ON environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP - - - -/////////////////////////////ui stuff///////////////////////////// - -/mob/living/simple_animal/hostile/construct/update_health_hud() - if(hud_used) - if(health >= maxHealth) - hud_used.healths.icon_state = "[icon_state]_health0" - else if(health > maxHealth*0.8) - hud_used.healths.icon_state = "[icon_state]_health2" - else if(health > maxHealth*0.6) - hud_used.healths.icon_state = "[icon_state]_health3" - else if(health > maxHealth*0.4) - hud_used.healths.icon_state = "[icon_state]_health4" - else if(health > maxHealth*0.2) - hud_used.healths.icon_state = "[icon_state]_health5" - else + + + +/////////////////////////////ui stuff///////////////////////////// + +/mob/living/simple_animal/hostile/construct/update_health_hud() + if(hud_used) + if(health >= maxHealth) + hud_used.healths.icon_state = "[icon_state]_health0" + else if(health > maxHealth*0.8) + hud_used.healths.icon_state = "[icon_state]_health2" + else if(health > maxHealth*0.6) + hud_used.healths.icon_state = "[icon_state]_health3" + else if(health > maxHealth*0.4) + hud_used.healths.icon_state = "[icon_state]_health4" + else if(health > maxHealth*0.2) + hud_used.healths.icon_state = "[icon_state]_health5" + else hud_used.healths.icon_state = "[icon_state]_health6" \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/constructs.dm.rej b/code/modules/mob/living/simple_animal/constructs.dm.rej new file mode 100644 index 0000000000..b730acfbe9 --- /dev/null +++ b/code/modules/mob/living/simple_animal/constructs.dm.rej @@ -0,0 +1,40 @@ +diff a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm (rejected hunks) +@@ -41,14 +41,11 @@ + for(var/spell in construct_spells) + AddSpell(new spell(null)) + +-<<<<<<< HEAD + /mob/living/simple_animal/hostile/construct/Destroy() + for(var/X in actions) + var/datum/action/A = X + qdel(A) + ..() +-======= +->>>>>>> a7603e4aba50d410795d5207e6d5e929b2401cb9 + + /mob/living/simple_animal/hostile/construct/Login() + ..() +@@ -288,8 +285,6 @@ + environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP + + +- +-<<<<<<< HEAD + ///////////////////////Master-Tracker/////////////////////// + + /datum/action/innate/seek_master +@@ -320,7 +315,7 @@ + the_construct.seeking = TRUE + to_chat(the_construct, "You are now tracking your master.") + +-======= ++ + /////////////////////////////ui stuff///////////////////////////// + + /mob/living/simple_animal/hostile/construct/update_health_hud() +@@ -337,4 +332,4 @@ + hud_used.healths.icon_state = "[icon_state]_health5" + else + hud_used.healths.icon_state = "[icon_state]_health6" +->>>>>>> a7603e4aba50d410795d5207e6d5e929b2401cb9 ++ diff --git a/config/game_options.txt b/config/game_options.txt index 356dc1acab..dce0b57d42 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -166,7 +166,7 @@ MIDROUND_ANTAG ABDUCTION #MIN_POP GANG 20 #MAX_POP GANG -1 -#MIN_POP CULT 24 +#MIN_POP CULT 0 #MAX_POP CULT -1 #MIN_POP CLOCKWORK_CULT 24 diff --git a/icons/effects/64x64.dmi b/icons/effects/64x64.dmi index 347bccc942..361cfff40c 100644 Binary files a/icons/effects/64x64.dmi and b/icons/effects/64x64.dmi differ diff --git a/icons/effects/cult_target.dmi b/icons/effects/cult_target.dmi new file mode 100644 index 0000000000..650feb3361 Binary files /dev/null and b/icons/effects/cult_target.dmi differ diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index d684794ee8..fe537a8e73 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi index 29c527b123..6d05b89039 100644 Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.dmi differ