Skip to content

Commit

Permalink
Странный Реагент.
Browse files Browse the repository at this point in the history
  • Loading branch information
SmiLeYre committed Nov 12, 2024
1 parent e849bee commit 6a4d121
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 66 deletions.
7 changes: 5 additions & 2 deletions code/__DEFINES/antagonists.dm
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,12 @@
///Heretics --
GLOBAL_LIST_EMPTY(living_heart_cache) //A list of all living hearts in existance, for us to iterate through.

#define IS_INTEQ(mob) (mob.mind?.has_antag_datum(/datum/antagonist/traitor) || mob.mind?.has_antag_datum(/datum/antagonist/raiders) || mob.mind?.has_antag_datum(/datum/antagonist/nukeop) || (ROLE_INTEQ in mob.faction))
#define IS_INTEQ(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/traitor) || mob.mind?.has_antag_datum(/datum/antagonist/raiders) || mob.mind?.has_antag_datum(/datum/antagonist/nukeop) || (ROLE_INTEQ in mob.faction))

#define IS_HERETIC(mob) (mob.mind?.has_antag_datum(/datum/antagonist/heretic))
/// Checks if the given mob is a changeling
#define IS_CHANGELING(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/changeling))

#define IS_HERETIC(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/heretic))
#define IS_HERETIC_MONSTER(mob) (mob.mind?.has_antag_datum(/datum/antagonist/heretic_monster))

/// Checks if the given mob is a malf ai.
Expand Down
23 changes: 23 additions & 0 deletions code/modules/mob/living/damage_procs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,18 @@
updatehealth()
return amount

/mob/living/proc/setBruteLoss(amount, updating_health = TRUE, forced = FALSE, required_bodytype = ALL)
if(!forced && (status_flags & GODMODE))
return FALSE
. = bruteloss
bruteloss = amount

if(!.) // no change, no need to update
return FALSE
if(updating_health)
updatehealth()
. -= bruteloss

/mob/living/proc/getOxyLoss()
return oxyloss

Expand Down Expand Up @@ -226,6 +238,17 @@
updatehealth()
return amount

/mob/living/proc/setFireLoss(amount, updating_health = TRUE, forced = FALSE, required_bodytype = ALL)
if(!forced && (status_flags & GODMODE))
return 0
. = fireloss
fireloss = amount
. -= fireloss
if(. == 0) // no change, no need to update
return 0
if(updating_health)
updatehealth()

/mob/living/proc/getCloneLoss()
return cloneloss

Expand Down
30 changes: 28 additions & 2 deletions code/modules/mob/living/living.dm
Original file line number Diff line number Diff line change
Expand Up @@ -643,8 +643,12 @@
clear_fullscreen("brute")

//Proc used to resuscitate a mob, for full_heal see fully_heal()
/mob/living/proc/revive(full_heal = FALSE, admin_revive = FALSE)
/mob/living/proc/revive(full_heal = FALSE, admin_revive = FALSE, excess_healing = 0)

Check failure on line 646 in code/modules/mob/living/living.dm

View workflow job for this annotation

GitHub Actions / Run Linters

7 overrides of /mob/living/proc/revive are missing keyword args
SEND_SIGNAL(src, COMSIG_LIVING_REVIVE, full_heal, admin_revive)
if(excess_healing)
adjustOxyLoss(-excess_healing, updating_health = FALSE)
adjustToxLoss(-excess_healing, updating_health = FALSE, forced = TRUE) //slime friendly
updatehealth()
if(full_heal)
fully_heal(admin_revive)
if(stat == DEAD && can_be_revived()) //in some cases you can't revive (e.g. no brain)
Expand All @@ -659,7 +663,10 @@
update_sight()
clear_alert("not_enough_oxy")
reload_fullscreen()
. = 1
. = TRUE
if(excess_healing)
INVOKE_ASYNC(src, PROC_REF(emote), "gasp")
log_combat(src, src, "revived")
if(mind)
for(var/S in mind.spell_list)
var/obj/effect/proc_holder/spell/spell = S
Expand Down Expand Up @@ -1429,3 +1436,22 @@
STAMINA:<font size='1'><a href='?_src_=vars;[HrefToken()];mobToDamage=[refid];adjustDamage=stamina' id='stamina'>[getStaminaLoss()]</a>
</font>
"}

