[MIRROR] Replace Heretic Phobia with Cursed Organs (#27664)

* Replace Heretic Phobia with Cursed Organs (#83082)

## About The Pull Request

Being sacrificed by a Heretic no longer applies an incurable brain
trauma that gives you a phobia of Heretic-associated items.
Instead it does the following things:

- Removes 2-4 of your organs and leaves them behind on the sacrificial
circle, replacing them with "corrupt" organs with negative effects.
- Gives you an unremovable "curse" debuff which will cause you to
immediately cough a random organ out of your chest if you attack or are
attack by the person who sacrificed you, **if they are wearing a Focus
at the time.**

Corrupt Organs can be removed via surgery, although doing so is
dangerous.
A removed corrupt organ will often either immediately explode for a
small (but not insignificant) amount of burn damage to everyone nearby,
or it will animate itself as a haunted object and be a general nuisance
until subdued.
A patient dosed with Holy Water can always have their organs removed
without consequence.
Chugging Holy Water will also suppress most of the negative effects of
your corrupt organs.

The effects of Corrupt Organs are as follows:
- **Eyes:** All Heretics **and also a selection of additional random
people** are completely invisible to you. You can still see their
runtext (or "hear") them, but they otherwise cannot be seen. And as you
can't see them you also cannot click on them.
- **Tongue:** When you speak, your language will sometimes be replaced
by "Shadowspeak", understood only by Curators and Nightmares. This is
less of an impediment during Halloween because of the friendly
Shadowpeople.
- **Liver:** When you drink something, it will add extra reagents to
your sip from a pool largely composed of alcohol and hallucinogens.
- **Stomach:** Your hunger will deplete significantly faster if you do
not regularly ingest blood. (Fun fact: Did you know that a drinking
glass full of blood automatically relabels itself "tomato juice"?)
- **Heart:** You will continue to be intermittently tormented by
frightening hands.
- **Lungs:** You will intermittently cough out dangerous or unpleasant
gases.
- **Appendix:** Very infrequently you will vomit out several hostile
worms.

Most of these effects aren't present while you're dodging the spooky
hands in the Mansus realm.
Plasmamen never have their lungs replaced because they'd immediately
start choking on their internals and that is annoying. Ethereals _do_
have their hearts replaced so may be surprised to find that they don't
respawn when killed if they don't get a new one.
I would haved called them Cursed Organs but the Cursed Heart already
exists.

## Why It's Good For The Game

Nobody enjoys the Heretic phobia from any angle.
Phobias suck to deal with in general. and while being scared of all
Heretic stuff from that point on is mildly flavourful it's not very
interesting.
This alternative hopefully provides a more interesting answer (which
should provide more storytelling and interaction with other people) to
the problems that the original change intended to address:
Making sure that nonlethal sacrifices still feel significantly bad
enough that you're not going to volunteer, and as an extra layer of
protection to ensure that someone you sacrificed functionally ceases to
be a threat to you.

## Changelog
🆑
del: Being sacrified by a Heretic no longer gives you an incurable
phobia.
add: Being sacrificed by a Heretic will drop 2-4 of your organs on the
ground and replace them with "corrupt organs" with negative effects
which can be suppressed with Holy Water.
add: Players who have been sacrificed by Heretics will experience
additional and rapidly lethal consequences for attempting to fight
someone who previously sacrificed them, as long as that person is
wearing a focus.
/🆑

* Replace Heretic Phobia with Cursed Organs

---------

Co-authored-by: Jacquerel <hnevard@gmail.com>
This commit is contained in:
SkyratBot
2024-05-09 08:32:37 +02:00
committed by GitHub
co-authored by Jacquerel
parent 19098a0f59
commit d4f03e8add
12 changed files with 482 additions and 26 deletions
+2
View File
@@ -238,6 +238,8 @@ GLOBAL_LIST_INIT(ai_employers, list(
#define IS_LUNATIC(mob) (mob.mind?.has_antag_datum(/datum/antagonist/lunatic))
/// Checks if the given mob is either a heretic, heretic monster or a lunatic.
#define IS_HERETIC_OR_MONSTER(mob) (IS_HERETIC(mob) || IS_HERETIC_MONSTER(mob) || IS_LUNATIC(mob))
/// CHecks if the given mob is in the mansus realm
#define IS_IN_MANSUS(mob) (istype(get_area(mob), /area/centcom/heretic_sacrifice))
/// Checks if the given mob is a wizard
#define IS_WIZARD(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/wizard))
+4 -3
View File
@@ -235,9 +235,10 @@ multiple modular subtrees with behaviors
///Called when the AI controller pawn changes z levels, we check if there's any clients on the new one and wake up the AI if there is.
/datum/ai_controller/proc/on_changed_z_level(atom/source, turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
SIGNAL_HANDLER
var/mob/mob_pawn = pawn
if((mob_pawn?.client && !continue_processing_when_client))
return
if (ismob(pawn))
var/mob/mob_pawn = pawn
if((mob_pawn?.client && !continue_processing_when_client))
return
if(old_turf)
SSai_controllers.ai_controllers_by_zlevel[old_turf.z] -= src
if(new_turf)
+66
View File
@@ -0,0 +1,66 @@
/// Component applying shared behaviour by cursed organs granted when sacrificed by a heretic
/// Mostly just does something spooky when it is removed
/datum/element/corrupted_organ
/datum/element/corrupted_organ/Attach(datum/target)
. = ..()
if (!isinternalorgan(target))
return ELEMENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_ORGAN_SURGICALLY_REMOVED, PROC_REF(on_removed))
var/atom/atom_parent = target
atom_parent.color = COLOR_VOID_PURPLE
atom_parent.add_filter(name = "ray", priority = 1, params = list(
type = "rays",
size = 12,
color = COLOR_VOID_PURPLE,
density = 12
))
var/ray_filter = atom_parent.get_filter("ray")
animate(ray_filter, offset = 100, time = 2 MINUTES, loop = -1, flags = ANIMATION_PARALLEL) // Absurdly long animate so nobody notices it hitching when it loops
animate(offset = 0, time = 2 MINUTES) // I sure hope duration of animate doesnt have any performance effect
/datum/element/corrupted_organ/Detach(datum/source)
UnregisterSignal(source, list(COMSIG_ORGAN_SURGICALLY_REMOVED))
return ..()
/// When we're taken out of someone, do something spooky
/datum/element/corrupted_organ/proc/on_removed(atom/organ, mob/living/carbon/loser)
SIGNAL_HANDLER
if (loser.has_reagent(/datum/reagent/water/holywater) || loser.can_block_magic(MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY) || prob(20))
return
if (prob(75))
organ.AddComponent(\
/datum/component/haunted_item,\
haunt_color = "#00000000", \
aggro_radius = 4, \
spawn_message = span_revenwarning("[organ] hovers ominously into the air, pulsating with unnatural vigour!"), \
despawn_message = span_revenwarning("[organ] falls motionless to the ground."), \
)
return
var/turf/origin_turf = get_turf(organ)
playsound(organ, 'sound/magic/forcewall.ogg', vol = 100)
new /obj/effect/temp_visual/curse_blast(origin_turf)
organ.visible_message(span_revenwarning("[organ] explodes in a burst of dark energy!"))
for(var/mob/living/target in range(1, origin_turf))
var/armor = target.run_armor_check(attack_flag = BOMB)
target.apply_damage(30, damagetype = BURN, blocked = armor, spread_damage = TRUE)
qdel(organ)
/obj/effect/temp_visual/curse_blast
icon = 'icons/effects/64x64.dmi'
pixel_x = -16
pixel_y = -16
icon_state = "curse"
duration = 0.3 SECONDS
/obj/effect/temp_visual/curse_blast/Initialize(mapload)
. = ..()
animate(src, transform = matrix() * 0.2, time = 0, flags = ANIMATION_PARALLEL)
animate(transform = matrix() * 2, time = duration, easing = EASE_IN)
animate(src, alpha = 255, time = 0, flags = ANIMATION_PARALLEL)
animate(alpha = 255, time = 0.2 SECONDS)
animate(alpha = 0, time = 0.1 SECONDS)
+9 -8
View File
@@ -7,14 +7,15 @@
/datum/element/relay_attackers/Attach(datum/target)
. = ..()
// Boy this sure is a lot of ways to tell us that someone tried to attack us
RegisterSignal(target, COMSIG_ATOM_AFTER_ATTACKEDBY, PROC_REF(after_attackby))
RegisterSignals(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW, COMSIG_MOB_ATTACK_ALIEN), PROC_REF(on_attack_generic))
RegisterSignals(target, list(COMSIG_ATOM_ATTACK_BASIC_MOB, COMSIG_ATOM_ATTACK_ANIMAL), PROC_REF(on_attack_npc))
RegisterSignal(target, COMSIG_PROJECTILE_PREHIT, PROC_REF(on_bullet_act))
RegisterSignal(target, COMSIG_ATOM_PREHITBY, PROC_REF(on_hitby))
RegisterSignal(target, COMSIG_ATOM_HULK_ATTACK, PROC_REF(on_attack_hulk))
RegisterSignal(target, COMSIG_ATOM_ATTACK_MECH, PROC_REF(on_attack_mech))
if (!HAS_TRAIT(target, TRAIT_RELAYING_ATTACKER)) // Little bit gross but we want to just apply this shit from a bunch of places
// Boy this sure is a lot of ways to tell us that someone tried to attack us
RegisterSignal(target, COMSIG_ATOM_AFTER_ATTACKEDBY, PROC_REF(after_attackby))
RegisterSignals(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW, COMSIG_MOB_ATTACK_ALIEN), PROC_REF(on_attack_generic))
RegisterSignals(target, list(COMSIG_ATOM_ATTACK_BASIC_MOB, COMSIG_ATOM_ATTACK_ANIMAL), PROC_REF(on_attack_npc))
RegisterSignal(target, COMSIG_PROJECTILE_PREHIT, PROC_REF(on_bullet_act))
RegisterSignal(target, COMSIG_ATOM_PREHITBY, PROC_REF(on_hitby))
RegisterSignal(target, COMSIG_ATOM_HULK_ATTACK, PROC_REF(on_attack_hulk))
RegisterSignal(target, COMSIG_ATOM_ATTACK_MECH, PROC_REF(on_attack_mech))
ADD_TRAIT(target, TRAIT_RELAYING_ATTACKER, REF(src))
/datum/element/relay_attackers/Detach(datum/source, ...)
@@ -219,6 +219,14 @@
. = ..()
. += emissive_appearance(icon, icon_state, src, alpha = src.alpha)
/// Nebula vomit with extra guests
/obj/effect/decal/cleanable/vomit/nebula/worms
/obj/effect/decal/cleanable/vomit/nebula/worms/Initialize(mapload, list/datum/disease/diseases)
. = ..()
for (var/i in 1 to rand(2, 3))
new /mob/living/basic/hivelord_brood(loc)
/obj/effect/decal/cleanable/vomit/old
name = "crusty dried vomit"
desc = "You try not to look at the chunks, and fail."
@@ -0,0 +1,242 @@
/// Renders you unable to see people who were heretics at the time that this organ is gained
/obj/item/organ/internal/eyes/corrupt
name = "corrupt orbs"
desc = "These eyes have seen something they shouldn't have."
/// The override images we are applying
var/list/hallucinations
/obj/item/organ/internal/eyes/corrupt/Initialize(mapload)
. = ..()
AddElement(/datum/element/corrupted_organ)
AddElement(/datum/element/noticable_organ, "%PRONOUN_Their eyes have wide dilated pupils, and no iris. Something is moving in the darkness.", BODY_ZONE_PRECISE_EYES)
/obj/item/organ/internal/eyes/corrupt/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
if (!organ_owner.client)
return
var/list/human_mobs = GLOB.human_list.Copy()
human_mobs -= organ_owner
for (var/mob/living/carbon/human/check_human as anything in human_mobs)
if (!IS_HERETIC(check_human) && !prob(5)) // Throw in some false positives
continue
var/image/invisible_man = image('icons/blanks/32x32.dmi', check_human, "nothing")
invisible_man.override = TRUE
LAZYADD(hallucinations, invisible_man)
if (LAZYLEN(hallucinations))
organ_owner.client.images |= hallucinations
/obj/item/organ/internal/eyes/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
if (!LAZYLEN(hallucinations))
return
organ_owner.client?.images -= hallucinations
QDEL_NULL(hallucinations)
/// Sometimes speak in incomprehensible tongues
/obj/item/organ/internal/tongue/corrupt
name = "corrupt tongue"
desc = "This one tells only lies."
/obj/item/organ/internal/tongue/corrupt/Initialize(mapload)
. = ..()
AddElement(/datum/element/corrupted_organ)
AddElement(/datum/element/noticable_organ, "The inside of %PRONOUN_Their mouth is full of stars.", BODY_ZONE_PRECISE_MOUTH)
/obj/item/organ/internal/tongue/corrupt/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
RegisterSignal(organ_owner, COMSIG_MOB_SAY, PROC_REF(on_spoken))
/obj/item/organ/internal/tongue/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
UnregisterSignal(organ_owner, COMSIG_MOB_SAY)
/// When the mob speaks, sometimes put it in a different language
/obj/item/organ/internal/tongue/corrupt/proc/on_spoken(mob/living/organ_owner, list/speech_args)
SIGNAL_HANDLER
if (organ_owner.has_reagent(/datum/reagent/water/holywater) || prob(60))
return
speech_args[SPEECH_LANGUAGE] = /datum/language/shadowtongue
/// Randomly secretes alcohol or hallucinogens when you're drinking something
/obj/item/organ/internal/liver/corrupt
name = "corrupt liver"
desc = "After what you've seen you could really go for a drink."
/// How much extra ingredients to add?
var/amount_added = 5
/// What extra ingredients can we add?
var/list/extra_ingredients = list(
/datum/reagent/consumable/ethanol/pina_olivada,
/datum/reagent/consumable/ethanol/rum,
/datum/reagent/consumable/ethanol/thirteenloko,
/datum/reagent/consumable/ethanol/vodka,
/datum/reagent/consumable/superlaughter,
/datum/reagent/drug/bath_salts,
/datum/reagent/drug/blastoff,
/datum/reagent/drug/happiness,
/datum/reagent/drug/mushroomhallucinogen,
)
/obj/item/organ/internal/liver/corrupt/Initialize(mapload)
. = ..()
AddElement(/datum/element/corrupted_organ)
/obj/item/organ/internal/liver/corrupt/on_mob_insert(mob/living/carbon/organ_owner, special)
. = ..()
RegisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS, PROC_REF(on_drank))
/obj/item/organ/internal/liver/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
UnregisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS)
/// If we drank something, add a little extra
/obj/item/organ/internal/liver/corrupt/proc/on_drank(atom/source, list/reagents, datum/reagents/source_reagents, methods)
SIGNAL_HANDLER
if (!(methods & INGEST))
return
var/datum/reagents/extra_reagents = new()
extra_reagents.add_reagent(pick(extra_ingredients), amount_added)
extra_reagents.trans_to(source, amount_added, transferred_by = src, methods = INJECT)
if (prob(20))
to_chat(source, span_warning("As you take a sip, you feel something bubbling in your stomach..."))
/// Rapidly become hungry if you are not digesting blood
/obj/item/organ/internal/stomach/corrupt
name = "corrupt stomach"
desc = "This parasite demands an unwholesome diet in order to be satisfied."
/// Do we have an unholy thirst?
var/thirst_satiated = FALSE
/// Timer for when we get thirsty again
var/thirst_timer
/// How long until we prompt the player to drink blood again?
COOLDOWN_DECLARE(message_cooldown)
/obj/item/organ/internal/stomach/corrupt/Initialize(mapload)
. = ..()
AddElement(/datum/element/corrupted_organ)
AddElement(/datum/element/noticable_organ, "%PRONOUN_They %PRONOUN_have an unhealthy pallor.")
/obj/item/organ/internal/stomach/corrupt/on_mob_insert(mob/living/carbon/organ_owner, special)
. = ..()
RegisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS, PROC_REF(on_drank))
/obj/item/organ/internal/stomach/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special)
. = ..()
UnregisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS)
/// Check if we drank a little blood
/obj/item/organ/internal/stomach/corrupt/proc/on_drank(atom/source, list/reagents, datum/reagents/source_reagents, methods)
SIGNAL_HANDLER
if (!(methods & INGEST))
return
var/contains_blood = locate(/datum/reagent/blood) in reagents
if (!contains_blood)
return
if (!thirst_satiated)
to_chat(source, span_cult_italic("The thirst is satisfied... for now."))
thirst_satiated = TRUE
deltimer(thirst_timer)
thirst_timer = addtimer(VARSET_CALLBACK(src, thirst_satiated, FALSE), 3 MINUTES, TIMER_STOPPABLE | TIMER_DELETE_ME)
/obj/item/organ/internal/stomach/corrupt/handle_hunger(mob/living/carbon/human/human, seconds_per_tick, times_fired)
if (thirst_satiated || human.has_reagent(/datum/reagent/water/holywater))
return ..()
human.adjust_nutrition(-1 * seconds_per_tick)
if (!COOLDOWN_FINISHED(src, message_cooldown))
return ..()
COOLDOWN_START(src, message_cooldown, 30 SECONDS)
var/static/list/blood_messages = list(
"Blood...",
"Everyone suddenly looks so tasty.",
"The blood...",
"There's an emptiness in you that only blood can fill.",
"You could really go for some blood right now.",
"You feel the blood rushing through your veins.",
"You think about biting someone's throat.",
"Your stomach growls and you feel a metallic taste in your mouth.",
)
to_chat(human, span_cult_italic(pick(blood_messages)))
return ..()
/// Occasionally bombards you with spooky hands and lets everyone hear your pulse.
/obj/item/organ/internal/heart/corrupt
name = "corrupt heart"
desc = "What corruption is this spreading along with the blood?"
/// How long until the next heart?
COOLDOWN_DECLARE(hand_cooldown)
/obj/item/organ/internal/heart/corrupt/Initialize(mapload)
. = ..()
AddElement(/datum/element/corrupted_organ)
/obj/item/organ/internal/heart/corrupt/on_life(seconds_per_tick, times_fired)
. = ..()
if (!COOLDOWN_FINISHED(src, hand_cooldown) || IS_IN_MANSUS(owner) || !owner.needs_heart() || !is_beating() || owner.has_reagent(/datum/reagent/water/holywater))
return
fire_curse_hand(owner)
COOLDOWN_START(src, hand_cooldown, rand(6 SECONDS, 45 SECONDS)) // Wide variance to put you off guard
/// Sometimes cough out some kind of dangerous gas
/obj/item/organ/internal/lungs/corrupt
name = "corrupt lungs"
desc = "Some things SHOULD be drowned in tar."
/// How likely are we not to cough every time we take a breath?
var/cough_chance = 15
/// How much gas to emit?
var/gas_amount = 30
/// What can we cough up?
var/list/gas_types = list(
/datum/gas/bz = 30,
/datum/gas/miasma = 50,
/datum/gas/plasma = 20,
)
/obj/item/organ/internal/lungs/corrupt/Initialize(mapload)
. = ..()
AddElement(/datum/element/corrupted_organ)
/obj/item/organ/internal/lungs/corrupt/check_breath(datum/gas_mixture/breath, mob/living/carbon/human/breather)
. = ..()
if (!. || IS_IN_MANSUS(owner) || breather.has_reagent(/datum/reagent/water/holywater) || !prob(cough_chance))
return
breather.emote("cough");
var/chosen_gas = pick_weight(gas_types)
var/datum/gas_mixture/mix_to_spawn = new()
mix_to_spawn.add_gas(pick(chosen_gas))
mix_to_spawn.gases[chosen_gas][MOLES] = gas_amount
mix_to_spawn.temperature = breather.bodytemperature
log_atmos("[owner] coughed some gas into the air due to their corrupted lungs.", mix_to_spawn)
var/turf/open/our_turf = get_turf(breather)
our_turf.assume_air(mix_to_spawn)
/// It's full of worms
/obj/item/organ/internal/appendix/corrupt
name = "corrupt appendix"
desc = "What kind of dark, cosmic force is even going to bother to corrupt an appendix?"
/// How likely are we to spawn worms?
var/worm_chance = 2
/obj/item/organ/internal/appendix/corrupt/Initialize(mapload)
. = ..()
AddElement(/datum/element/corrupted_organ)
AddElement(/datum/element/noticable_organ, "%PRONOUN_Their abdomen is distended... and wiggling.", BODY_ZONE_PRECISE_GROIN)
/obj/item/organ/internal/appendix/corrupt/on_life(seconds_per_tick, times_fired)
. = ..()
if (owner.stat != CONSCIOUS || owner.has_reagent(/datum/reagent/water/holywater) || IS_IN_MANSUS(owner) || !SPT_PROB(worm_chance, seconds_per_tick))
return
owner.vomit(MOB_VOMIT_MESSAGE | MOB_VOMIT_HARM, vomit_type = /obj/effect/decal/cleanable/vomit/nebula/worms, distance = 0)
owner.Knockdown(0.5 SECONDS)
@@ -107,3 +107,17 @@
return
bloodiest_wound.adjust_blood_flow(-0.5 * seconds_between_ticks)
/// Torment the target with a frightening hand
/proc/fire_curse_hand(mob/living/carbon/victim)
var/grab_dir = turn(victim.dir, pick(-90, 90, 180, 180)) // Not in front, favour behind
var/turf/spawn_turf = get_ranged_target_turf(victim, grab_dir, 8)
if (isnull(spawn_turf))
return
new /obj/effect/temp_visual/dir_setting/curse/grasp_portal(spawn_turf, victim.dir)
playsound(spawn_turf, 'sound/effects/curse2.ogg', 80, TRUE, -1)
var/obj/projectile/curse_hand/hel/hand = new (spawn_turf)
hand.preparePixelProjectile(victim, spawn_turf)
if (QDELETED(hand)) // safety check if above fails - above has a stack trace if it does fail
return
hand.fire()
@@ -0,0 +1,93 @@
/// A curse given to people to disencourage them from retaliating against someone who sacrificed them
/datum/status_effect/heretic_curse
id = "heretic_curse"
alert_type = null
status_type = STATUS_EFFECT_MULTIPLE // In case several different people sacrifice you, unlucky
/// Who cursed us?
var/mob/living/the_curser
/// Don't experience bad things too often
COOLDOWN_DECLARE(consequence_cooldown)
/datum/status_effect/heretic_curse/on_creation(mob/living/new_owner, mob/living/the_curser)
src.the_curser = the_curser
return ..()
/datum/status_effect/heretic_curse/Destroy()
the_curser = null
return ..()
/datum/status_effect/heretic_curse/on_apply()
if (isnull(the_curser) || !iscarbon(owner))
return FALSE
the_curser.AddElement(/datum/element/relay_attackers)
RegisterSignal(the_curser, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_curser_attacked))
RegisterSignal(the_curser, COMSIG_QDELETING, PROC_REF(on_curser_destroyed))
owner.AddElement(/datum/element/relay_attackers)
RegisterSignal(owner, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_owner_attacked))
return TRUE
/datum/status_effect/heretic_curse/on_remove()
UnregisterSignal(owner, COMSIG_ATOM_WAS_ATTACKED)
UnregisterSignal(the_curser, COMSIG_ATOM_WAS_ATTACKED)
the_curser = null
/// If the heretic that cursed us is destroyed this thing is useless now
/datum/status_effect/heretic_curse/proc/on_curser_destroyed()
SIGNAL_HANDLER
qdel(src)
/// If we attack the guy who cursed us, that's no good
/datum/status_effect/heretic_curse/proc/on_curser_attacked(datum/source, mob/attacker)
SIGNAL_HANDLER
if (attacker != owner || !HAS_TRAIT(source, TRAIT_ALLOW_HERETIC_CASTING))
return
log_combat(owner, the_curser, "attacked", addition = "and lost some organs because they had previously been sacrificed by them.")
experience_the_consequences()
/// If we are attacked by the guy who cursed us, that's also no good
/datum/status_effect/heretic_curse/proc/on_owner_attacked(datum/source, mob/attacker)
SIGNAL_HANDLER
if (attacker != the_curser || !HAS_TRAIT(attacker, TRAIT_ALLOW_HERETIC_CASTING))
return
log_combat(the_curser, owner, "attacked", addition = "and as they had previously sacrificed them, removed some of their organs.")
experience_the_consequences()
/// Experience something you may not enjoy which may also significantly shorten your lifespan
/datum/status_effect/heretic_curse/proc/experience_the_consequences()
if (!COOLDOWN_FINISHED(src, consequence_cooldown) || owner.stat != CONSCIOUS)
return
var/mob/living/carbon/carbon_owner = owner
var/obj/item/bodypart/chest/organ_storage = owner.get_bodypart(BODY_ZONE_CHEST)
if (isnull(organ_storage))
carbon_owner.gib() // IDK how you don't have a chest but you're not getting away that easily
return
var/list/removable_organs = list()
for(var/obj/item/organ/internal/bodypart_organ in organ_storage.contents)
if(bodypart_organ.organ_flags & ORGAN_UNREMOVABLE)
continue
removable_organs += bodypart_organ
if (!length(removable_organs))
return // This one is a little more possible but they're probably already in pretty bad shape by this point
var/obj/item/organ/internal/removing_organ = pick(removable_organs)
if (carbon_owner.vomit(vomit_flags = VOMIT_CATEGORY_BLOOD))
carbon_owner.visible_message(span_boldwarning("[carbon_owner] vomits out [carbon_owner.p_their()] [removing_organ]"))
else
carbon_owner.visible_message(span_boldwarning("[carbon_owner]'s [removing_organ] rips itself out of `[carbon_owner.p_their()] chest!"))
removing_organ.Remove(carbon_owner)
var/turf/land_turf = get_step(carbon_owner, carbon_owner.dir)
if (land_turf.is_blocked_turf(exclude_mobs = TRUE))
land_turf = carbon_owner.drop_location()
removing_organ.forceMove(land_turf)
COOLDOWN_START(src, consequence_cooldown, 10 SECONDS)
@@ -26,6 +26,16 @@
var/list/datum/mind/target_blacklist
/// An assoc list of [ref] to [timers] - a list of all the timers of people in the shadow realm currently
var/list/return_timers
/// Evil organs we can put in people
var/static/list/grantable_organs = list(
/obj/item/organ/internal/appendix/corrupt,
/obj/item/organ/internal/eyes/corrupt,
/obj/item/organ/internal/heart/corrupt,
/obj/item/organ/internal/liver/corrupt,
/obj/item/organ/internal/lungs/corrupt,
/obj/item/organ/internal/stomach/corrupt,
/obj/item/organ/internal/tongue/corrupt,
)
/datum/heretic_knowledge/hunt_and_sacrifice/Destroy(force)
heretic_mind = null
@@ -197,6 +207,8 @@
heretic_datum.total_sacrifices++
heretic_datum.knowledge_points += 2
sacrifice.apply_status_effect(/datum/status_effect/heretic_curse, user)
if(!begin_sacrifice(sacrifice))
disembowel_target(sacrifice)
@@ -278,6 +290,8 @@
disembowel_target(sac_target)
return
curse_organs(sac_target)
// Send 'em to the destination. If the teleport fails, just disembowel them and stop the chain
if(!destination || !do_teleport(sac_target, destination, asoundin = 'sound/magic/repulse.ogg', asoundout = 'sound/magic/blind.ogg', no_effects = TRUE, channel = TELEPORT_CHANNEL_MAGIC, forced = TRUE))
disembowel_target(sac_target)
@@ -299,6 +313,31 @@
RegisterSignal(sac_target, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(on_target_escape)) // Cheese condition
RegisterSignal(sac_target, COMSIG_LIVING_DEATH, PROC_REF(on_target_death)) // Loss condition
/// Apply a sinister curse to some of the target's organs as an incentive to leave us alone
/datum/heretic_knowledge/hunt_and_sacrifice/proc/curse_organs(mob/living/carbon/human/sac_target)
var/usable_organs = grantable_organs.Copy()
if (isplasmaman(sac_target))
usable_organs -= /obj/item/organ/internal/lungs/corrupt // Their lungs are already more cursed than anything I could give them
var/total_implant = rand(2, 4)
var/gave_any = FALSE
for (var/i in 1 to total_implant)
if (!length(usable_organs))
break
var/organ_path = pick_n_take(usable_organs)
var/obj/item/organ/internal/to_give = new organ_path
if (!to_give.Insert(sac_target))
qdel(to_give)
else
gave_any = TRUE
if (!gave_any)
return
new /obj/effect/gibspawner/human/bodypartless(get_turf(sac_target))
sac_target.visible_message(span_boldwarning("Several organs force themselves out of [sac_target]!"))
/**
* This proc is called from [proc/after_target_sleeps] when the [sac_target] should be waking up.)
*
@@ -375,8 +414,7 @@
if(IS_HERETIC(sac_target))
var/datum/antagonist/heretic/victim_heretic = sac_target.mind?.has_antag_datum(/datum/antagonist/heretic)
victim_heretic.knowledge_points -= 3
else
sac_target.gain_trauma(/datum/brain_trauma/mild/phobia/heresy, TRAUMA_RESILIENCE_MAGIC)
// Wherever we end up, we sure as hell won't be able to explain
sac_target.adjust_timed_status_effect(40 SECONDS, /datum/status_effect/speech/slurring/heretic)
sac_target.adjust_stutter(40 SECONDS)
@@ -100,18 +100,7 @@ Basically, we fill the time between now and 2s from now with hands based off the
/datum/reagent/inverse/helgrasp/proc/spawn_hands(mob/living/carbon/affected_mob)
if(!affected_mob && iscarbon(holder.my_atom))//Catch timer
affected_mob = holder.my_atom
//Adapted from the end of the curse - but lasts a short time
var/grab_dir = turn(affected_mob.dir, pick(-90, 90, 180, 180)) //grab them from a random direction other than the one faced, favoring grabbing from behind
var/turf/spawn_turf = get_ranged_target_turf(affected_mob, grab_dir, 8)//Larger range so you have more time to dodge
if(!spawn_turf)
return
new/obj/effect/temp_visual/dir_setting/curse/grasp_portal(spawn_turf, affected_mob.dir)
playsound(spawn_turf, 'sound/effects/curse2.ogg', 80, TRUE, -1)
var/obj/projectile/curse_hand/hel/hand = new (spawn_turf)
hand.preparePixelProjectile(affected_mob, spawn_turf)
if(QDELETED(hand)) //safety check if above fails - above has a stack trace if it does fail
return
hand.fire()
fire_curse_hand(affected_mob)
//At the end, we clear up any loose hanging timers just in case and spawn any remaining lag_remaining hands all at once.
/datum/reagent/inverse/helgrasp/on_mob_delete(mob/living/affected_mob)
@@ -193,7 +193,6 @@
// The true movement is here
moveToNullspace()
bodypart_owner.contents -= src
bodypart_owner = null
on_bodypart_remove(limb)
+3
View File
@@ -1488,6 +1488,7 @@
#include "code\datums\elements\connect_loc.dm"
#include "code\datums\elements\consumable_mob.dm"
#include "code\datums\elements\content_barfer.dm"
#include "code\datums\elements\corrupted_organ.dm"
#include "code\datums\elements\crackable.dm"
#include "code\datums\elements\crusher_loot.dm"
#include "code\datums\elements\cuffsnapping.dm"
@@ -3161,6 +3162,7 @@
#include "code\modules\antagonists\heretic\moon_lunatic.dm"
#include "code\modules\antagonists\heretic\rust_effect.dm"
#include "code\modules\antagonists\heretic\transmutation_rune.dm"
#include "code\modules\antagonists\heretic\items\corrupted_organs.dm"
#include "code\modules\antagonists\heretic\items\eldritch_flask.dm"
#include "code\modules\antagonists\heretic\items\eldritch_painting.dm"
#include "code\modules\antagonists\heretic\items\forbidden_book.dm"
@@ -3191,6 +3193,7 @@
#include "code\modules\antagonists\heretic\knowledge\starting_lore.dm"
#include "code\modules\antagonists\heretic\knowledge\void_lore.dm"
#include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_buff.dm"
#include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_curse.dm"
#include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_knowledge.dm"
#include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_map.dm"
#include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_moodlets.dm"