diff --git a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_datum.dm b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_datum.dm index ee6b9de5c34d..8d7a0af9b5ce 100644 --- a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_datum.dm +++ b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_datum.dm @@ -41,6 +41,8 @@ var/frenzy_threshold = FRENZY_THRESHOLD_ENTER ///If we are currently in a Frenzy var/frenzied = FALSE + /// Whether the death handling code is active or not. + var/handling_death = FALSE ///ALL Powers currently owned var/list/datum/action/cooldown/bloodsucker/powers = list() @@ -96,6 +98,14 @@ TRAIT_HARDLY_WOUNDED, TRAIT_NO_MIRROR_REFLECTION ) + /// Traits applied during Torpor. + var/static/list/torpor_traits = list( + TRAIT_DEATHCOMA, + TRAIT_FAKEDEATH, + TRAIT_NODEATH, + TRAIT_RESISTHIGHPRESSURE, + TRAIT_RESISTLOWPRESSURE + ) /** * Apply innate effects is everything given to the mob diff --git a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_life.dm b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_life.dm index 4c370841d8de..e7a1ac56cf5c 100644 --- a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_life.dm +++ b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_life.dm @@ -4,16 +4,15 @@ /// Runs from COMSIG_LIVING_LIFE, handles Bloodsucker constant proccesses. /datum/antagonist/bloodsucker/proc/LifeTick(mob/living/source, seconds_per_tick, times_fired) SIGNAL_HANDLER - - if(isbrain(owner.current)) + if(isbrain(owner?.current)) return - if(!owner) - INVOKE_ASYNC(src, PROC_REF(HandleDeath)) + if(QDELETED(owner)) + INVOKE_ASYNC(src, PROC_REF(handle_death)) return - if(HAS_TRAIT_FROM(owner.current, TRAIT_NODEATH, TORPOR_TRAIT)) + if(is_in_torpor()) check_end_torpor() // Deduct Blood - if(owner.current.stat == CONSCIOUS && !HAS_TRAIT(owner.current, TRAIT_IMMOBILIZED) && !HAS_TRAIT_FROM(owner.current, TRAIT_NODEATH, TORPOR_TRAIT)) + if(owner.current.stat == CONSCIOUS && !HAS_TRAIT(owner.current, TRAIT_IMMOBILIZED) && !is_in_torpor()) INVOKE_ASYNC(src, PROC_REF(AddBloodVolume), -BLOODSUCKER_PASSIVE_BLOOD_DRAIN) // -.1 currently if(HandleHealing()) if((COOLDOWN_FINISHED(src, bloodsucker_spam_healing)) && bloodsucker_blood_volume > 0) @@ -23,17 +22,13 @@ SEND_SIGNAL(src, COMSIG_BLOODSUCKER_ON_LIFETICK) INVOKE_ASYNC(src, PROC_REF(HandleStarving)) INVOKE_ASYNC(src, PROC_REF(update_blood)) - INVOKE_ASYNC(src, PROC_REF(update_hud)) /datum/antagonist/bloodsucker/proc/on_death(mob/living/source, gibbed) SIGNAL_HANDLER - RegisterSignal(owner.current, COMSIG_LIVING_REVIVE, PROC_REF(on_revive)) - RegisterSignal(src, COMSIG_BLOODSUCKER_ON_LIFETICK, PROC_REF(HandleDeath)) - -/datum/antagonist/bloodsucker/proc/on_revive(mob/living/source) - UnregisterSignal(owner.current, COMSIG_LIVING_REVIVE) - UnregisterSignal(src, COMSIG_BLOODSUCKER_ON_LIFETICK) + if(source.stat != DEAD) // weirdness shield + return + INVOKE_ASYNC(src, PROC_REF(handle_death)) /** * ## BLOOD STUFF @@ -71,7 +66,7 @@ // Reagents (NOT Blood!) if(target.reagents && target.reagents.total_volume) target.reagents.trans_to(owner.current, INGEST, 1) // Run transfer of 1 unit of reagent from them to me. - owner.current.playsound_local(null, 'sound/effects/singlebeat.ogg', 40, 1) // Play THIS sound for user only. The "null" is where turf would go if a location was needed. Null puts it right in their head. + owner.current.playsound_local(null, 'sound/effects/singlebeat.ogg', vol = 40, vary = TRUE) // Play THIS sound for user only. The "null" is where turf would go if a location was needed. Null puts it right in their head. total_blood_drank += blood_taken return blood_taken @@ -83,7 +78,7 @@ /datum/antagonist/bloodsucker/proc/HandleHealing(mult = 1) var/actual_regen = bloodsucker_regen_rate + additional_regen // Don't heal if I'm staked or on Masquerade (+ not in a Coffin). Masqueraded Bloodsuckers in a Coffin however, will heal. - if(owner.current.am_staked() || (HAS_TRAIT(owner.current, TRAIT_MASQUERADE) && !HAS_TRAIT_FROM(owner.current, TRAIT_NODEATH, TORPOR_TRAIT))) + if(owner.current.am_staked() || (HAS_TRAIT(owner.current, TRAIT_MASQUERADE) && !is_in_torpor())) return FALSE owner.current.adjustCloneLoss(-1 * (actual_regen * 4) * mult, 0) owner.current.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1 * (actual_regen * 4) * mult) //adjustBrainLoss(-1 * (actual_regen * 4) * mult, 0) @@ -95,7 +90,7 @@ var/fireheal = 0 // BURN: Heal in Coffin while Fakedeath, or when damage above maxhealth (you can never fully heal fire) // Checks if you're in a coffin here, additionally checks for Torpor right below it. var/amInCoffin = istype(user.loc, /obj/structure/closet/crate/coffin) - if(amInCoffin && HAS_TRAIT_FROM(user, TRAIT_NODEATH, TORPOR_TRAIT)) + if(amInCoffin && is_in_torpor()) if(HAS_TRAIT(owner.current, TRAIT_MASQUERADE) && (COOLDOWN_FINISHED(src, bloodsucker_spam_healing))) to_chat(user, span_alert("You do not heal while your Masquerade ability is active.")) COOLDOWN_START(src, bloodsucker_spam_healing, BLOODSUCKER_SPAM_MASQUERADE) @@ -108,14 +103,14 @@ if(check_limbs(costMult)) return TRUE // In Torpor, but not in a Coffin? Heal faster anyways. - else if(HAS_TRAIT_FROM(user, TRAIT_NODEATH, TORPOR_TRAIT)) + else if(is_in_torpor()) fireheal = min(user.getFireLoss_nonProsthetic(), actual_regen) / 1.2 // 20% slower than being in a coffin mult *= 3 // Heal if Damaged if((bruteheal + fireheal > 0) && mult != 0) // Just a check? Don't heal/spend, and return. // We have damage. Let's heal (one time) - user.adjustBruteLoss(-bruteheal * mult, forced=TRUE) // Heal BRUTE / BURN in random portions throughout the body. - user.adjustFireLoss(-fireheal * mult, forced=TRUE) + user.adjustBruteLoss(-bruteheal * mult, forced = TRUE) // Heal BRUTE / BURN in random portions throughout the body. + user.adjustFireLoss(-fireheal * mult, forced = TRUE) AddBloodVolume(((bruteheal * -0.5) + (fireheal * -1)) * costMult * mult) // Costs blood to heal return TRUE @@ -131,7 +126,7 @@ var/obj/item/bodypart/missing_bodypart = user.get_bodypart(missing_limb) // 2) Limb returns Damaged missing_bodypart.brute_dam = 60 to_chat(user, span_notice("Your flesh knits as it regrows your [missing_bodypart]!")) - playsound(user, 'sound/magic/demon_consume.ogg', 50, TRUE) + playsound(user, 'sound/magic/demon_consume.ogg', vol = 50, vary = TRUE) return TRUE /* @@ -184,22 +179,32 @@ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/// FINAL DEATH -/datum/antagonist/bloodsucker/proc/HandleDeath() +/// A wrapper around final death code, to prevent multiple calls, +/// and to ensure any sort of weird runtimes won't result in [handling_death] being broken. +/datum/antagonist/bloodsucker/proc/handle_death() + if(handling_death) + return + handling_death = TRUE + do_handle_death() + handling_death = FALSE + +/// FINAL DEATH. +/// Don't call this directly, use handle_death(). +/datum/antagonist/bloodsucker/proc/do_handle_death() // Not "Alive"? - if(!owner.current) - FinalDeath() + if(QDELETED(owner.current)) + final_death() return // Fire Damage? (above double health) if(owner.current.getFireLoss() >= owner.current.maxHealth * 2.5) - FinalDeath() + final_death() return // Staked while "Temp Death" or Asleep if(owner.current.StakeCanKillMe() && owner.current.am_staked()) - FinalDeath() + final_death() return // Temporary Death? Convert to Torpor. - if(HAS_TRAIT_FROM(owner.current, TRAIT_NODEATH, TORPOR_TRAIT)) + if(is_in_torpor()) return to_chat(owner.current, span_danger("Your immortal body will not yet relinquish your soul to the abyss. You enter Torpor.")) check_begin_torpor(TRUE) @@ -215,7 +220,7 @@ if(bloodsucker_blood_volume >= FRENZY_THRESHOLD_EXIT && frenzied) owner.current.remove_status_effect(/datum/status_effect/frenzy) // BLOOD_VOLUME_BAD: [224] - Jitter - if(bloodsucker_blood_volume < BLOOD_VOLUME_BAD && prob(0.5) && !HAS_TRAIT_FROM(owner.current, TRAIT_NODEATH, TORPOR_TRAIT) && !HAS_TRAIT(owner.current, TRAIT_MASQUERADE)) + if(bloodsucker_blood_volume < BLOOD_VOLUME_BAD && prob(0.5) && !is_in_torpor() && !HAS_TRAIT(owner.current, TRAIT_MASQUERADE)) owner.current.set_timed_status_effect(3 SECONDS, /datum/status_effect/jitter, only_if_higher = TRUE) // BLOOD_VOLUME_SURVIVE: [122] - Blur Vision if(bloodsucker_blood_volume < BLOOD_VOLUME_SURVIVE) @@ -253,13 +258,12 @@ owner.current.blood_volume = bloodsucker_blood_volume /// Gibs the Bloodsucker, roundremoving them. -/datum/antagonist/bloodsucker/proc/FinalDeath() +/datum/antagonist/bloodsucker/proc/final_death() // If we have no body, end here. - if(!owner.current) + if(QDELETED(owner.current)) return UnregisterSignal(src, list( COMSIG_BLOODSUCKER_ON_LIFETICK, - COMSIG_LIVING_REVIVE, COMSIG_LIVING_LIFE, COMSIG_LIVING_DEATH, )) diff --git a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm index 198af5c7a787..3ddf790cd1c2 100644 --- a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm +++ b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm @@ -87,7 +87,7 @@ ///Disables all powers, accounting for torpor /datum/antagonist/bloodsucker/proc/DisableAllPowers(forced = FALSE) for(var/datum/action/cooldown/bloodsucker/power as anything in powers) - if(forced || ((power.check_flags & BP_CANT_USE_IN_TORPOR) && HAS_TRAIT_FROM(owner.current, TRAIT_NODEATH, TORPOR_TRAIT))) + if(forced || ((power.check_flags & BP_CANT_USE_IN_TORPOR) && is_in_torpor())) if(power.active) power.DeactivatePower() diff --git a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_sol.dm b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_sol.dm index 93e5af1852f5..252408b50d54 100644 --- a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_sol.dm +++ b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_sol.dm @@ -66,7 +66,7 @@ if(owner.current.am_staked() && COOLDOWN_FINISHED(src, bloodsucker_spam_sol_burn)) to_chat(owner.current, span_userdanger("You are staked! Remove the offending weapon from your heart before sleeping.")) COOLDOWN_START(src, bloodsucker_spam_sol_burn, BLOODSUCKER_SPAM_SOL) //This should happen twice per Sol - if(!HAS_TRAIT_FROM(owner.current, TRAIT_NODEATH, TORPOR_TRAIT)) + if(!is_in_torpor()) check_begin_torpor(TRUE) owner.current.add_mood_event("vampsleep", /datum/mood_event/coffinsleep) return @@ -106,7 +106,7 @@ * Torpor is triggered by: * - Being in a Coffin while Sol is on, dealt with by Sol * - Entering a Coffin with more than 10 combined Brute/Burn damage, dealt with by /closet/crate/coffin/close() [bloodsucker_coffin.dm] - * - Death, dealt with by /HandleDeath() + * - Death, dealt with by /handle_death() * Torpor is ended by: * - Having less than 10 Brute damage while OUTSIDE of your Coffin while it isnt Sol. * - Having less than 10 Brute & Burn Combined while INSIDE of your Coffin while it isnt Sol. @@ -122,7 +122,7 @@ var/total_burn = user.getFireLoss_nonProsthetic() var/total_damage = total_brute + total_burn /// Checks - Not daylight & Has more than 10 Brute/Burn & not already in Torpor - if(!SSsunlight.sunlight_active && total_damage >= 10 && !HAS_TRAIT_FROM(owner.current, TRAIT_NODEATH, TORPOR_TRAIT)) + if(!SSsunlight.sunlight_active && total_damage >= 10 && !is_in_torpor()) torpor_begin() /datum/antagonist/bloodsucker/proc/check_end_torpor() @@ -142,12 +142,17 @@ if(total_brute <= 10) torpor_end() +/datum/antagonist/bloodsucker/proc/is_in_torpor() + if(QDELETED(owner.current)) + return FALSE + return HAS_TRAIT_FROM(owner.current, TRAIT_NODEATH, TORPOR_TRAIT) + /datum/antagonist/bloodsucker/proc/torpor_begin() to_chat(owner.current, span_notice("You enter the horrible slumber of deathless Torpor. You will heal until you are renewed.")) // Force them to go to sleep REMOVE_TRAIT(owner.current, TRAIT_SLEEPIMMUNE, BLOODSUCKER_TRAIT) // Without this, you'll just keep dying while you recover. - owner.current.add_traits(list(TRAIT_NODEATH, TRAIT_FAKEDEATH, TRAIT_DEATHCOMA, TRAIT_RESISTLOWPRESSURE, TRAIT_RESISTHIGHPRESSURE), TORPOR_TRAIT) + owner.current.add_traits(torpor_traits, TORPOR_TRAIT) owner.current.set_timed_status_effect(0 SECONDS, /datum/status_effect/jitter, only_if_higher = TRUE) // Disable ALL Powers DisableAllPowers() @@ -155,7 +160,7 @@ /datum/antagonist/bloodsucker/proc/torpor_end() owner.current.grab_ghost() to_chat(owner.current, span_warning("You have recovered from Torpor.")) - REMOVE_TRAITS_IN(owner.current, TORPOR_TRAIT) + owner.current.remove_traits(torpor_traits, TORPOR_TRAIT) if(!HAS_TRAIT(owner.current, TRAIT_MASQUERADE)) ADD_TRAIT(owner.current, TRAIT_SLEEPIMMUNE, BLOODSUCKER_TRAIT) heal_vampire_organs() diff --git a/monkestation/code/modules/bloodsuckers/structures/bloodsucker_objects.dm b/monkestation/code/modules/bloodsuckers/structures/bloodsucker_objects.dm index 817d0347f045..1b365e0d2d55 100644 --- a/monkestation/code/modules/bloodsuckers/structures/bloodsucker_objects.dm +++ b/monkestation/code/modules/bloodsuckers/structures/bloodsucker_objects.dm @@ -155,7 +155,7 @@ if(bloodsuckerdatum) // If DEAD or TORPID... Kill Bloodsucker! if(target.StakeCanKillMe()) - bloodsuckerdatum.FinalDeath() + bloodsuckerdatum.final_death() else to_chat(target, span_userdanger("You have been staked! Your powers are useless, your death forever, while it remains in place.")) target.balloon_alert(target, "you have been staked!") diff --git a/monkestation/code/modules/smithing/oozelings/body/organs.dm b/monkestation/code/modules/smithing/oozelings/body/organs.dm index 655253a0ae00..2b0a73d976ec 100644 --- a/monkestation/code/modules/smithing/oozelings/body/organs.dm +++ b/monkestation/code/modules/smithing/oozelings/body/organs.dm @@ -162,12 +162,12 @@ if(target_ling) if(target_ling.oozeling_revives > 0) target_ling.oozeling_revives-- - addtimer(CALLBACK(src, PROC_REF(rebuild_body)), 30 SECONDS) + addtimer(CALLBACK(src, PROC_REF(rebuild_body), null, FALSE), 30 SECONDS) if(IS_BLOODSUCKER(brainmob)) var/datum/antagonist/bloodsucker/target_bloodsucker = brainmob.mind.has_antag_datum(/datum/antagonist/bloodsucker) if(target_bloodsucker.bloodsucker_blood_volume >= target_bloodsucker.max_blood_volume * 0.4) - addtimer(CALLBACK(src, PROC_REF(rebuild_body)), 30 SECONDS) + addtimer(CALLBACK(src, PROC_REF(rebuild_body), null, FALSE), 30 SECONDS) target_bloodsucker.bloodsucker_blood_volume -= target_bloodsucker.max_blood_volume * 0.15 rebuilt = FALSE @@ -220,7 +220,7 @@ item.forceMove(turf) stored_items.Cut() -/obj/item/organ/internal/brain/slime/proc/rebuild_body(mob/user) +/obj/item/organ/internal/brain/slime/proc/rebuild_body(mob/user, nugget = TRUE) if(rebuilt) return rebuilt = TRUE @@ -255,7 +255,9 @@ new_body.updateappearance(mutcolor_update = TRUE) new_body.domutcheck() new_body.forceMove(drop_location()) - new_body.blood_volume = BLOOD_VOLUME_SAFE + 60 + if(!nugget) + new_body.set_nutrition(NUTRITION_LEVEL_FED) + new_body.blood_volume = nugget ? (BLOOD_VOLUME_SAFE + 60) : BLOOD_VOLUME_NORMAL REMOVE_TRAIT(new_body, TRAIT_NO_TRANSFORM, REF(src)) if(!QDELETED(brainmob)) SSquirks.AssignQuirks(new_body, brainmob.client) @@ -263,12 +265,16 @@ qdel(new_body_brain) forceMove(new_body) Insert(new_body) - for(var/obj/item/bodypart as anything in new_body.bodyparts) - if(istype(bodypart, /obj/item/bodypart/chest)) - continue - qdel(bodypart) - new_body.visible_message(span_warning("[new_body]'s torso \"forms\" from [new_body.p_their()] core, yet to form the rest.")) - to_chat(owner, span_purple("Your torso fully forms out of your core, yet to form the rest.")) + if(nugget) + for(var/obj/item/bodypart as anything in new_body.bodyparts) + if(istype(bodypart, /obj/item/bodypart/chest)) + continue + qdel(bodypart) + new_body.visible_message(span_warning("[new_body]'s torso \"forms\" from [new_body.p_their()] core, yet to form the rest.")) + to_chat(owner, span_purple("Your torso fully forms out of your core, yet to form the rest.")) + else + new_body.visible_message(span_warning("[new_body]'s body fully forms from [new_body.p_their()] core!")) + to_chat(owner, span_purple("Your body fully forms from your core!")) brainmob?.mind?.transfer_to(new_body) new_body.grab_ghost()