/**
* Called by strange_reagent, with the amount of healing the strange reagent is doing
* It uses the healing amount on brute/fire damage, and then uses the excess healing for revive
*/
/mob/living/proc/do_strange_reagent_revival(healing_amount)
var/brute_loss = getBruteLoss()
if(brute_loss)
var/brute_healing = min(healing_amount * 0.5, brute_loss) // 50% of the healing goes to brute
setBruteLoss(round(brute_loss - brute_healing, DAMAGE_PRECISION), updating_health=FALSE, forced=TRUE)
healing_amount = max(0, healing_amount - brute_healing)

var/fire_loss = getFireLoss()
if(fire_loss && healing_amount)
var/fire_healing = min(healing_amount, fire_loss) // rest of the healing goes to fire
setFireLoss(round(fire_loss - fire_healing, DAMAGE_PRECISION), updating_health=TRUE, forced=TRUE)
healing_amount = max(0, healing_amount - fire_healing)

revive(FALSE, FALSE, excess_healing=max(healing_amount, 0)) // and any excess healing is passed along
183 changes: 121 additions & 62 deletions code/modules/reagents/chemistry/reagents/medicine_reagents.dm
Original file line number Diff line number Diff line change
Expand Up @@ -995,74 +995,133 @@
description = "A miracle drug capable of bringing the dead back to life. Only functions when applied by patch or spray, if the target has less than 100 brute and burn damage (independent of one another) and hasn't been husked. Causes slight damage to the living."
reagent_state = LIQUID
color = "#A0E85E"
metabolization_rate = 0.5 * REAGENTS_METABOLISM
metabolization_rate = 1.25 * REAGENTS_METABOLISM
// chemical_flags = REAGENT_ALL_PROCESS (BLUEMOON REMOVAL - роботы не должны получать эффекты реагента)
taste_description = "magnets"
pH = 0
pH = 0.5
value = REAGENT_VALUE_RARE
/// The amount of damage a single unit of this will heal
var/healing_per_reagent_unit = 5
/// The ratio of the excess reagent used to contribute to excess healing
var/excess_healing_ratio = 0.8
/// Do we instantly revive
var/instant = FALSE
/// The maximum amount of damage we can revive from, as a ratio of max health
var/max_revive_damage_ratio = 2

/datum/reagent/medicine/strange_reagent/instant
name = "Stranger Reagent"
instant = TRUE
chemical_flags = NONE

/datum/reagent/medicine/strange_reagent/New()
. = ..()
description = replacetext(description, "%MAXHEALTHRATIO%", "[max_revive_damage_ratio * 100]%")
if(instant)
description += " It appears to be pulsing with a warm pink light."

// FEED ME SEYMOUR
/datum/reagent/medicine/strange_reagent/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user)
/datum/reagent/medicine/strange_reagent/on_hydroponics_apply(obj/machinery/hydroponics/mytray, mob/user)
mytray.spawnplant()

/// Calculates the amount of reagent to at a bare minimum make the target not dead
/datum/reagent/medicine/strange_reagent/proc/calculate_amount_needed_to_revive(mob/living/benefactor)
var/their_health = benefactor.getMaxHealth() - (benefactor.getBruteLoss() + benefactor.getFireLoss())
if(their_health > 0)
return 1

return round(-their_health / healing_per_reagent_unit, DAMAGE_PRECISION)

/// Calculates the amount of reagent that will be needed to both revive and full heal the target. Looks at healing_per_reagent_unit and excess_healing_ratio
/datum/reagent/medicine/strange_reagent/proc/calculate_amount_needed_to_full_heal(mob/living/benefactor)
var/their_health = benefactor.getBruteLoss() + benefactor.getFireLoss()
var/max_health = benefactor.getMaxHealth()
if(their_health >= max_health)
return 1

var/amount_needed_to_revive = calculate_amount_needed_to_revive(benefactor)
var/expected_amount_to_full_heal = round(max_health / healing_per_reagent_unit, DAMAGE_PRECISION) / excess_healing_ratio
return amount_needed_to_revive + expected_amount_to_full_heal

