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. */