Skip to content

Commit

Permalink
Fix bloodsucker torpor and oozeling bugs (#2232)
Browse files Browse the repository at this point in the history
  • Loading branch information
Absolucy authored Jun 16, 2024
1 parent fe2f8fb commit b6dddf3
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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

Expand All @@ -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

/*
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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,
))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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()
Expand All @@ -142,20 +142,25 @@
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()

/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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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!")
Expand Down
26 changes: 16 additions & 10 deletions monkestation/code/modules/smithing/oozelings/body/organs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -255,20 +255,26 @@
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)
var/obj/item/organ/internal/brain/new_body_brain = new_body.get_organ_slot(ORGAN_SLOT_BRAIN)
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()
Expand Down

0 comments on commit b6dddf3

Please sign in to comment.