From c9e586bf196833531b1a10fd9a28cd0360470fca Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Tue, 9 Jan 2024 13:20:58 -0600 Subject: [PATCH] Changes how Cultists obtain the bastard sword. Instead of sacrificing Heretics, you now sacrifice Null Rods, with a caveat. (#80583) Merry Christmas, I bring Chaplain code. This PR reworks how cultists obtain the bloody bastard sword. Instead of sacrificing a Heretic, you gain the bastard sword by sacrificing a Null Rod that has been used by a holy person to spill cultist blood. You can also sacrifice a Null Rod to turn it into a cult blade it if hasn't spilled any blood, as a means of destroying it. - Putting a Null Rod over an Offer rune with - 0 or 1 cultist crits / kills will transform it into ritual dagger. - 2 cultist crits / kills will transform it into longsword. - 3 or more cultist crits / kills will transform it into the bastard sword. This effect does not apply to special null rod types like the Bow or Scythe implant. Heretics retain their innate cult stun resistance, and are still un-convertable. - Allowing the Cult to get multiple of these things is horrible. - This puts a soft-cap on it to 1 under most circumstances. - This makes obtaining the sword less incredibly easy. - Heretics require a lot more time and effort to get equipped to pose a passing chance fighting cultists. As a consequence, it's dead easy to obtain: `I try to convert a guy 10 minutes into the round, they glow green, I stab them to death with my dagger. They can do nothing to defend themself. Free sword.` - Instead, we put it behind the chaplain, who more often than not is protected by security. - Additionally, we put it behind the requirements that the chaplain is *robust*: a chaplain that has been killing cultists actively. - This also serves as a "comeback" mechanic, as if a chaplain has been ripping and tearing through your ranks, but you manage to take them down, you are rewarded with a strong weapon. Honestly I would prefer if we straight up removed the sword (or... maybe nerfed it) but improve, don't remove etc. :cl: Melbert balance: Blood cultists can now convert a Null Rod into a cult weapon on an offer rune. The strength of the weapon it is converted into depends on how many cultists the Chaplain crit / killed with it. At five or more, it will turn into a Bastard Sword. Note, sacrificing someone holding a Null Rod will automatically convert it after they are gibbed. balance: Heretics no longer produce a Bastard Sword upon cult conversion. They are still immune to cult stun and cannot be converted by blood cultists. /:cl: --- code/game/objects/items/holy_weapons.dm | 23 +++ code/modules/antagonists/cult/runes.dm | 136 ++++++++++++------ .../antagonists/heretic/heretic_antag.dm | 11 -- 3 files changed, 112 insertions(+), 58 deletions(-) diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm index 7a3f7a2e18fc..3459c497843e 100644 --- a/code/game/objects/items/holy_weapons.dm +++ b/code/game/objects/items/holy_weapons.dm @@ -20,6 +20,8 @@ var/chaplain_spawnable = TRUE /// Short description of what this item is capable of, for radial menu uses. var/menu_description = "A standard chaplain's weapon. Fits in pockets. Can be worn on the belt." + /// Lazylist, tracks refs()s to all cultists which have been crit or killed by this nullrod. + var/list/cultists_slain /obj/item/nullrod/Initialize(mapload) . = ..() @@ -59,6 +61,27 @@ user.visible_message(span_suicide("[user] is killing [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to get closer to god!")) return (BRUTELOSS|FIRELOSS) +/obj/item/nullrod/attack(mob/living/target_mob, mob/living/user, params) + if(!user.mind?.holy_role) + return ..() + if(!IS_CULTIST(target_mob) || istype(target_mob, /mob/living/carbon/human/cult_ghost)) + return ..() + + var/old_stat = target_mob.stat + . = ..() + if(old_stat < target_mob.stat) + LAZYOR(cultists_slain, REF(target_mob)) + return . + +/obj/item/nullrod/examine(mob/user) + . = ..() + if(!IS_CULTIST(user) || !GET_ATOM_BLOOD_DNA_LENGTH(src)) + return + + var/num_slain = LAZYLEN(cultists_slain) + . += span_cultitalic("It has the blood of [num_slain] fallen cultist[num_slain == 1 ? "" : "s"] on it. \ + Offering it to Nar'sie will transform it into a [num_slain >= 3 ? "powerful" : "standard"] cult weapon.") + /obj/item/nullrod/godhand name = "god hand" desc = "This hand of yours glows with an awesome power!" diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index cee4f32dc859..d4740795fddb 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -222,52 +222,58 @@ structure_check() searches for nearby cultist structures required for the invoca /obj/effect/rune/convert/invoke(list/invokers) if(rune_in_use) return + var/list/myriad_targets = list() - var/turf/T = get_turf(src) - for(var/mob/living/M in T) - if(!IS_CULTIST(M)) - myriad_targets |= M - if(!length(myriad_targets)) + for(var/mob/living/non_cultist in loc) + if(!IS_CULTIST(non_cultist)) + myriad_targets += non_cultist + + if(!length(myriad_targets) && !try_spawn_sword()) fail_invoke() - log_game("Offer rune failed - no eligible targets.") return + rune_in_use = TRUE visible_message(span_warning("[src] pulses blood red!")) var/oldcolor = color color = RUNE_COLOR_DARKRED - var/mob/living/L = pick(myriad_targets) - - 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) - if(L.stat != DEAD && is_convertable) - invocation = "Mah'weyh pleggh at e'ntrath!" - ..() - if(is_convertable) - do_convert(L, invokers, Cult_team) + + if(length(myriad_targets)) + var/mob/living/new_convertee = pick(myriad_targets) + var/mob/living/first_invoker = invokers[1] + var/datum/antagonist/cult/first_invoker_datum = first_invoker.mind.has_antag_datum(/datum/antagonist/cult) + var/datum/team/cult/cult_team = first_invoker_datum.get_team() + + var/is_convertable = is_convertable_to_cult(new_convertee, cult_team) + if(new_convertee.stat != DEAD && is_convertable) + invocation = "Mah'weyh pleggh at e'ntrath!" + ..() + do_convert(new_convertee, invokers, cult_team) + + else + invocation = "Barhah hra zar'garis!" + ..() + do_sacrifice(new_convertee, invokers, cult_team) + + cult_team.check_size() // Triggers the eye glow or aura effects if the cult has grown large enough relative to the crew + else - invocation = "Barhah hra zar'garis!" - ..() - do_sacrifice(L, invokers) - animate(src, color = oldcolor, time = 5) - addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_atom_colour)), 5) - Cult_team.check_size() // Triggers the eye glow or aura effects if the cult has grown large enough relative to the crew + do_invoke_glow() + + animate(src, color = oldcolor, time = 0.5 SECONDS) + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_atom_colour)), 0.5 SECONDS) rune_in_use = FALSE /obj/effect/rune/convert/proc/do_convert(mob/living/convertee, list/invokers, datum/team/cult/cult_team) ASSERT(convertee.mind) if(length(invokers) < 2) - for(var/M in invokers) - to_chat(M, span_warning("You need at least two invokers to convert [convertee]!")) - log_game("Offer rune with [convertee] on it failed - tried conversion with one invoker.") + for(var/invoker in invokers) + to_chat(invoker, span_warning("You need at least two invokers to convert [convertee]!")) return FALSE if(convertee.can_block_magic(MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY, charge_cost = 0)) //No charge_cost because it can be spammed - for(var/M in invokers) - to_chat(M, span_warning("Something is shielding [convertee]'s mind!")) - log_game("Offer rune with [convertee] on it failed - convertee had anti-magic.") + for(var/invoker in invokers) + to_chat(invoker, span_warning("Something is shielding [convertee]'s mind!")) return FALSE var/brutedamage = convertee.getBruteLoss() @@ -314,19 +320,11 @@ structure_check() searches for nearby cultist structures required for the invoca convertee.name = convertee.real_name return TRUE -/obj/effect/rune/convert/proc/do_sacrifice(mob/living/sacrificial, list/invokers) - var/mob/living/first_invoker = invokers[1] - if(!first_invoker) - return FALSE - var/datum/antagonist/cult/C = first_invoker.mind.has_antag_datum(/datum/antagonist/cult,TRUE) - if(!C) - return FALSE - +/obj/effect/rune/convert/proc/do_sacrifice(mob/living/sacrificial, list/invokers, datum/team/cult/cult_team) var/big_sac = FALSE - if((((ishuman(sacrificial) || iscyborg(sacrificial)) && sacrificial.stat != DEAD) || C.cult_team.is_sacrifice_target(sacrificial.mind)) && length(invokers) < 3) - for(var/M in invokers) - to_chat(M, span_cultitalic("[sacrificial] is too greatly linked to the world! You need three acolytes!")) - log_game("Offer rune with [sacrificial] on it failed - not enough acolytes and target is living or sac target") + if((((ishuman(sacrificial) || iscyborg(sacrificial)) && sacrificial.stat != DEAD) || cult_team.is_sacrifice_target(sacrificial.mind)) && length(invokers) < 3) + for(var/invoker in invokers) + to_chat(invoker, span_cultitalic("[sacrificial] is too greatly linked to the world! You need three acolytes!")) return FALSE var/signal_result = SEND_SIGNAL(sacrificial, COMSIG_LIVING_CULT_SACRIFICED, invokers) @@ -335,7 +333,7 @@ structure_check() searches for nearby cultist structures required for the invoca if(sacrificial.mind) LAZYADD(GLOB.sacrificed, WEAKREF(sacrificial.mind)) - for(var/datum/objective/sacrifice/sac_objective in C.cult_team.objectives) + for(var/datum/objective/sacrifice/sac_objective in cult_team.objectives) if(sac_objective.target == sacrificial.mind) sac_objective.sacced = TRUE sac_objective.clear_sacrifice() @@ -344,7 +342,7 @@ structure_check() searches for nearby cultist structures required for the invoca else LAZYADD(GLOB.sacrificed, WEAKREF(sacrificial)) - new /obj/effect/temp_visual/cult/sac(get_turf(src)) + new /obj/effect/temp_visual/cult/sac(loc) if(!(signal_result & SILENCE_SACRIFICE_MESSAGE)) for(var/invoker in invokers) @@ -357,26 +355,70 @@ structure_check() searches for nearby cultist structures required for the invoca to_chat(invoker, span_cultlarge("\"I accept this meager sacrifice.\"")) if(iscyborg(sacrificial)) - var/construct_class = show_radial_menu(first_invoker, sacrificial, GLOB.construct_radial_images, require_near = TRUE, tooltips = TRUE) + var/construct_class = show_radial_menu(invokers[1], sacrificial, GLOB.construct_radial_images, require_near = TRUE, tooltips = TRUE) if(QDELETED(sacrificial) || !construct_class) return FALSE sacrificial.grab_ghost() - make_new_construct_from_class(construct_class, THEME_CULT, sacrificial, first_invoker, TRUE, get_turf(src)) + make_new_construct_from_class(construct_class, THEME_CULT, sacrificial, invokers[1], TRUE, get_turf(src)) var/mob/living/silicon/robot/sacriborg = sacrificial sacrificial.log_message("was sacrificed as a cyborg.", LOG_GAME) sacriborg.mmi = null qdel(sacrificial) return TRUE - var/obj/item/soulstone/stone = new /obj/item/soulstone(get_turf(src)) + + var/obj/item/soulstone/stone = new(loc) if(sacrificial.mind && !HAS_TRAIT(sacrificial, TRAIT_SUICIDED)) - stone.capture_soul(sacrificial, first_invoker, TRUE) + stone.capture_soul(sacrificial, invokers[1], forced = TRUE) if(sacrificial) playsound(sacrificial, 'sound/magic/disintegrate.ogg', 100, TRUE) sacrificial.investigate_log("has been sacrificially gibbed by the cult.", INVESTIGATE_DEATHS) sacrificial.gib() + + try_spawn_sword() // after sharding and gibbing, which potentially dropped a null rod return TRUE +/// Tries to convert a null rod over the rune to a cult sword +/obj/effect/rune/convert/proc/try_spawn_sword() + for(var/obj/item/nullrod/rod in loc) + if(rod.anchored || (rod.resistance_flags & INDESTRUCTIBLE)) + continue + + var/num_slain = LAZYLEN(rod.cultists_slain) + var/displayed_message = "[rod] glows an unholy red and begins to transform..." + if(GET_ATOM_BLOOD_DNA_LENGTH(rod)) + displayed_message += " The blood of [num_slain] fallen cultist[num_slain == 1 ? "":"s"] is absorbed into [rod]!" + + rod.visible_message(span_cultitalic(displayed_message)) + switch(num_slain) + if(0, 1) + animate_spawn_sword(rod, /obj/item/melee/cultblade/dagger) + if(2) + animate_spawn_sword(rod, /obj/item/melee/cultblade) + else + animate_spawn_sword(rod, /obj/item/cult_bastard) + return TRUE + + return FALSE + +/// Does an animation of a null rod transforming into a cult sword +/obj/effect/rune/convert/proc/animate_spawn_sword(obj/item/nullrod/former_rod, new_blade_typepath) + playsound(src, 'sound/effects/magic.ogg', 33, vary = TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, frequency = 0.66) + former_rod.anchored = TRUE + former_rod.Shake() + animate(former_rod, alpha = 0, transform = matrix(former_rod.transform).Scale(0.01), time = 2 SECONDS, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL) + QDEL_IN(former_rod, 2 SECONDS) + + var/obj/item/new_blade = new new_blade_typepath(loc) + var/matrix/blade_matrix_on_spawn = matrix(new_blade.transform) + new_blade.name = "converted [new_blade.name]" + new_blade.anchored = TRUE + new_blade.alpha = 0 + new_blade.transform = matrix(new_blade.transform).Scale(0.01) + new_blade.Shake() + animate(new_blade, alpha = 255, transform = blade_matrix_on_spawn, time = 2 SECONDS, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL) + addtimer(VARSET_CALLBACK(new_blade, anchored, FALSE), 2 SECONDS) + /obj/effect/rune/empower cultist_name = "Empower" cultist_desc = "allows cultists to prepare greater amounts of blood magic at far less of a cost." diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm index 3a1559ca926e..6f9ffe66d3d9 100644 --- a/code/modules/antagonists/heretic/heretic_antag.dm +++ b/code/modules/antagonists/heretic/heretic_antag.dm @@ -224,7 +224,6 @@ RegisterSignal(our_mob, COMSIG_MOB_ITEM_AFTERATTACK, PROC_REF(on_item_afterattack)) RegisterSignal(our_mob, COMSIG_MOB_LOGIN, PROC_REF(fix_influence_network)) RegisterSignal(our_mob, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(after_fully_healed)) - RegisterSignal(our_mob, COMSIG_LIVING_CULT_SACRIFICED, PROC_REF(on_cult_sacrificed)) /datum/antagonist/heretic/remove_innate_effects(mob/living/mob_override) var/mob/living/our_mob = mob_override || owner.current @@ -237,7 +236,6 @@ COMSIG_MOB_ITEM_AFTERATTACK, COMSIG_MOB_LOGIN, COMSIG_LIVING_POST_FULLY_HEAL, - COMSIG_LIVING_CULT_SACRIFICED )) /datum/antagonist/heretic/on_body_transfer(mob/living/old_body, mob/living/new_body) @@ -382,15 +380,6 @@ var/datum/heretic_knowledge/living_heart/heart_knowledge = get_knowledge(/datum/heretic_knowledge/living_heart) heart_knowledge.on_research(source, src) -/// Signal proc for [COMSIG_LIVING_CULT_SACRIFICED] to reward cultists for sacrificing a heretic -/datum/antagonist/heretic/proc/on_cult_sacrificed(mob/living/source, list/invokers) - SIGNAL_HANDLER - - new /obj/item/cult_bastard(source.loc) - for(var/mob/living/cultist as anything in invokers) - to_chat(cultist, span_cultlarge("\"A follower of the forgotten gods! You must be rewarded for such a valuable sacrifice.\"")) - return SILENCE_SACRIFICE_MESSAGE - /** * Create our objectives for our heretic. */