/datum/reagent/medicine/strange_reagent/reaction_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume)
if(exposed_mob.stat != DEAD || !(exposed_mob.mob_biotypes & MOB_ORGANIC))
return ..()

if(exposed_mob.suiciding) //they are never coming back
exposed_mob.visible_message(span_warning("Тело [exposed_mob] не реагирует..."))
return

if(iscarbon(exposed_mob) && !(methods & INGEST)) //simplemobs can still be splashed
return ..()

if(HAS_TRAIT(exposed_mob, TRAIT_HUSK))
exposed_mob.visible_message(span_warning("Тело [exposed_mob] выпустило кучу дыма..."))
var/datum/effect_system/smoke_spread/bad/smoke = new
smoke.set_up(8, exposed_mob.loc)
smoke.start()
return

if((exposed_mob.getBruteLoss() + exposed_mob.getFireLoss()) > (exposed_mob.getMaxHealth() * max_revive_damage_ratio))
if(IS_CHANGELING(exposed_mob) || iszombie(exposed_mob))
return
var/num = rand(0, 1)
switch(num)
if(0)
addtimer(CALLBACK(exposed_mob, TYPE_PROC_REF(/mob/living, do_jitter_animation), 1 SECONDS), 4 SECONDS)
addtimer(CALLBACK(exposed_mob, TYPE_PROC_REF(/mob/living, gib), FALSE, TRUE, TRUE), 2 SECONDS)
if(1)
addtimer(CALLBACK(exposed_mob, TYPE_PROC_REF(/mob/living, do_jitter_animation), 1 SECONDS), 4 SECONDS)
addtimer(CALLBACK(exposed_mob, TYPE_PROC_REF(/mob/living/carbon/human, cluwneify)), 2 SECONDS)
return

var/needed_to_revive = calculate_amount_needed_to_revive(exposed_mob)
if(reac_volume < needed_to_revive)
exposed_mob.visible_message(span_warning("Тело [exposed_mob] немного конвульсирует, а затем снова замирает.."))
exposed_mob.do_jitter_animation(10)
return

exposed_mob.visible_message(span_warning("Тело [exposed_mob] начинает дёргаться!"))
exposed_mob.notify_ghost_cloning(source = exposed_mob)
exposed_mob.do_jitter_animation(10)

// we factor in healing needed when determing if we do anything
var/healing = needed_to_revive * healing_per_reagent_unit
// but excessive healing is penalized, to reward doctors who use the perfect amount
reac_volume -= needed_to_revive
healing += (reac_volume * healing_per_reagent_unit) * excess_healing_ratio

// during unit tests, we want it to happen immediately
if(instant)
exposed_mob.do_strange_reagent_revival(healing)
else
// jitter immediately, after four seconds, and after eight seconds
addtimer(CALLBACK(exposed_mob, TYPE_PROC_REF(/mob/living, do_jitter_animation), 1 SECONDS), 4 SECONDS)
addtimer(CALLBACK(exposed_mob, TYPE_PROC_REF(/mob/living, do_strange_reagent_revival), healing), 7 SECONDS)
addtimer(CALLBACK(exposed_mob, TYPE_PROC_REF(/mob/living, do_jitter_animation), 1 SECONDS), 8 SECONDS)

var/tplus = world.time - exposed_mob.timeofdeath
if(exposed_mob.revive())
exposed_mob.grab_ghost()
var/list/policies = CONFIG_GET(keyed_list/policy)
var/timelimit = CONFIG_GET(number/defib_cmd_time_limit) * 10 //the config is in seconds, not deciseconds
var/late = timelimit && (tplus > timelimit)
var/policy = policies[POLICYCONFIG_ON_DEFIB_LATE] //Always causes memory loss due to the nature of strange reagent.
if(policy)
to_chat(exposed_mob, policy)
exposed_mob.log_message("revived using strange reagent, [tplus] deciseconds from time of death, considered late revival due to usage of strange reagent.", LOG_GAME)
message_admins("[ADMIN_LOOKUPFLW(exposed_mob)] возвращён к жизни и [late? "всё помнит" : "ничего не помнит"].")
log_admin(exposed_mob, "[exposed_mob] возвращён к жизни и [late? "всё помнит" : "ничего не помнит"].")

