From 05011a57c5a38ea210f3c77cc1b4d0c95b08f70a Mon Sep 17 00:00:00 2001
From: NovaBot <154629622+NovaBot13@users.noreply.github.com>
Date: Fri, 12 Apr 2024 19:08:10 -0400
Subject: [PATCH] [MIRROR] Blood cult: cleans up blood rites code, fixes a bug
or two, & adds blackbox logging for spells, runes, and shades (#1951)
* Blood cult: cleans up blood rites code, fixes a bug or two, & adds blackbox logging for spells, runes, and shades (#82444)
* Blood cult: cleans up blood rites code, fixes a bug or two, & adds blackbox logging for spells, runes, and shades
---------
Co-authored-by: wesoda25 <37246588+wesoda25@users.noreply.github.com>
---
code/modules/antagonists/cult/blood_magic.dm | 430 +++++++++++-------
code/modules/antagonists/cult/runes.dm | 1 +
.../antagonists/wizard/equipment/soulstone.dm | 4 +
code/modules/library/bibles.dm | 1 +
code/modules/mob/living/basic/cult/shade.dm | 7 +
5 files changed, 271 insertions(+), 172 deletions(-)
diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm
index cbccf1d9ff1..cf79872c947 100644
--- a/code/modules/antagonists/cult/blood_magic.dm
+++ b/code/modules/antagonists/cult/blood_magic.dm
@@ -1,3 +1,10 @@
+/// how many units of blood one charge of blood rites is worth
+#define USES_TO_BLOOD 2
+/// blood rites charges gained from sapping blood from a victim
+#define BLOOD_DRAIN_GAIN 50
+/// penalty for self healing, 1 point of damage * this # = charges required
+#define SELF_HEAL_PENALTY 1.65
+
/datum/action/innate/cult/blood_magic //Blood magic handles the creation of blood spells (formerly talismans)
name = "Prepare Blood Magic"
button_icon_state = "carve"
@@ -103,6 +110,8 @@
var/health_cost = 0
/// Have we already been positioned into our starting location?
var/positioned = FALSE
+ /// If false, the spell will not delete after running out of charges
+ var/deletes_on_empty = TRUE
/datum/action/innate/cult/blood_spell/Grant(mob/living/owner, datum/action/innate/cult/blood_magic/BM)
if(health_cost)
@@ -121,26 +130,25 @@
..()
/datum/action/innate/cult/blood_spell/IsAvailable(feedback = FALSE)
- if(!IS_CULTIST(owner) || owner.incapacitated() || !charges)
+ if(!IS_CULTIST(owner) || owner.incapacitated() || (!charges && deletes_on_empty))
return FALSE
return ..()
/datum/action/innate/cult/blood_spell/Activate()
- if(magic_path) //If this spell flows from the hand
- if(!hand_magic)
- hand_magic = new magic_path(owner, src)
- if(!owner.put_in_hands(hand_magic))
- qdel(hand_magic)
- hand_magic = null
- to_chat(owner, span_warning("You have no empty hand for invoking blood magic!"))
- return
- to_chat(owner, span_notice("Your wounds glow as you invoke the [name]."))
- return
- if(hand_magic)
- qdel(hand_magic)
- hand_magic = null
- to_chat(owner, span_warning("You snuff out the spell, saving it for later."))
-
+ if(!magic_path) // only concerned with spells that flow from the hand
+ return
+ if(hand_magic)
+ qdel(hand_magic)
+ hand_magic = null
+ to_chat(owner, span_warning("You snuff out the spell, saving it for later."))
+ return
+ hand_magic = new magic_path(owner, src)
+ if(!owner.put_in_hands(hand_magic))
+ qdel(hand_magic)
+ hand_magic = null
+ to_chat(owner, span_warning("You have no empty hand for invoking blood magic!"))
+ return
+ to_chat(owner, span_notice("Your wounds glow as you invoke the [name]."))
//Cult Blood Spells
/datum/action/innate/cult/blood_spell/stun
@@ -170,6 +178,7 @@
span_cult_italic("You speak the cursed words, emitting an EMP blast from your hand."))
empulse(owner, 2, 5)
charges--
+ SSblackbox.record_feedback("tally", "cult_spell_invoke", 1, "[name]")
if(charges <= 0)
qdel(src)
@@ -214,6 +223,7 @@
span_cult_italic("A [summoned_blade] materializes at your feet."))
SEND_SOUND(owner, sound('sound/effects/magic.ogg', FALSE, 0, 25))
charges--
+ SSblackbox.record_feedback("tally", "cult_spell_invoke", 1, "[name]")
if(charges <= 0)
qdel(src)
@@ -255,6 +265,7 @@
desc = base_desc
desc += "
Has [charges] use\s remaining."
build_all_button_icons()
+ SSblackbox.record_feedback("tally", "cult_spell_invoke", 1, "[name]")
if(charges <= 0)
to_chat(caller, span_cult("You have exhausted the spell's power!"))
qdel(src)
@@ -308,6 +319,7 @@
revealing = FALSE
name = "Conceal Runes"
button_icon_state = "gone"
+ SSblackbox.record_feedback("tally", "cult_spell_invoke", 1, "Conceal Runes")
if(charges <= 0)
qdel(src)
desc = base_desc
@@ -321,6 +333,7 @@
button_icon_state = "manip"
charges = 5
magic_path = "/obj/item/melee/blood_magic/manipulator"
+ deletes_on_empty = FALSE
// The "magic hand" items
/obj/item/melee/blood_magic
@@ -350,7 +363,7 @@
/obj/item/melee/blood_magic/Destroy()
if(!QDELETED(source))
- if(uses <= 0)
+ if(uses <= 0 && source.deletes_on_empty)
source.hand_magic = null
qdel(source)
source = null
@@ -371,6 +384,7 @@
qdel(src)
return
log_combat(user, M, "used a cult spell on", source.name, "")
+ SSblackbox.record_feedback("tally", "cult_spell_invoke", 1, "[name]")
M.lastattacker = user.real_name
M.lastattackerckey = user.ckey
@@ -681,167 +695,235 @@
. = ..()
. += "Bloody halberd, blood bolt barrage, and blood beam cost [BLOOD_HALBERD_COST], [BLOOD_BARRAGE_COST], and [BLOOD_BEAM_COST] charges respectively."
+/**
+ * handles inhand use of blood rites on constructs, humans, or non-living blood sources
+ *
+ * see '/obj/item/melee/blood_magic/manipulator/proc/heal_construct' for construct/shade behavior
+ * see '/obj/item/melee/blood_magic/manipulator/proc/heal_cultist' for human cultist behavior
+ * see '/obj/item/melee/blood_magic/manipulator/proc/drain_victim' for human non-cultist behavior
+ * if any of the above procs return FALSE, '/obj/item/melee/blood_magic/afterattack' will not be called
+ *
+ * '/obj/item/melee/blood_magic/manipulator/proc/blood_draw' handles blood pools/trails and does not affect parent proc
+ */
/obj/item/melee/blood_magic/manipulator/afterattack(atom/target, mob/living/carbon/human/user, proximity)
- if(proximity)
- if(ishuman(target))
- var/mob/living/carbon/human/human_bloodbag = target
- if(HAS_TRAIT(human_bloodbag, TRAIT_NOBLOOD))
- to_chat(user,span_warning("Blood rites do not work on people with no blood!"))
- return
- if(IS_CULTIST(human_bloodbag))
- if(human_bloodbag.stat == DEAD)
- to_chat(user,span_warning("Only a revive rune can bring back the dead!"))
- return
- if(human_bloodbag.blood_volume < BLOOD_VOLUME_SAFE)
- var/restore_blood = BLOOD_VOLUME_SAFE - human_bloodbag.blood_volume
- if(uses*2 < restore_blood)
- human_bloodbag.blood_volume += uses*2
- to_chat(user,span_danger("You use the last of your blood rites to restore what blood you could!"))
- uses = 0
- return ..()
- else
- human_bloodbag.blood_volume = BLOOD_VOLUME_SAFE
- uses -= round(restore_blood/2)
- to_chat(user,span_warning("Your blood rites have restored [human_bloodbag == user ? "your" : "[human_bloodbag.p_their()]"] blood to safe levels!"))
- var/overall_damage = human_bloodbag.getBruteLoss() + human_bloodbag.getFireLoss() + human_bloodbag.getToxLoss() + human_bloodbag.getOxyLoss()
- if(overall_damage == 0)
- to_chat(user,span_cult("That cultist doesn't require healing!"))
- else
- var/ratio = uses/overall_damage
- if(human_bloodbag == user)
- to_chat(user,span_cult("Your blood healing is far less efficient when used on yourself!"))
- ratio *= 0.35 // Healing is half as effective if you can't perform a full heal
- uses -= round(overall_damage) // Healing is 65% more "expensive" even if you can still perform the full heal
- if(ratio>1)
- ratio = 1
- uses -= round(overall_damage)
- human_bloodbag.visible_message(span_warning("[human_bloodbag] is fully healed by [human_bloodbag == user ? "[human_bloodbag.p_their()]":"[human_bloodbag]'s"] blood magic!"))
- else
- human_bloodbag.visible_message(span_warning("[human_bloodbag] is partially healed by [human_bloodbag == user ? "[human_bloodbag.p_their()]":"[human_bloodbag]'s"] blood magic."))
- uses = 0
- ratio *= -1
- var/need_mob_update = FALSE
- need_mob_update += human_bloodbag.adjustOxyLoss((overall_damage*ratio) * (human_bloodbag.getOxyLoss() / overall_damage), updating_health = FALSE)
- need_mob_update += human_bloodbag.adjustToxLoss((overall_damage*ratio) * (human_bloodbag.getToxLoss() / overall_damage), updating_health = FALSE)
- need_mob_update += human_bloodbag.adjustFireLoss((overall_damage*ratio) * (human_bloodbag.getFireLoss() / overall_damage), updating_health = FALSE)
- need_mob_update += human_bloodbag.adjustBruteLoss((overall_damage*ratio) * (human_bloodbag.getBruteLoss() / overall_damage), updating_health = FALSE)
- if(need_mob_update)
- human_bloodbag.updatehealth()
- playsound(get_turf(human_bloodbag), 'sound/magic/staff_healing.ogg', 25)
- new /obj/effect/temp_visual/cult/sparks(get_turf(human_bloodbag))
- user.Beam(human_bloodbag, icon_state="sendbeam", time = 15)
- else
- if(human_bloodbag.stat == DEAD)
- to_chat(user,span_warning("[human_bloodbag.p_Their()] blood has stopped flowing, you'll have to find another way to extract it."))
- return
- if(human_bloodbag.has_status_effect(/datum/status_effect/speech/slurring/cult))
- to_chat(user,span_danger("[human_bloodbag.p_Their()] blood has been tainted by an even stronger form of blood magic, it's no use to us like this!"))
- return
- if(human_bloodbag.blood_volume > BLOOD_VOLUME_SAFE)
- human_bloodbag.blood_volume -= 100
- uses += 50
- user.Beam(human_bloodbag, icon_state="drainbeam", time = 1 SECONDS)
- playsound(get_turf(human_bloodbag), 'sound/magic/enter_blood.ogg', 50)
- human_bloodbag.visible_message(span_danger("[user] drains some of [human_bloodbag]'s blood!"))
- to_chat(user,span_cult_italic("Your blood rite gains 50 charges from draining [human_bloodbag]'s blood."))
- new /obj/effect/temp_visual/cult/sparks(get_turf(human_bloodbag))
- else
- to_chat(user,span_warning("[human_bloodbag.p_Theyre()] missing too much blood - you cannot drain [human_bloodbag.p_them()] further!"))
- return
- if(isconstruct(target))
- var/mob/living/basic/construct/construct_thing = target
- var/missing_health = construct_thing.maxHealth - construct_thing.health
- if(missing_health)
- if(uses > missing_health)
- construct_thing.adjust_health(-missing_health)
- construct_thing.visible_message(span_warning("[construct_thing] is fully healed by [user]'s blood magic!"))
- uses -= missing_health
- else
- construct_thing.adjust_health(-uses)
- construct_thing.visible_message(span_warning("[construct_thing] is partially healed by [user]'s blood magic!"))
- uses = 0
- playsound(get_turf(construct_thing), 'sound/magic/staff_healing.ogg', 25)
- user.Beam(construct_thing, icon_state="sendbeam", time = 1 SECONDS)
- if(istype(target, /obj/effect/decal/cleanable/blood) || istype(target, /obj/effect/decal/cleanable/trail_holder) || isturf(target))
- blood_draw(target, user)
- ..()
+ if(!proximity)
+ return
+
+ if((isconstruct(target) || isshade(target)) && !heal_construct(target, user))
+ return
+ if(istype(target, /obj/effect/decal/cleanable/blood) || istype(target, /obj/effect/decal/cleanable/trail_holder) || isturf(target))
+ blood_draw(target, user)
+ if(ishuman(target))
+ var/mob/living/carbon/human/human_bloodbag = target
+ if(HAS_TRAIT(human_bloodbag, TRAIT_NOBLOOD))
+ human_bloodbag.balloon_alert(user, "no blood!")
+ return
+ if(human_bloodbag.stat == DEAD)
+ human_bloodbag.balloon_alert(user, "dead!")
+ return
+
+ if(IS_CULTIST(human_bloodbag) && !heal_cultist(human_bloodbag, user))
+ return
+ if(!IS_CULTIST(human_bloodbag) && !drain_victim(human_bloodbag, user))
+ return
+ ..()
+
+/**
+ * handles blood rites usage on constructs
+ *
+ * will only return TRUE if some amount healing is done
+ */
+/obj/item/melee/blood_magic/manipulator/proc/heal_construct(atom/target, mob/living/carbon/human/user)
+ var/mob/living/basic/construct_thing = target
+ if(!IS_CULTIST(construct_thing))
+ return FALSE
+ var/missing_health = construct_thing.maxHealth - construct_thing.health
+ if(!missing_health)
+ to_chat(user,span_cult("That cultist doesn't require healing!"))
+ return FALSE
+ if(uses <= 0)
+ construct_thing.balloon_alert(user, "out of blood!")
+ return FALSE
+ if(uses > missing_health)
+ construct_thing.adjust_health(-missing_health)
+ construct_thing.visible_message(span_warning("[construct_thing] is fully healed by [user]'s blood magic!"))
+ uses -= missing_health
+ else
+ construct_thing.adjust_health(-uses)
+ construct_thing.visible_message(span_warning("[construct_thing] is partially healed by [user]'s blood magic!"))
+ uses = 0
+ playsound(get_turf(construct_thing), 'sound/magic/staff_healing.ogg', 25)
+ user.Beam(construct_thing, icon_state="sendbeam", time = 1 SECONDS)
+ return TRUE
+
+/**
+ * handles blood rites usage on human cultists
+ *
+ * first restores blood, then heals damage. healing damage is more expensive, especially if performed on oneself
+ * returns TRUE if some amount of blood is restored and/or damage is healed
+ */
+/obj/item/melee/blood_magic/manipulator/proc/heal_cultist(mob/living/carbon/human/human_bloodbag, mob/living/carbon/human/user)
+ if(uses <= 0)
+ human_bloodbag.balloon_alert(user, "out of blood!")
+ return FALSE
+
+ /// used to ensure the proc returns TRUE if we completely restore an undamaged persons blood
+ var/blood_donor = FALSE
+ if(human_bloodbag.blood_volume < BLOOD_VOLUME_SAFE)
+ var/blood_needed = BLOOD_VOLUME_SAFE - human_bloodbag.blood_volume
+ /// how much blood we are capable of restoring, based on spell charges
+ var/blood_bank = USES_TO_BLOOD * uses
+ if(blood_bank < blood_needed)
+ human_bloodbag.blood_volume += blood_bank
+ to_chat(user,span_danger("You use the last of your blood rites to restore what blood you could!"))
+ uses = 0
+ return TRUE
+ blood_donor = TRUE
+ human_bloodbag.blood_volume = BLOOD_VOLUME_SAFE
+ uses -= round(blood_needed / USES_TO_BLOOD)
+ to_chat(user,span_warning("Your blood rites have restored [human_bloodbag == user ? "your" : "[human_bloodbag.p_their()]"] blood to safe levels!"))
+
+ var/overall_damage = human_bloodbag.getBruteLoss() + human_bloodbag.getFireLoss() + human_bloodbag.getToxLoss() + human_bloodbag.getOxyLoss()
+ if(overall_damage == 0)
+ if(blood_donor)
+ return TRUE
+ to_chat(user,span_cult("That cultist doesn't require healing!"))
+ return FALSE
+ /// how much damage we can/will heal
+ var/damage_healed = -1 * min(uses, overall_damage)
+ /// how many spell charges will be consumed to heal said damage
+ var/healing_cost = damage_healed
+ if(human_bloodbag == user)
+ to_chat(user,span_cult("Your blood healing is far less efficient when used on yourself!"))
+ damage_healed = -1 * min(uses * (1 / SELF_HEAL_PENALTY), overall_damage)
+ healing_cost = damage_healed * SELF_HEAL_PENALTY
+ uses += round(healing_cost)
+ human_bloodbag.visible_message(span_warning("[human_bloodbag] is [uses == 0 ? "partially healed":"fully healed"] by [human_bloodbag == user ? "[human_bloodbag.p_their()]":"[human_bloodbag]'s"] blood magic!"))
+
+ var/need_mob_update = FALSE
+ need_mob_update += human_bloodbag.adjustOxyLoss(damage_healed * (human_bloodbag.getOxyLoss() / overall_damage), updating_health = FALSE)
+ need_mob_update += human_bloodbag.adjustToxLoss(damage_healed * (human_bloodbag.getToxLoss() / overall_damage), updating_health = FALSE)
+ need_mob_update += human_bloodbag.adjustFireLoss(damage_healed * (human_bloodbag.getFireLoss() / overall_damage), updating_health = FALSE)
+ need_mob_update += human_bloodbag.adjustBruteLoss(damage_healed * (human_bloodbag.getBruteLoss() / overall_damage), updating_health = FALSE)
+ if(need_mob_update)
+ human_bloodbag.updatehealth()
+ playsound(get_turf(human_bloodbag), 'sound/magic/staff_healing.ogg', 25)
+ new /obj/effect/temp_visual/cult/sparks(get_turf(human_bloodbag))
+ user.Beam(human_bloodbag, icon_state="sendbeam", time = 15)
+ return TRUE
+
+/**
+ * handles blood rites use on a non-cultist human
+ *
+ * returns TRUE if blood is successfully drained from the victim
+ */
+/obj/item/melee/blood_magic/manipulator/proc/drain_victim(mob/living/carbon/human/human_bloodbag, mob/living/carbon/human/user)
+ if(human_bloodbag.has_status_effect(/datum/status_effect/speech/slurring/cult))
+ to_chat(user,span_danger("[human_bloodbag.p_Their()] blood has been tainted by an even stronger form of blood magic, it's no use to us like this!"))
+ return FALSE
+ if(human_bloodbag.blood_volume <= BLOOD_VOLUME_SAFE)
+ to_chat(user,span_warning("[human_bloodbag.p_Theyre()] missing too much blood - you cannot drain [human_bloodbag.p_them()] further!"))
+ return FALSE
+ human_bloodbag.blood_volume -= BLOOD_DRAIN_GAIN * USES_TO_BLOOD
+ uses += BLOOD_DRAIN_GAIN
+ user.Beam(human_bloodbag, icon_state="drainbeam", time = 1 SECONDS)
+ playsound(get_turf(human_bloodbag), 'sound/magic/enter_blood.ogg', 50)
+ human_bloodbag.visible_message(span_danger("[user] drains some of [human_bloodbag]'s blood!"))
+ to_chat(user,span_cult_italic("Your blood rite gains 50 charges from draining [human_bloodbag]'s blood."))
+ new /obj/effect/temp_visual/cult/sparks(get_turf(human_bloodbag))
+ return TRUE
+/**
+ * handles blood rites use on turfs, blood pools, and blood trails
+ */
/obj/item/melee/blood_magic/manipulator/proc/blood_draw(atom/target, mob/living/carbon/human/user)
var/blood_to_gain = 0
var/turf/our_turf = get_turf(target)
- if(our_turf)
- for(var/obj/effect/decal/cleanable/blood/blood_around_us in range(our_turf,2))
- if(blood_around_us.blood_state == BLOOD_STATE_HUMAN)
- if(blood_around_us.bloodiness == 100) //Bonus for "pristine" bloodpools, also to prevent cheese with footprint spam
- blood_to_gain += 30
- else
- blood_to_gain += max((blood_around_us.bloodiness**2)/800,1)
- new /obj/effect/temp_visual/cult/turf/floor(get_turf(blood_around_us))
- qdel(blood_around_us)
- for(var/obj/effect/decal/cleanable/trail_holder/trail_around_us in range(our_turf, 2))
- if(trail_around_us.blood_state == BLOOD_STATE_HUMAN)
- blood_to_gain += 5 //These don't get bloodiness, so we'll just increase this by a fixed value
- new /obj/effect/temp_visual/cult/turf/floor(get_turf(trail_around_us))
- qdel(trail_around_us)
- if(blood_to_gain)
- user.Beam(our_turf,icon_state="drainbeam", time = 15)
- new /obj/effect/temp_visual/cult/sparks(get_turf(user))
- playsound(our_turf, 'sound/magic/enter_blood.ogg', 50)
- to_chat(user, span_cult_italic("Your blood rite has gained [round(blood_to_gain)] charge\s from blood sources around you!"))
- uses += max(1, round(blood_to_gain))
-
+ if(!our_turf)
+ return
+ for(var/obj/effect/decal/cleanable/blood/blood_around_us in range(our_turf,2))
+ if(blood_around_us.blood_state != BLOOD_STATE_HUMAN)
+ break
+ if(blood_around_us.bloodiness == 100) // Bonus for "pristine" bloodpools, also to prevent cheese with footprint spam
+ blood_to_gain += 30
+ else
+ blood_to_gain += max((blood_around_us.bloodiness**2)/800,1)
+ new /obj/effect/temp_visual/cult/turf/floor(get_turf(blood_around_us))
+ qdel(blood_around_us)
+ for(var/obj/effect/decal/cleanable/trail_holder/trail_around_us in range(our_turf, 2))
+ if(trail_around_us.blood_state != BLOOD_STATE_HUMAN)
+ break
+ blood_to_gain += 5 //These don't get bloodiness, so we'll just increase this by a fixed value
+ new /obj/effect/temp_visual/cult/turf/floor(get_turf(trail_around_us))
+ qdel(trail_around_us)
+
+ if(!blood_to_gain)
+ return
+ user.Beam(our_turf,icon_state="drainbeam", time = 15)
+ new /obj/effect/temp_visual/cult/sparks(get_turf(user))
+ playsound(our_turf, 'sound/magic/enter_blood.ogg', 50)
+ to_chat(user, span_cult_italic("Your blood rite has gained [round(blood_to_gain)] charge\s from blood sources around you!"))
+ uses += max(1, round(blood_to_gain))
+
+/**
+ * handles untargeted use of blood rites
+ *
+ * allows user to trade in spell uses for equipment or spells
+ */
/obj/item/melee/blood_magic/manipulator/attack_self(mob/living/user)
- if(IS_CULTIST(user))
- var/static/list/spells = list(
- "Bloody Halberd (150)" = image(icon = 'icons/obj/weapons/spear.dmi', icon_state = "occultpoleaxe0"),
- "Blood Bolt Barrage (300)" = image(icon = 'icons/obj/weapons/guns/ballistic.dmi', icon_state = "arcane_barrage"),
- "Blood Beam (500)" = image(icon = 'icons/obj/weapons/hand.dmi', icon_state = "disintegrate")
- )
- var/choice = show_radial_menu(user, src, spells, custom_check = CALLBACK(src, PROC_REF(check_menu), user), require_near = TRUE)
- if(!check_menu(user))
- to_chat(user, span_cult_italic("You decide against conducting a greater blood rite."))
- return
- switch(choice)
- if("Bloody Halberd (150)")
- if(uses < BLOOD_HALBERD_COST)
- to_chat(user, span_cult_italic("You need [BLOOD_HALBERD_COST] charges to perform this rite."))
- else
- uses -= BLOOD_HALBERD_COST
- var/turf/current_position = get_turf(user)
- qdel(src)
- var/datum/action/innate/cult/halberd/halberd_act_granted = new(user)
- var/obj/item/melee/cultblade/halberd/rite = new(current_position)
- halberd_act_granted.Grant(user, rite)
- rite.halberd_act = halberd_act_granted
- if(user.put_in_hands(rite))
- to_chat(user, span_cult_italic("A [rite.name] appears in your hand!"))
- else
- user.visible_message(span_warning("A [rite.name] appears at [user]'s feet!"), \
- span_cult_italic("A [rite.name] materializes at your feet."))
- if("Blood Bolt Barrage (300)")
- if(uses < BLOOD_BARRAGE_COST)
- to_chat(user, span_cult_italic("You need [BLOOD_BARRAGE_COST] charges to perform this rite."))
- else
- var/obj/rite = new /obj/item/gun/magic/wand/arcane_barrage/blood()
- uses -= BLOOD_BARRAGE_COST
- qdel(src)
- if(user.put_in_hands(rite))
- to_chat(user, span_cult("Your hands glow with power!"))
- else
- to_chat(user, span_cult_italic("You need a free hand for this rite!"))
- qdel(rite)
- if("Blood Beam (500)")
- if(uses < BLOOD_BEAM_COST)
- to_chat(user, span_cult_italic("You need [BLOOD_BEAM_COST] charges to perform this rite."))
- else
- var/obj/rite = new /obj/item/blood_beam()
- uses -= BLOOD_BEAM_COST
- qdel(src)
- if(user.put_in_hands(rite))
- to_chat(user, span_cult_large("Your hands glow with POWER OVERWHELMING!!!"))
- else
- to_chat(user, span_cult_italic("You need a free hand for this rite!"))
- qdel(rite)
+ var/static/list/spells = list(
+ "Bloody Halberd (150)" = image(icon = 'icons/obj/weapons/spear.dmi', icon_state = "occultpoleaxe0"),
+ "Blood Bolt Barrage (300)" = image(icon = 'icons/obj/weapons/guns/ballistic.dmi', icon_state = "arcane_barrage"),
+ "Blood Beam (500)" = image(icon = 'icons/obj/weapons/hand.dmi', icon_state = "disintegrate")
+ )
+ var/choice = show_radial_menu(user, src, spells, custom_check = CALLBACK(src, PROC_REF(check_menu), user), require_near = TRUE)
+ if(!check_menu(user))
+ to_chat(user, span_cult_italic("You decide against conducting a greater blood rite."))
+ return
+
+ switch(choice)
+ if("Bloody Halberd (150)")
+ if(uses < BLOOD_HALBERD_COST)
+ to_chat(user, span_cult_italic("You need [BLOOD_HALBERD_COST] charges to perform this rite."))
+ return
+ uses -= BLOOD_HALBERD_COST
+ var/turf/current_position = get_turf(user)
+ qdel(src)
+ var/datum/action/innate/cult/halberd/halberd_act_granted = new(user)
+ var/obj/item/melee/cultblade/halberd/rite = new(current_position)
+ halberd_act_granted.Grant(user, rite)
+ rite.halberd_act = halberd_act_granted
+ if(user.put_in_hands(rite))
+ to_chat(user, span_cult_italic("A [rite.name] appears in your hand!"))
+ else
+ user.visible_message(span_warning("A [rite.name] appears at [user]'s feet!"), \
+ span_cult_italic("A [rite.name] materializes at your feet."))
+
+ if("Blood Bolt Barrage (300)")
+ if(uses < BLOOD_BARRAGE_COST)
+ to_chat(user, span_cult_italic("You need [BLOOD_BARRAGE_COST] charges to perform this rite."))
+ return
+ var/obj/rite = new /obj/item/gun/magic/wand/arcane_barrage/blood()
+ uses -= BLOOD_BARRAGE_COST
+ qdel(src)
+ if(user.put_in_hands(rite))
+ to_chat(user, span_cult("Your hands glow with power!"))
+ else
+ to_chat(user, span_cult_italic("You need a free hand for this rite!"))
+ qdel(rite)
+
+ if("Blood Beam (500)")
+ if(uses < BLOOD_BEAM_COST)
+ to_chat(user, span_cult_italic("You need [BLOOD_BEAM_COST] charges to perform this rite."))
+ return
+ var/obj/rite = new /obj/item/blood_beam()
+ uses -= BLOOD_BEAM_COST
+ qdel(src)
+ if(user.put_in_hands(rite))
+ to_chat(user, span_cult_large("Your hands glow with POWER OVERWHELMING!!!"))
+ else
+ to_chat(user, span_cult_italic("You need a free hand for this rite!"))
+ qdel(rite)
/obj/item/melee/blood_magic/manipulator/proc/check_menu(mob/living/user)
if(!istype(user))
@@ -849,3 +931,7 @@
if(user.incapacitated() || !user.Adjacent(src))
return FALSE
return TRUE
+
+#undef USES_TO_BLOOD
+#undef BLOOD_DRAIN_GAIN
+#undef SELF_HEAL_PENALTY
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index d42ace3f4cd..aa979ff7814 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -104,6 +104,7 @@ Runes can either be invoked by one's self or with many different cultists. Each
var/list/invokers = can_invoke(user)
if(length(invokers) >= req_cultists)
invoke(invokers)
+ SSblackbox.record_feedback("tally", "cult_rune_invoke", 1, "[name]")
else
to_chat(user, span_danger("You need [req_cultists - length(invokers)] more adjacent cultists to use this rune in such a manner."))
fail_invoke()
diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm
index abceef36f0c..13273d22f6e 100644
--- a/code/modules/antagonists/wizard/equipment/soulstone.dm
+++ b/code/modules/antagonists/wizard/equipment/soulstone.dm
@@ -399,6 +399,7 @@
soulstone_spirit.faction |= "[REF(user)]" //Add the master as a faction, allowing inter-mob cooperation
if(IS_CULTIST(user))
soulstone_spirit.mind.add_antag_datum(/datum/antagonist/cult)
+ SSblackbox.record_feedback("tally", "cult_shade_created", 1)
soulstone_spirit.cancel_camera()
update_appearance()
@@ -459,6 +460,7 @@
if(CONSTRUCT_JUGGERNAUT)
if(IS_CULTIST(creator))
make_new_construct(/mob/living/basic/construct/juggernaut, target, creator, cultoverride, loc_override) // ignore themes, the actual giving of cult info is in the make_new_construct proc
+ SSblackbox.record_feedback("tally", "cult_shade_to_jugger", 1)
return
switch(theme)
if(THEME_WIZARD)
@@ -470,6 +472,7 @@
if(CONSTRUCT_WRAITH)
if(IS_CULTIST(creator))
make_new_construct(/mob/living/basic/construct/wraith, target, creator, cultoverride, loc_override) // ignore themes, the actual giving of cult info is in the make_new_construct proc
+ SSblackbox.record_feedback("tally", "cult_shade_to_wraith", 1)
return
switch(theme)
if(THEME_WIZARD)
@@ -481,6 +484,7 @@
if(CONSTRUCT_ARTIFICER)
if(IS_CULTIST(creator))
make_new_construct(/mob/living/basic/construct/artificer, target, creator, cultoverride, loc_override) // ignore themes, the actual giving of cult info is in the make_new_construct proc
+ SSblackbox.record_feedback("tally", "cult_shade_to_arti", 1)
return
switch(theme)
if(THEME_WIZARD)
diff --git a/code/modules/library/bibles.dm b/code/modules/library/bibles.dm
index 656c90d77bb..31630c550a1 100644
--- a/code/modules/library/bibles.dm
+++ b/code/modules/library/bibles.dm
@@ -324,6 +324,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list(
if(cultist)
cultist.silent = TRUE
cultist.on_removal()
+ SSblackbox.record_feedback("tally", "cult_shade_purified", 1)
shade.theme = THEME_HOLY
shade.name = "Purified [shade.real_name]"
shade.update_appearance(UPDATE_ICON_STATE)
diff --git a/code/modules/mob/living/basic/cult/shade.dm b/code/modules/mob/living/basic/cult/shade.dm
index fac1d347665..bf6fbe13944 100644
--- a/code/modules/mob/living/basic/cult/shade.dm
+++ b/code/modules/mob/living/basic/cult/shade.dm
@@ -54,6 +54,8 @@
icon_living = icon_state
/mob/living/basic/shade/death()
+ if(IS_CULTIST(src))
+ SSblackbox.record_feedback("tally", "cult_shade_killed", 1)
if(death_message == initial(death_message))
death_message = "lets out a contented sigh as [p_their()] form unwinds."
..()
@@ -63,6 +65,11 @@
return FALSE
return ..()
+/mob/living/basic/shade/suicide_log(obj/item/suicide_tool)
+ if(IS_CULTIST(src))
+ SSblackbox.record_feedback("tally", "cult_shade_suicided", 1)
+ ..()
+
/mob/living/basic/shade/attackby(obj/item/item, mob/user, params)
if(istype(item, /obj/item/soulstone))
var/obj/item/soulstone/stone = item