diff --git a/code/datums/elements/update_icon_blocker.dm b/code/datums/elements/update_icon_blocker.dm index 5c84ed9886a..45206772564 100644 --- a/code/datums/elements/update_icon_blocker.dm +++ b/code/datums/elements/update_icon_blocker.dm @@ -1,11 +1,17 @@ -//Prevents calling anything in update_icon() like update_icon_state() or update_overlays() +/// Prevents calling anything in update_icon() like update_icon_state() or update_overlays() +/datum/element/update_icon_blocker /datum/element/update_icon_blocker/Attach(datum/target) . = ..() - if(!istype(target, /atom)) + if(!isatom(target)) return ELEMENT_INCOMPATIBLE + RegisterSignal(target, COMSIG_ATOM_UPDATE_ICON, .proc/block_update_icon) +/datum/element/update_icon_blocker/Detach(datum/source, ...) + UnregisterSignal(source, COMSIG_ATOM_UPDATE_ICON) + return ..() + /datum/element/update_icon_blocker/proc/block_update_icon() SIGNAL_HANDLER diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm index 020cec3eab7..142354fb21e 100644 --- a/code/datums/status_effects/debuffs/debuffs.dm +++ b/code/datums/status_effects/debuffs/debuffs.dm @@ -770,78 +770,6 @@ msg_stage++ -/datum/status_effect/corrosion_curse - id = "corrosion_curse" - status_type = STATUS_EFFECT_REPLACE - alert_type = null - tick_interval = 1 SECONDS - -/datum/status_effect/corrosion_curse/on_creation(mob/living/new_owner, ...) - . = ..() - to_chat(owner, span_userdanger("Your body starts to break apart!")) - -/datum/status_effect/corrosion_curse/tick() - . = ..() - if(!ishuman(owner)) - return - var/mob/living/carbon/human/human_owner = owner - var/chance = rand(0, 100) - switch(chance) - if(0 to 10) - human_owner.vomit() - if(20 to 30) - human_owner.set_timed_status_effect(100 SECONDS, /datum/status_effect/dizziness, only_if_higher = TRUE) - human_owner.set_timed_status_effect(100 SECONDS, /datum/status_effect/jitter, only_if_higher = TRUE) - if(30 to 40) - human_owner.adjustOrganLoss(ORGAN_SLOT_LIVER, 5) - if(40 to 50) - human_owner.adjustOrganLoss(ORGAN_SLOT_HEART, 5, 90) - if(50 to 60) - human_owner.adjustOrganLoss(ORGAN_SLOT_STOMACH, 5) - if(60 to 70) - human_owner.adjustOrganLoss(ORGAN_SLOT_EYES, 10) - if(70 to 80) - human_owner.adjustOrganLoss(ORGAN_SLOT_EARS, 10) - if(80 to 90) - human_owner.adjustOrganLoss(ORGAN_SLOT_LUNGS, 10) - if(90 to 95) - human_owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, 20, 190) - if(95 to 100) - human_owner.adjust_timed_status_effect(12 SECONDS, /datum/status_effect/confusion) - -/datum/status_effect/cloudstruck - id = "cloudstruck" - status_type = STATUS_EFFECT_REPLACE - duration = 3 SECONDS - on_remove_on_mob_delete = TRUE - ///This overlay is applied to the owner for the duration of the effect. - var/mutable_appearance/mob_overlay - -/datum/status_effect/cloudstruck/on_creation(mob/living/new_owner, set_duration) - if(isnum(set_duration)) - duration = set_duration - . = ..() - -/datum/status_effect/cloudstruck/on_apply() - mob_overlay = mutable_appearance('icons/effects/eldritch.dmi', "cloud_swirl", ABOVE_MOB_LAYER) - owner.overlays += mob_overlay - owner.update_appearance() - ADD_TRAIT(owner, TRAIT_BLIND, STATUS_EFFECT_TRAIT) - return TRUE - -/datum/status_effect/cloudstruck/on_remove() - . = ..() - if(QDELETED(owner)) - return - REMOVE_TRAIT(owner, TRAIT_BLIND, STATUS_EFFECT_TRAIT) - if(owner) - owner.overlays -= mob_overlay - owner.update_appearance() - -/datum/status_effect/cloudstruck/Destroy() - . = ..() - QDEL_NULL(mob_overlay) - //Deals with ants covering someone. /datum/status_effect/ants id = "ants" diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm index 378f12264fe..d1355a687ee 100644 --- a/code/modules/antagonists/heretic/heretic_antag.dm +++ b/code/modules/antagonists/heretic/heretic_antag.dm @@ -29,7 +29,7 @@ var/ascended = FALSE /// The path our heretic has chosen. Mostly used for flavor. var/heretic_path = PATH_START - /// A list of how many knowledge points this heretic CURRENTLY has. Used to research. + /// A sum of how many knowledge points this heretic CURRENTLY has. Used to research. var/knowledge_points = 1 /// The time between gaining influence passively. The heretic gain +1 knowledge points every this duration of time. var/passive_gain_timer = 20 MINUTES @@ -39,9 +39,9 @@ var/living_heart_organ_slot = ORGAN_SLOT_HEART /// A list of TOTAL how many sacrifices completed. (Includes high value sacrifices) var/total_sacrifices = 0 - /// A list of TOTAL how many high value sacrifices completed. + /// A list of TOTAL how many high value sacrifices completed. (Heads of staff) var/high_value_sacrifices = 0 - /// Lazy assoc list of [weakrefs to humans] to [image previews of the human]. Humans that we have as sacrifice targets. + /// Lazy assoc list of [refs to humans] to [image previews of the human]. Humans that we have as sacrifice targets. var/list/mob/living/carbon/human/sac_targets /// Whether we're drawing a rune or not var/drawing_rune = FALSE @@ -181,7 +181,6 @@ return ..() /datum/antagonist/heretic/on_removal() - for(var/knowledge_index in researched_knowledge) var/datum/heretic_knowledge/knowledge = researched_knowledge[knowledge_index] knowledge.on_lose(owner.current) @@ -194,15 +193,24 @@ var/mob/living/our_mob = mob_override || owner.current handle_clown_mutation(our_mob, "Ancient knowledge described to you has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") our_mob.faction |= FACTION_HERETIC + RegisterSignal(our_mob, list(COMSIG_MOB_BEFORE_SPELL_CAST, COMSIG_MOB_SPELL_ACTIVATED), .proc/on_spell_cast) RegisterSignal(our_mob, COMSIG_MOB_ITEM_AFTERATTACK, .proc/on_item_afterattack) RegisterSignal(our_mob, COMSIG_MOB_LOGIN, .proc/fix_influence_network) + RegisterSignal(our_mob, COMSIG_LIVING_POST_FULLY_HEAL, .proc/after_fully_healed) /datum/antagonist/heretic/remove_innate_effects(mob/living/mob_override) var/mob/living/our_mob = mob_override || owner.current handle_clown_mutation(our_mob, removing = FALSE) our_mob.faction -= FACTION_HERETIC - UnregisterSignal(our_mob, list(COMSIG_MOB_BEFORE_SPELL_CAST, COMSIG_MOB_SPELL_ACTIVATED, COMSIG_MOB_ITEM_AFTERATTACK, COMSIG_MOB_LOGIN)) + + UnregisterSignal(our_mob, list( + COMSIG_MOB_BEFORE_SPELL_CAST, + COMSIG_MOB_SPELL_ACTIVATED, + COMSIG_MOB_ITEM_AFTERATTACK, + COMSIG_MOB_LOGIN, + COMSIG_LIVING_POST_FULLY_HEAL, + )) /datum/antagonist/heretic/on_body_transfer(mob/living/old_body, mob/living/new_body) . = ..() @@ -327,6 +335,17 @@ GLOB.reality_smash_track.rework_network() +/// Signal proc for [COMSIG_LIVING_POST_FULLY_HEAL], when we get fullhealed / ahealed, +/// all of our organs are "deleted" and regenerated (cause it's a full heal) +/// which unfortunately means we lose our living heart. +/// So, we'll give them some lee-way and give them back the living heart afterwards +/// (Maybe put this behind only admin_revives only? Not sure.) +/datum/antagonist/heretic/proc/after_fully_healed(mob/living/source, admin_revive) + SIGNAL_HANDLER + + var/datum/heretic_knowledge/living_heart/heart_knowledge = get_knowledge(/datum/heretic_knowledge/living_heart) + heart_knowledge.on_research(source) + /** * Create our objectives for our heretic. */ diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm index 6322c9760ed..0fbc8be2383 100644 --- a/code/modules/antagonists/heretic/heretic_knowledge.dm +++ b/code/modules/antagonists/heretic/heretic_knowledge.dm @@ -374,64 +374,123 @@ */ /datum/heretic_knowledge/curse abstract_parent_type = /datum/heretic_knowledge/curse + /// How far can we curse people? + var/max_range = 64 /// The duration of the curse - var/duration = 5 MINUTES - /// Cache list of fingerprints (actual fingerprint strings) we have from our current ritual + var/duration = 1 MINUTES + /// The duration of the curse on people which have a fingerprint or blood sample present + var/duration_modifier = 2 + /// What color do we outline cursed folk with? + var/curse_color = "#dadada" + /// A list of all the fingerprints that were found on our atoms, in our last go at the ritual var/list/fingerprints + /// A list of all the blood samples that were found on our atoms, in our last go at the ritual + var/list/blood_samples /datum/heretic_knowledge/curse/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc) fingerprints = list() - for(var/atom/requirements as anything in atoms) - fingerprints |= GET_ATOM_FINGERPRINTS(requirements) - list_clear_nulls(fingerprints) + blood_samples = list() + for(var/atom/requirement as anything in atoms) + for(var/print in GET_ATOM_FINGERPRINTS(requirement)) + fingerprints[print] = 1 - // No fingerprints? No ritual - if(!length(fingerprints)) - loc.balloon_alert(user, "ritual failed, no fingerprints!") - return FALSE + for(var/blood in GET_ATOM_BLOOD_DNA(requirement)) + blood_samples[blood] = 1 return TRUE /datum/heretic_knowledge/curse/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc) - var/list/compiled_list = list() + // Potential targets is an assoc list of [names] to [human mob ref]. + var/list/potential_targets = list() + // Boosted targets is a list of human mob references. + var/list/boosted_targets = list() - for(var/mob/living/carbon/human/human_to_check as anything in GLOB.human_list) - if(fingerprints[md5(human_to_check.dna.unique_identity)]) - compiled_list |= human_to_check.real_name - compiled_list[human_to_check.real_name] = human_to_check + for(var/datum/mind/crewmember as anything in get_crewmember_minds()) + var/mob/living/carbon/human/human_to_check = crewmember.current + if(!istype(human_to_check) || human_to_check.stat == DEAD || !human_to_check.dna) + continue + var/their_prints = md5(human_to_check.dna.unique_identity) + var/their_blood = human_to_check.dna.unique_enzymes + // Having their fingerprints or blood present will boost the curse + // and also not run any z or dist checks, as a bonus for those going beyond + if(fingerprints[their_prints] || blood_samples[their_blood]) + boosted_targets += human_to_check + potential_targets["[human_to_check.real_name] (Boosted)"] = human_to_check + continue - if(!length(compiled_list)) - loc.balloon_alert(user, "ritual failed, no fingerprints!") - return FALSE + // No boost present, so we should be a little stricter moving forward + var/turf/check_turf = get_turf(human_to_check) + // We have to match z-levels. + // Otherwise, you could probably hard own miners, which is funny but mean. + // Multi-z stations technically work though. + if(!is_valid_z_level(check_turf, loc)) + continue + // Also has to abide by our max range. + if(get_dist(check_turf, loc) > max_range) + continue - var/chosen_mob = tgui_input_list(user, "Select the person you wish to curse", "Eldritch Curse", sort_list(compiled_list)) + potential_targets[human_to_check.real_name] = human_to_check + + var/chosen_mob = tgui_input_list(user, "Select the victim you wish to curse.", name, sort_list(potential_targets, /proc/cmp_text_asc)) if(isnull(chosen_mob)) return FALSE - var/mob/living/carbon/human/to_curse = compiled_list[chosen_mob] + var/mob/living/carbon/human/to_curse = potential_targets[chosen_mob] if(QDELETED(to_curse)) loc.balloon_alert(user, "ritual failed, invalid choice!") return FALSE - log_combat(user, to_curse, "cursed via heretic ritual", addition = "([name])") - curse(to_curse) - addtimer(CALLBACK(src, .proc/uncurse, to_curse), duration) + // Yes, you COULD curse yourself, not sure why but you could + if(to_curse == user) + var/are_you_sure = tgui_alert(user, "Are you sure you want to curse yourself?", name, list("Yes", "No")) + if(are_you_sure != "Yes") + return FALSE + + var/boosted = (to_curse in boosted_targets) + var/turf/curse_turf = get_turf(to_curse) + if(!boosted && (!is_valid_z_level(curse_turf, loc) || get_dist(curse_turf, loc) > max_range * 1.5)) // Give a bit of leeway on max range for people moving around + loc.balloon_alert(user, "ritual failed, too far!") + return FALSE + + if(to_curse.can_block_magic(MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY, charge_cost = 0)) + to_chat(to_curse, span_warning("You feel a ghastly chill, but the feeling passes shortly.")) + return TRUE + + log_combat(user, to_curse, "cursed via heretic ritual", addition = "([boosted ? "Boosted" : ""] [name])") + curse(to_curse, boosted) + to_chat(user, span_hierophant("You cast a[boosted ? "n empowered":""] [name] upon [to_curse.real_name].")) + + fingerprints = null + blood_samples = null return TRUE /** * Calls a curse onto [chosen_mob]. */ -/datum/heretic_knowledge/curse/proc/curse(mob/living/carbon/human/chosen_mob) - SHOULD_CALL_PARENT(FALSE) - CRASH("[type] did not implement curse()!") +/datum/heretic_knowledge/curse/proc/curse(mob/living/carbon/human/chosen_mob, boosted = FALSE) + SHOULD_CALL_PARENT(TRUE) + + addtimer(CALLBACK(src, .proc/uncurse, chosen_mob, boosted), duration * (boosted ? duration_modifier : 1)) + + if(!curse_color) + return + + chosen_mob.add_filter(name, 2, list("type" = "outline", "color" = curse_color, "size" = 1)) /** * Removes a curse from [chosen_mob]. Used in timers / callbacks. */ -/datum/heretic_knowledge/curse/proc/uncurse(mob/living/carbon/human/chosen_mob) - SHOULD_CALL_PARENT(FALSE) - CRASH("[type] did not implement uncurse()!") +/datum/heretic_knowledge/curse/proc/uncurse(mob/living/carbon/human/chosen_mob, boosted = FALSE) + SHOULD_CALL_PARENT(TRUE) + + if(QDELETED(chosen_mob)) + return + + if(!curse_color) + return + + chosen_mob.remove_filter(name) /* * A knowledge subtype lets the heretic summon a monster with the ritual. diff --git a/code/modules/antagonists/heretic/heretic_living_heart.dm b/code/modules/antagonists/heretic/heretic_living_heart.dm index 8609a0d96de..29e3c2aedbc 100644 --- a/code/modules/antagonists/heretic/heretic_living_heart.dm +++ b/code/modules/antagonists/heretic/heretic_living_heart.dm @@ -14,14 +14,13 @@ var/datum/action/item_action/organ_action/track_target/action /// The icon of the heart before we made it a living heart. var/old_icon - /// The icon_state of the heart before we made it a living heart. - var/old_icon_state /datum/component/living_heart/Initialize() if(!isorgan(parent)) return COMPONENT_INCOMPATIBLE var/obj/item/organ/organ_parent = parent + // These are incompatible for "balance" reasons, not due to code limitations. if(organ_parent.status != ORGAN_ORGANIC || (organ_parent.organ_flags & ORGAN_SYNTHETIC)) return COMPONENT_INCOMPATIBLE @@ -34,12 +33,10 @@ ADD_TRAIT(parent, TRAIT_LIVING_HEART, REF(src)) RegisterSignal(parent, COMSIG_ORGAN_REMOVED, .proc/on_organ_removed) - // It's not technically visible, - // but the organ sprite shows up in the action - // So we'll do this anyways + // The heart's not technically visible (and is never visible to anyone besides the heretic), + // but the organ sprite shows up in the organ action - so we'll do this anyways parent.AddElement(/datum/element/update_icon_blocker) old_icon = organ_parent.icon - old_icon_state = organ_parent.icon_state organ_parent.icon = 'icons/obj/eldritch.dmi' organ_parent.icon_state = "living_heart" @@ -50,10 +47,14 @@ REMOVE_TRAIT(parent, TRAIT_LIVING_HEART, REF(src)) UnregisterSignal(parent, COMSIG_ORGAN_REMOVED) + // Restore the heart to look normal parent.RemoveElement(/datum/element/update_icon_blocker) var/obj/item/organ/organ_parent = parent organ_parent.icon = old_icon - organ_parent.icon_state = old_icon_state + organ_parent.icon_state = initial(organ_parent.icon_state) + + // Sets the icon state to be the correct state + organ_parent.update_appearance(UPDATE_ICON_STATE) return ..() @@ -82,7 +83,7 @@ /// Whether the target radial is currently opened. var/radial_open = FALSE /// How long we have to wait between tracking uses. - var/track_cooldown_lenth = 8 SECONDS + var/track_cooldown_lenth = 4 SECONDS /// The cooldown between button uses. COOLDOWN_DECLARE(track_cooldown) @@ -106,12 +107,15 @@ if(radial_open) return FALSE + return TRUE + /datum/action/item_action/organ_action/track_target/Trigger(trigger_flags) . = ..() if(!.) return var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(owner) + var/datum/heretic_knowledge/sac_knowledge = heretic_datum.get_knowledge(/datum/heretic_knowledge/hunt_and_sacrifice) if(!LAZYLEN(heretic_datum.sac_targets)) owner.balloon_alert(owner, "no targets, visit a rune!") return TRUE @@ -147,14 +151,71 @@ return FALSE COOLDOWN_START(src, track_cooldown, track_cooldown_lenth) - var/balloon_message = "error text!" - + UpdateButtons() + addtimer(CALLBACK(src, .proc/UpdateButtons), track_cooldown_lenth + 1) playsound(owner, 'sound/effects/singlebeat.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE) - if(isturf(tracked_mob.loc) && owner.z != tracked_mob.z) + owner.balloon_alert(owner, get_balloon_message(tracked_mob)) + + // Let them know how to sacrifice people if they're able to be sac'd + if(tracked_mob.stat == DEAD) + to_chat(owner, span_hierophant("[tracked_mob] is dead. Bring them to a transmutation rune \ + and invoke \"[sac_knowledge.name]\" to sacrifice them!")) + + return TRUE + +/// Callback for the radial to ensure it's closed when not allowed. +/datum/action/item_action/organ_action/track_target/proc/check_menu() + if(QDELETED(src)) + return FALSE + if(!IS_HERETIC(owner)) + return FALSE + if(!HAS_TRAIT(target, TRAIT_LIVING_HEART)) + return FALSE + return TRUE + +/// Gets the balloon message for who we're tracking. +/datum/action/item_action/organ_action/track_target/proc/get_balloon_message(mob/living/carbon/human/tracked_mob) + var/balloon_message = "error text!" + var/turf/their_turf = get_turf(tracked_mob) + var/turf/our_turf = get_turf(owner) + var/their_z = their_turf?.z + var/our_z = our_turf?.z + + // One of us is in somewhere we shouldn't be + if(!our_z || !their_z) + // "Hell if I know" balloon_message = "on another plane!" + + // They're not on the same z-level as us + else if(our_z != their_z) + // They're on the station + if(is_station_level(their_z)) + // We're on a multi-z station + if(is_station_level(our_z)) + if(our_z > their_z) + balloon_message = "below you!" + else + balloon_message = "above you!" + // We're off station, they're not + else + balloon_message = "on station!" + + // Mining + else if(is_mining_level(their_z)) + balloon_message = "on lavaland!" + + // In the gateway + else if(is_away_level(their_z) || is_secret_level(their_z)) + balloon_message = "beyond the gateway!" + + // They're somewhere we probably can't get too - sacrifice z-level, centcom, etc + else + balloon_message = "on another plane!" + + // They're on the same z-level as us! else - var/dist = get_dist(get_turf(owner), get_turf(tracked_mob)) - var/dir = get_dir(get_turf(owner), get_turf(tracked_mob)) + var/dist = get_dist(our_turf, their_turf) + var/dir = get_dir(our_turf, their_turf) switch(dist) if(0 to 15) @@ -169,15 +230,4 @@ if(tracked_mob.stat == DEAD) balloon_message = "they're dead, " + balloon_message - owner.balloon_alert(owner, balloon_message) - return TRUE - -/// Callback for the radial to ensure it's closed when not allowed. -/datum/action/item_action/organ_action/track_target/proc/check_menu() - if(QDELETED(src)) - return FALSE - if(!IS_HERETIC(owner)) - return FALSE - if(!HAS_TRAIT(target, TRAIT_LIVING_HEART)) - return FALSE - return TRUE + return balloon_message diff --git a/code/modules/antagonists/heretic/knowledge/rust_lore.dm b/code/modules/antagonists/heretic/knowledge/rust_lore.dm index 1da2a83b780..bb2c36cb4ab 100644 --- a/code/modules/antagonists/heretic/knowledge/rust_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/rust_lore.dm @@ -122,12 +122,17 @@ if(!HAS_TRAIT(our_turf, TRAIT_RUSTY)) return + // Heals all damage + Stamina source.adjustBruteLoss(-2, FALSE) source.adjustFireLoss(-2, FALSE) - source.adjustToxLoss(-2, FALSE, forced = TRUE) + source.adjustToxLoss(-2, FALSE, forced = TRUE) // Slimes are people to source.adjustOxyLoss(-0.5, FALSE) source.adjustStaminaLoss(-2) - source.AdjustAllImmobility(-5) + // Reduces duration of stuns/etc + source.AdjustAllImmobility(-0.5 SECONDS) + // Heals blood loss + if(source.blood_volume < BLOOD_VOLUME_NORMAL) + source.blood_volume += 2.5 * delta_time /datum/heretic_knowledge/mark/rust_mark name = "Mark of Rust" diff --git a/code/modules/antagonists/heretic/knowledge/side_ash_flesh.dm b/code/modules/antagonists/heretic/knowledge/side_ash_flesh.dm index 279027554b3..bdfff87e15e 100644 --- a/code/modules/antagonists/heretic/knowledge/side_ash_flesh.dm +++ b/code/modules/antagonists/heretic/knowledge/side_ash_flesh.dm @@ -19,10 +19,9 @@ /datum/heretic_knowledge/curse/paralysis name = "Curse of Paralysis" - desc = "Allows you to transmute a hatchet, a left and right leg, \ - and an item containing fingerprints to cast a curse of immobility \ - on one of the fingerprint's owners for five minutes. While cursed, \ - the victim will be unable to walk." + desc = "Allows you to transmute a hatchet and both a left and right leg to cast a curse of immobility on a crew member. \ + While cursed, the victim will be unable to walk. You can additionally supply an item that a victim has touched \ + or is covered in the victim's blood to empower the curse." gain_text = "The flesh of humanity is weak. Make them bleed. Show them their fragility." next_knowledge = list( /datum/heretic_knowledge/mad_mask, @@ -33,27 +32,31 @@ /obj/item/bodypart/r_leg = 1, /obj/item/hatchet = 1, ) - duration = 5 MINUTES + duration = 3 MINUTES + duration_modifier = 2 + curse_color = "#f19a9a" cost = 1 route = PATH_SIDE -/datum/heretic_knowledge/curse/paralysis/curse(mob/living/carbon/human/chosen_mob) +/datum/heretic_knowledge/curse/paralysis/curse(mob/living/carbon/human/chosen_mob, boosted = FALSE) if(chosen_mob.usable_legs <= 0) // What're you gonna do, curse someone who already can't walk? to_chat(chosen_mob, span_notice("You feel a slight pain for a moment, but it passes shortly. Odd.")) - else - to_chat(chosen_mob, span_danger("You suddenly lose feeling in your leg[chosen_mob.usable_legs == 1 ? "":"s"]!")) + return + to_chat(chosen_mob, span_danger("You suddenly lose feeling in your leg[chosen_mob.usable_legs == 1 ? "":"s"]!")) ADD_TRAIT(chosen_mob, TRAIT_PARALYSIS_L_LEG, type) ADD_TRAIT(chosen_mob, TRAIT_PARALYSIS_R_LEG, type) + return ..() + +/datum/heretic_knowledge/curse/paralysis/uncurse(mob/living/carbon/human/chosen_mob, boosted = FALSE) + if(QDELETED(chosen_mob)) + return -/datum/heretic_knowledge/curse/paralysis/uncurse(mob/living/carbon/human/chosen_mob) REMOVE_TRAIT(chosen_mob, TRAIT_PARALYSIS_L_LEG, type) REMOVE_TRAIT(chosen_mob, TRAIT_PARALYSIS_R_LEG, type) - - if(chosen_mob.usable_legs <= 0) // What're you gonna do, curse someone who already can't walk? - to_chat(chosen_mob, span_notice("The slight pain returns, but disperses shortly.")) - else - to_chat(chosen_mob, span_notice("You regain feeling in your leg[chosen_mob.usable_legs == 1 ? "":"s"]!")) + if(chosen_mob.usable_legs > 1) + to_chat(chosen_mob, span_green("You regain feeling in your leg[chosen_mob.usable_legs == 1 ? "":"s"]!")) + return ..() /datum/heretic_knowledge/summon/ashy name = "Ashen Ritual" diff --git a/code/modules/antagonists/heretic/knowledge/side_rust_ash.dm b/code/modules/antagonists/heretic/knowledge/side_rust_ash.dm index 0e15e167d2e..2467e854b17 100644 --- a/code/modules/antagonists/heretic/knowledge/side_rust_ash.dm +++ b/code/modules/antagonists/heretic/knowledge/side_rust_ash.dm @@ -20,10 +20,9 @@ /datum/heretic_knowledge/curse/corrosion name = "Curse of Corrosion" - desc = "Allows you to transmute wirecutters, a pool of vomit, a heart, \ - and an item containing fingerprints to cast a curse of plague \ - on one of the fingerprint's owners for two minutes. While cursed, \ - the victim will repeatedly vomit while their organs will take constant damage." + desc = "Allows you to transmute wirecutters, a pool of vomit, and a heart to cast a curse of sickness on a crew member.\ + While cursed, the victim will repeatedly vomit while their organs will take constant damage. You can additionally supply an item \ + that a victim has touched or is covered in the victim's blood to empower the curse." gain_text = "The body of humanity is temporary. Their weaknesses cannot be stopped, like iron falling to rust. Show them all." next_knowledge = list( /datum/heretic_knowledge/mad_mask, @@ -34,17 +33,24 @@ /obj/effect/decal/cleanable/vomit = 1, /obj/item/organ/internal/heart = 1, ) - duration = 2 MINUTES + duration = 0.5 MINUTES + duration_modifier = 4 + curse_color = "#c1ffc9" cost = 1 route = PATH_SIDE -/datum/heretic_knowledge/curse/corrosion/curse(mob/living/carbon/human/chosen_mob) - to_chat(chosen_mob, span_danger("You feel very ill.")) +/datum/heretic_knowledge/curse/corrosion/curse(mob/living/carbon/human/chosen_mob, boosted = FALSE) + to_chat(chosen_mob, span_danger("You feel very ill...")) chosen_mob.apply_status_effect(/datum/status_effect/corrosion_curse) + return ..() + +/datum/heretic_knowledge/curse/corrosion/uncurse(mob/living/carbon/human/chosen_mob, boosted = FALSE) + if(QDELETED(chosen_mob)) + return -/datum/heretic_knowledge/curse/corrosion/uncurse(mob/living/carbon/human/chosen_mob) chosen_mob.remove_status_effect(/datum/status_effect/corrosion_curse) - to_chat(chosen_mob, span_notice("You start to feel better.")) + to_chat(chosen_mob, span_green("You start to feel better.")) + return ..() /datum/heretic_knowledge/summon/rusty name = "Rusted Ritual" diff --git a/code/modules/antagonists/heretic/magic/rust_wave.dm b/code/modules/antagonists/heretic/magic/rust_wave.dm index 9a90b7ffd7b..e7f4d267d7e 100644 --- a/code/modules/antagonists/heretic/magic/rust_wave.dm +++ b/code/modules/antagonists/heretic/magic/rust_wave.dm @@ -5,6 +5,7 @@ background_icon_state = "bg_ecult" icon_icon = 'icons/mob/actions/actions_ecult.dmi' button_icon_state = "entropic_plume" + sound = 'sound/magic/forcewall.ogg' school = SCHOOL_FORBIDDEN cooldown_time = 30 SECONDS @@ -27,10 +28,8 @@ if(victim.can_block_magic(antimagic_flags) || IS_HERETIC_OR_MONSTER(victim)) return victim.apply_status_effect(/datum/status_effect/amok) - victim.apply_status_effect(/datum/status_effect/cloudstruck, (level * 1 SECONDS)) - if(iscarbon(victim)) - var/mob/living/carbon/carbon_victim = victim - carbon_victim.reagents?.add_reagent(/datum/reagent/eldritch, min(1, 6 - level)) + victim.apply_status_effect(/datum/status_effect/cloudstruck, level * 1 SECONDS) + victim.reagents?.add_reagent(/datum/reagent/eldritch, max(1, 6 - level)) /datum/action/cooldown/spell/cone/staggered/entropic_plume/calculate_cone_shape(current_level) if(current_level == cone_levels) diff --git a/code/modules/antagonists/heretic/status_effects/debuffs.dm b/code/modules/antagonists/heretic/status_effects/debuffs.dm index 93d4e680123..751e3dcb548 100644 --- a/code/modules/antagonists/heretic/status_effects/debuffs.dm +++ b/code/modules/antagonists/heretic/status_effects/debuffs.dm @@ -39,20 +39,99 @@ tick_interval = 1 SECONDS /datum/status_effect/amok/on_apply(mob/living/afflicted) - . = ..() to_chat(owner, span_boldwarning("You feel filled with a rage that is not your own!")) + return TRUE /datum/status_effect/amok/tick() - . = ..() var/prev_combat_mode = owner.combat_mode owner.set_combat_mode(TRUE) + // If we're holding a gun, expand the range a bit. + // Otherwise, just look for adjacent targets + var/search_radius = isgun(owner.get_active_held_item()) ? 3 : 1 + var/list/mob/living/targets = list() - for(var/mob/living/potential_target in oview(owner, 1)) + for(var/mob/living/potential_target in oview(owner, search_radius)) if(IS_HERETIC_OR_MONSTER(potential_target)) continue targets += potential_target + if(LAZYLEN(targets)) owner.log_message(" attacked someone due to the amok debuff.", LOG_ATTACK) //the following attack will log itself owner.ClickOn(pick(targets)) + owner.set_combat_mode(prev_combat_mode) + +/datum/status_effect/cloudstruck + id = "cloudstruck" + status_type = STATUS_EFFECT_REPLACE + alert_type = null + duration = 3 SECONDS + on_remove_on_mob_delete = TRUE + ///This overlay is applied to the owner for the duration of the effect. + var/static/mutable_appearance/mob_overlay + +/datum/status_effect/cloudstruck/on_creation(mob/living/new_owner, duration = 10 SECONDS) + src.duration = duration + if(!mob_overlay) + mob_overlay = mutable_appearance('icons/effects/eldritch.dmi', "cloud_swirl", ABOVE_MOB_LAYER) + return ..() + +/datum/status_effect/cloudstruck/on_apply() + owner.add_overlay(mob_overlay) + owner.become_blind(STATUS_EFFECT_TRAIT) + owner.update_blindness() // become_blind is supposed to call this but it doesn't work for some reason because blindness is a fuck + return TRUE + +/datum/status_effect/cloudstruck/on_remove() + if(QDELETED(owner)) + return + owner.cure_blind(STATUS_EFFECT_TRAIT) + owner.update_blindness() + owner.cut_overlay(mob_overlay) + +/datum/status_effect/corrosion_curse + id = "corrosion_curse" + status_type = STATUS_EFFECT_REPLACE + alert_type = null + tick_interval = 1 SECONDS + +/datum/status_effect/corrosion_curse/on_apply() + to_chat(owner, span_userdanger("Your body starts to break apart!")) + return TRUE + +/datum/status_effect/corrosion_curse/tick() + . = ..() + if(!ishuman(owner)) + return + var/mob/living/carbon/human/human_owner = owner + var/chance = rand(0, 100) + switch(chance) + if(0 to 10) + human_owner.vomit() + if(20 to 30) + human_owner.set_timed_status_effect(100 SECONDS, /datum/status_effect/dizziness, only_if_higher = TRUE) + human_owner.set_timed_status_effect(100 SECONDS, /datum/status_effect/jitter, only_if_higher = TRUE) + if(30 to 40) + // Don't fully kill liver that's important + human_owner.adjustOrganLoss(ORGAN_SLOT_LIVER, 10, 90) + if(40 to 50) + // Don't fully kill heart that's important + human_owner.adjustOrganLoss(ORGAN_SLOT_HEART, 10, 90) + if(50 to 60) + // You can fully kill the stomach that's not crucial + human_owner.adjustOrganLoss(ORGAN_SLOT_STOMACH, 10) + if(60 to 70) + // Same with eyes + human_owner.adjustOrganLoss(ORGAN_SLOT_EYES, 5) + if(70 to 80) + // And same with ears + human_owner.adjustOrganLoss(ORGAN_SLOT_EARS, 10) + if(80 to 90) + // But don't fully kill lungs that's usually important + human_owner.adjustOrganLoss(ORGAN_SLOT_LUNGS, 10, 90) + if(90 to 95) + // And definitely don't fully kil brains + human_owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, 20, 190) + if(95 to 100) + human_owner.adjust_timed_status_effect(12 SECONDS, /datum/status_effect/confusion, max_duration = 24 SECONDS) diff --git a/code/modules/spells/spell_types/cone/_cone.dm b/code/modules/spells/spell_types/cone/_cone.dm index 0832b01b97d..e1439be824d 100644 --- a/code/modules/spells/spell_types/cone/_cone.dm +++ b/code/modules/spells/spell_types/cone/_cone.dm @@ -32,9 +32,9 @@ for(var/atom/movable/movable_content as anything in target_turf) if(isobj(movable_content)) - do_obj_cone_effect(movable_content, level) + do_obj_cone_effect(movable_content, caster, level) else if(isliving(movable_content)) - do_mob_cone_effect(movable_content, level) + do_mob_cone_effect(movable_content, caster, level) ///This proc deterimines how the spell will affect turfs. /datum/action/cooldown/spell/cone/proc/do_turf_cone_effect(turf/target_turf, atom/caster, level)