return ..()

/datum/reagent/medicine/strange_reagent/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
. = ..()
if(chems.has_reagent(type, 1))
mytray.spawnplant()

/datum/reagent/medicine/strange_reagent/reaction_mob(mob/living/M, method=TOUCH, reac_volume)
if(M.stat == DEAD)
if(M.suiciding || M.hellbound) //they are never coming back
M.visible_message("<span class='warning'>[M]'s body does not react...</span>")
return ..()
if(M.getBruteLoss() >= 100 || M.getFireLoss() >= 100 || HAS_TRAIT(M, TRAIT_HUSK)) //body is too damaged to be revived
M.visible_message("<span class='warning'>[M]'s body convulses a bit, and then falls still once more.</span>")
M.do_jitter_animation(10)
return ..()
else
M.visible_message("<span class='warning'>[M]'s body starts convulsing!</span>")
M.notify_ghost_cloning(source = M)
M.do_jitter_animation(10)
addtimer(CALLBACK(M, TYPE_PROC_REF(/mob/living/carbon, do_jitter_animation), 10), 40) //jitter immediately, then again after 4 and 8 seconds
addtimer(CALLBACK(M, TYPE_PROC_REF(/mob/living/carbon, do_jitter_animation), 10), 80)

spawn(100) //so the ghost has time to re-enter
if(iscarbon(M))
var/mob/living/carbon/C = M
if(!(C.dna && C.dna.species && (NOBLOOD in C.dna.species.species_traits)))
C.blood_volume = max(C.blood_volume, BLOOD_VOLUME_BAD*C.blood_ratio) //so you don't instantly re-die from a lack of blood. You'll still need help if you had none though.
var/obj/item/organ/heart/H = C.getorganslot(ORGAN_SLOT_HEART)
if(H && H.organ_flags & ORGAN_FAILING)
H.applyOrganDamage(-15)
for(var/obj/item/organ/O as anything in C.internal_organs)
if(O.organ_flags & ORGAN_FAILING)
O.applyOrganDamage(-5)

M.adjustOxyLoss(-20, 0)
M.adjustToxLoss(-20, 0)
M.updatehealth()
if(iscarbon(M))
var/mob/living/carbon/C = M
if(!C.can_revive(ignore_timelimit = TRUE, maximum_brute_dam = 100, maximum_fire_dam = 100, ignore_heart = TRUE))
return
var/tplus = world.time - M.timeofdeath
if(M.revive())
M.grab_ghost()
M.emote("gasp")
log_combat(M, M, "revived", src)
var/list/policies = CONFIG_GET(keyed_list/policy)
var/timelimit = CONFIG_GET(number/defib_cmd_time_limit) * 10 //the config is in seconds, not deciseconds
var/late = timelimit && (tplus > timelimit)
var/policy = policies[POLICYCONFIG_ON_DEFIB_LATE] //Always causes memory loss due to the nature of strange reagent.
if(policy)
to_chat(M, policy)
M.log_message("revived using strange reagent, [tplus] deciseconds from time of death, considered late revival due to usage of strange reagent.", LOG_GAME)
message_admins("[ADMIN_LOOKUPFLW(M)] возвращён к жизни и [late? "всё помнит" : "ничего не помнит"].")
log_admin(M, "[M] возвращён к жизни и [late? "всё помнит" : "ничего не помнит"].")
..()


/datum/reagent/medicine/strange_reagent/on_mob_life(mob/living/carbon/M)
M.adjustBruteLoss(0.5*REM, 0)
M.adjustFireLoss(0.5*REM, 0)
..()

var/damage_at_random = rand(0, 250)/100 //0 to 2.5
var/need_mob_update
need_mob_update = affected_mob.adjustBruteLoss(damage_at_random * REM * seconds_per_tick, updating_health = FALSE)
need_mob_update += affected_mob.adjustFireLoss(damage_at_random * REM * seconds_per_tick, updating_health = FALSE)

. = 1

/datum/reagent/medicine/mannitol
Expand Down

0 comments on commit 6a4d121

Please sign in to comment.