Nuke Limb Processing (#22814)

<img width="1549" height="857" alt="image"
src="https://github.com/user-attachments/assets/3830233d-0635-4942-b2aa-024651e6cffa"
/>

Limbs make up roughly half of the "Per additional player" processing
overhead, while Organs make up most of the other half.
Not every organ is an easy candidate for "Event-only processing", but
external limbs are significantly easier to do so.

So this PR makes it so that External Limbs no longer require Processing
unless an event happens that would justify them requiring processing.
Which SIGNIFICANTLY reduces the overall additional cost to the
Processing subsystem per player that joins the server.

Oh and I also made the Appendix and Kidneys not require constant
processing. The Appendix will only process if it is hit by an
Appendicitis event, and the Kidneys will only process if they are either
damaged, or you get poisoned.
This commit is contained in:
VMSolidus
2026-07-12 20:45:24 +00:00
committed by GitHub
parent 48d8a06d1c
commit e117ef3f87
44 changed files with 310 additions and 216 deletions
+1 -1
View File
@@ -111,7 +111,7 @@
pulse_mod *= heart.too_fast_pump_modifier
if(PULSE_THREADY)
pulse_mod *= heart.thready_pump_modifier
blood_volume *= pulse_mod * max(min_efficiency, (1-(heart.damage / heart.max_damage)))
blood_volume *= pulse_mod * max(min_efficiency, (1-(heart.get_damage() / heart.max_damage)))
return min(blood_volume, 100)
+4 -6
View File
@@ -39,20 +39,17 @@
return ..()
/// Sets the internal organ as belonging to the targeted external organ, and matches the target's species/robotness. Also updates all organ lists belonging to the owner.
/obj/item/organ/internal/replaced(var/mob/living/carbon/human/target, var/obj/item/organ/external/affected)
if(!istype(target))
return 0
/obj/item/organ/internal/replaced(mob/living/carbon/human/target, obj/item/organ/external/affected)
. = ..()
// robotic organs emulate behavior of the equivalent flesh organ of the species
if(BP_IS_ROBOTIC(src) || !species)
species = target.species
..()
target.internal_organs |= src
affected.internal_organs |= src
target.internal_organs_by_name[organ_tag] = src
return 1
return TRUE
/obj/item/organ/internal/die()
..()
@@ -121,6 +118,7 @@
min_bruised_damage = FLOOR(0.25 * max_damage, 1)
/obj/item/organ/internal/proc/take_internal_damage(amount, var/silent=0)
START_PROCESSING(SSprocessing, src)
if(BP_IS_ROBOTIC(src))
damage = between(0, src.damage + (amount * 0.8), max_damage)
else
+8 -1
View File
@@ -21,6 +21,13 @@
/// The amount of damage an appendix takes per second while in stage 2 of appendicitis.
var/stage_two_organ_damage_per_second = 0.006
/**
* Override so that the appendix will never process on its own.
* It is instead added to processing via the spontaneous_appendicitis event.
*/
/obj/item/organ/internal/appendix/process_initialize()
return
/obj/item/organ/internal/appendix/update_icon()
..()
if(inflamed)
@@ -30,7 +37,7 @@
/obj/item/organ/internal/appendix/process(seconds_per_tick)
..()
if(!owner || !inflamed || !BP_IS_ROBOTIC(src))
return
return PROCESS_KILL
if(owner.stasis_value > 0)
// Decrease the effective tickrate when in stasis.
+6 -3
View File
@@ -43,9 +43,9 @@
var/dead_toxin_accumulation_per_second = 0.66
/obj/item/organ/internal/kidneys/process(seconds_per_tick)
..()
if(!owner)
return
. = ..()
if(. == PROCESS_KILL || !owner)
return PROCESS_KILL
if(owner.stasis_value > 0) // Decrease the effective tickrate when in stasis.
seconds_per_tick /= owner.stasis_value
@@ -72,3 +72,6 @@
owner.adjustToxLoss(broken_toxin_accumulation_per_second * seconds_per_tick)
if(status & ORGAN_DEAD)
owner.adjustToxLoss(dead_toxin_accumulation_per_second * seconds_per_tick)
if (!owner.getToxLoss() && !damage) // None of the conditions triggered, therefore we have healthy kidneys that we don't care about.
return PROCESS_KILL
+35 -4
View File
@@ -16,7 +16,11 @@
var/death_time
//Organ damage stats.
var/damage = 0 // amount of damage to the organ
/**
* amount of damage to the organ. THIS IS PRIVATE FOR A REASON.
* USE get_damage and set_damage IF YOU NEED TO CHANGE OR RETRIEVE THIS.
*/
VAR_PRIVATE/damage = 0
/// Total amount of EMP damage a mechanical organ has taken. Effectively equal to "number of seconds the organ EMP effect will last".
var/surge_damage = 0.0
/**
@@ -83,8 +87,14 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
blood_DNA[dna.unique_enzymes] = dna.b_type
if(internal)
holder.internal_organs |= src
START_PROCESSING(SSprocessing, src)
process_initialize()
/**
* This proc exists so that organs which do not require "always processing" can override it
* in order to opt-out of processing for performance reasons.
*/
/obj/item/organ/proc/process_initialize()
START_PROCESSING(SSprocessing, src)
/obj/item/organ/Destroy()
STOP_PROCESSING(SSprocessing, src)
@@ -142,6 +152,7 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
return damage >= min_bruised_damage
/obj/item/organ/proc/bruise()
START_PROCESSING(SSprocessing, src)
damage = max(damage, min_bruised_damage)
#define ORGAN_RECOVERY_THRESHOLD (5 MINUTES)
@@ -205,15 +216,22 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
if(damage >= max_damage)
die()
/**
* Handles per-second EMP damage effects.
* returns FALSE if there's no EMP effects left.
* returns "Truthy" if there's still more EMP to handle on the next tick.
* It will wrap back around to FALSE if the surge damage recovery ticked it back down to 0.
*/
/obj/item/organ/proc/tick_surge_damage(seconds_per_tick)
ENFORCE_CALCULUS(seconds_per_tick)
if(!surge_damage)
clear_surge_effects()
return
return FALSE
do_surge_effects()
surge_damage = max(0, surge_damage - (surge_recovery_per_second * seconds_per_tick))
return surge_damage
/obj/item/organ/proc/do_surge_effects()
return
@@ -485,7 +503,7 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
forceMove(owner) //just in case
if(BP_IS_ROBOTIC(src))
set_dna(owner.dna)
return 1
return TRUE
/obj/item/organ/internal/eyes/replaced(var/mob/living/carbon/human/target)
@@ -524,3 +542,16 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
//used by stethoscope
/obj/item/organ/proc/listen()
return
/obj/item/organ/proc/get_damage()
return damage
/obj/item/organ/proc/set_damage(to_set)
damage = to_set
START_PROCESSING(SSprocessing, src)
/obj/item/organ/add_damage(to_add)
START_PROCESSING(SSprocessing, src)
if (..(to_add))
return
damage += to_add
+58 -63
View File
@@ -192,8 +192,6 @@
var/is_emissive = FALSE
/// Whether the limb has an active overlay icon state associated with it
var/is_overlay = FALSE
/// Whether the limb is a tesla limb (required for special handling)
var/is_tesla = FALSE
/obj/item/organ/external/Initialize(mapload)
if(robotize_type)
@@ -226,7 +224,6 @@
limb_flags &= ~ORGAN_HAS_TENDON
/obj/item/organ/external/Destroy()
if(parent?.children)
parent.children -= src
@@ -359,6 +356,8 @@
/obj/item/organ/external/proc/dislocate(var/primary)
if(dislocated == -1)
return
START_PROCESSING(SSprocessing, src)
if(primary)
dislocated = 2
else
@@ -372,6 +371,7 @@
if(dislocated == -1)
return
START_PROCESSING(SSprocessing, src)
dislocated = 0
if(children && children.len)
for(var/obj/item/organ/external/child in children)
@@ -385,11 +385,12 @@
remove_verb(owner, /mob/living/carbon/human/proc/undislocate)
/obj/item/organ/external/update_organ_health()
START_PROCESSING(SSprocessing, src)
damage = min(max_damage, (brute_dam + burn_dam))
/obj/item/organ/external/replaced(var/mob/living/carbon/human/target)
..()
/obj/item/organ/external/replaced(mob/living/carbon/human/target)
. = ..()
START_PROCESSING(SSprocessing, src)
if(istype(owner))
owner.organs_by_name[limb_name] = src
owner.organs |= src
@@ -438,11 +439,12 @@
brute = round(brute * brute_mod, 0.1)
burn = round(burn * burn_mod, 0.1)
if((brute <= 0) && (burn <= 0))
return 0
return FALSE
if(damage_flags & DAMAGE_FLAG_IGNORE_PROSTHETICS && BP_IS_ROBOTIC(src))
return 0
return FALSE
START_PROCESSING(SSprocessing, src)
var/laser = (damage_flags & DAMAGE_FLAG_LASER)
var/sharp = (damage_flags & DAMAGE_FLAG_SHARP)
var/edge = (damage_flags & DAMAGE_FLAG_EDGE)
@@ -548,7 +550,7 @@
var/list/victims = list()
var/organ_hit_chance = 0
for(var/obj/item/organ/internal/I in internal_organs)
if(I.damage < I.max_damage)
if(I.get_damage() < I.max_damage)
victims[I] = I.relative_size
organ_hit_chance += I.relative_size
@@ -569,6 +571,7 @@
var/obj/item/organ/internal/victim = pickweight(victims)
damage_amt -= max(damage_amt*victim.damage_reduction, 0)
victim.take_internal_damage(damage_amt)
START_PROCESSING(SSprocessing, src)
return TRUE
/obj/item/organ/external/proc/handle_limb_gibbing(var/used_weapon, var/brute, var/burn)
@@ -628,7 +631,7 @@
SEND_SIGNAL(src, COMSIG_UPDATE_LIMB_IMAGE)
owner.updatehealth()
START_PROCESSING(SSprocessing, src)
return update_icon()
/*
@@ -658,12 +661,12 @@ This function completely restores a damaged organ to perfect condition.
tendon.rejuvenate()
owner.updatehealth()
START_PROCESSING(SSprocessing, src)
/obj/item/organ/external/proc/createwound(type = INJURY_TYPE_CUT, damage)
if(damage <= 0 || !owner)
return
START_PROCESSING(SSprocessing, src)
//moved this before the open_wound check so that having many small wounds for example doesn't somehow protect you from taking internal damage (because of the return)
//Possibly trigger an internal wound, too.
var/local_damage = brute_dam + burn_dam + damage
@@ -768,70 +771,50 @@ This function completely restores a damaged organ to perfect condition.
//Determines if we even need to process this organ.
/obj/item/organ/external/proc/need_process()
if(BP_IS_ROBOTIC(src) && surge_damage)
return TRUE
if(is_tesla && owner)
return TRUE
if(BP_IS_ROBOTIC(src))
return FALSE
if(get_pain())
return TRUE
if(status & (ORGAN_CUT_AWAY|ORGAN_BLEEDING|ORGAN_BROKEN|ORGAN_DESTROYED|ORGAN_SPLINTED|ORGAN_DEAD|ORGAN_MUTATED))
return TRUE
if(brute_dam || burn_dam)
return surge_damage
if(get_pain() || status & (ORGAN_CUT_AWAY|ORGAN_BLEEDING|ORGAN_BROKEN|ORGAN_DESTROYED|ORGAN_SPLINTED|ORGAN_DEAD|ORGAN_MUTATED|ORGAN_ARTERY_CUT) || brute_dam || burn_dam)
return TRUE
if(last_dam != brute_dam + burn_dam) // Process when we are fully healed up.
last_dam = brute_dam + burn_dam
return TRUE
else
last_dam = brute_dam + burn_dam
if(germ_level)
return TRUE
return FALSE
return germ_level
/obj/item/organ/external/process(seconds_per_tick)
if(owner)
//Specialized handling for tesla limbs. Checks if the limb currently has an associated tesla spine. Else, will disable the emissive and active overlays
if(is_tesla)
var/obj/item/organ/internal/augment/tesla/T = owner.internal_organs_by_name[BP_AUG_TESLA]
if(T && !T.is_broken())
is_emissive = initial(is_emissive)
is_overlay = initial(is_overlay)
else
is_emissive = FALSE
is_overlay = FALSE
return
. = ..()
if (!need_process())
return PROCESS_KILL
// Process wounds, doing healing etc. Only do this every few ticks to save processing power
if(owner.life_tick % wound_update_accuracy == 0)
update_wounds()
if(!owner)
return
//Chem traces slowly vanish
if(owner.life_tick % 10 == 0)
for(var/chemID in trace_chemicals)
trace_chemicals[chemID] = trace_chemicals[chemID] - 1
if(trace_chemicals[chemID] <= 0)
trace_chemicals.Remove(chemID)
// Process wounds, doing healing etc. Only do this every few ticks to save processing power
if(owner.life_tick % wound_update_accuracy == 0)
update_wounds()
if(!(status & ORGAN_BROKEN))
perma_injury = 0
//Chem traces slowly vanish
if(owner.life_tick % 10 == 0)
for(var/chemID in trace_chemicals)
trace_chemicals[chemID] = trace_chemicals[chemID] - 1
if(trace_chemicals[chemID] <= 0)
trace_chemicals.Remove(chemID)
if(status & ORGAN_NYMPH)
var/datum/component/nymph_limb/N = GetComponent(/datum/component/nymph_limb)
N.handle_nymph(src)
if(!(status & ORGAN_BROKEN))
perma_injury = 0
if(surge_damage && (status & ORGAN_ASSISTED))
tick_surge_damage(seconds_per_tick) //Yes, this being here is intentional since this proc does not call ..() unless the owner is null.
if(surge_damage && (status & ORGAN_ASSISTED))
tick_surge_damage(seconds_per_tick) //Yes, this being here is intentional since this proc does not call ..() unless the owner is null.
//Infections
update_germs()
//Infections
update_germs()
//check if an online RIG can splint the broken bone
check_rigsplints()
else
..()
//check if an online RIG can splint the broken bone
check_rigsplints()
/obj/item/organ/external/do_surge_effects()
START_PROCESSING(SSprocessing, src)
if(prob(surge_damage))
owner.custom_pain("The artificial nerves in your [name] scream out in pain!", surge_damage/6)
@@ -898,6 +881,7 @@ Note that amputating the affected organ does in fact remove the infection from t
for(var/datum/wound/W as anything in wounds)
//Open wounds can become infected
if (owner.germ_level > W.germ_level && W.infection_check())
START_PROCESSING(SSprocessing, src)
W.germ_level++
if(!increased_own_germs && antibiotics < 5 && W.germ_level > germ_level)
germ_level++
@@ -928,6 +912,7 @@ Note that amputating the affected organ does in fact remove the infection from t
infect_target_external = null
return ..()
START_PROCESSING(SSprocessing, src)
germ_level++
if(germ_level >= INFECTION_LEVEL_TWO && REAGENT_VOLUME(owner.reagents, /singleton/reagent/thetamycin) < 5) //The presence of 5 units of thetamycin will stop infections spreading
@@ -1293,6 +1278,7 @@ Note that amputating the affected organ does in fact remove the infection from t
if(QDELETED(owner))
return
START_PROCESSING(SSprocessing, src)
if(!silent)
var/message = pick("broke in half", "shattered")
owner.visible_message(\
@@ -1358,9 +1344,13 @@ Note that amputating the affected organ does in fact remove the infection from t
else
is_overlay = FALSE
if(R.is_tesla)
is_tesla = TRUE
else
is_tesla = FALSE
// We don't know if the tesla limb or the tesla spine spawned first.
// So both will attempt to check for each other once during init.
var/obj/item/organ/internal/augment/tesla/T = owner?.internal_organs_by_name[BP_AUG_TESLA]
if (T && !T.is_broken())
is_emissive = TRUE
is_overlay = TRUE
brute_mod = R.brute_mod
burn_mod = R.burn_mod
robotize_type = company
@@ -1389,6 +1379,7 @@ Note that amputating the affected organ does in fact remove the infection from t
if(BP_IS_ROBOTIC(src))
return
src.status |= ORGAN_MUTATED
START_PROCESSING(SSprocessing, src)
if(owner) owner.update_body()
/obj/item/organ/external/proc/unmutate()
@@ -1396,7 +1387,7 @@ Note that amputating the affected organ does in fact remove the infection from t
src.status &= ~ORGAN_MUTATED
if(owner) owner.update_body()
/obj/item/organ/external/proc/get_damage() //returns total damage
/obj/item/organ/external/get_damage() //returns total damage
return max(brute_dam + burn_dam - perma_injury, perma_injury) //could use max_damage?
/obj/item/organ/external/proc/has_infected_wound()
@@ -1461,6 +1452,7 @@ Note that amputating the affected organ does in fact remove the infection from t
if(!supplied_wound || (W in supplied_wound.embedded_objects)) // Just in case.
return
START_PROCESSING(SSprocessing, src)
LAZYADD(supplied_wound.embedded_objects, W)
implants += W
owner.embedded_flag = 1
@@ -1474,7 +1466,7 @@ Note that amputating the affected organ does in fact remove the infection from t
#undef EMBED_BASE_DAMAGE
/obj/item/organ/external/removed(var/mob/living/user, var/ignore_children = 0)
START_PROCESSING(SSprocessing, src)
if(!owner)
return
var/is_robotic = BP_IS_ROBOTIC(src)
@@ -1544,6 +1536,7 @@ Note that amputating the affected organ does in fact remove the infection from t
owner.visible_message(SPAN_WARNING("\The [owner]'s [name] melts away, turning into a mangled mess!"), \
SPAN_DANGER("Your [name] melts away!"), \
SPAN_WARNING("You hear a sickening sizzle."))
START_PROCESSING(SSprocessing, src)
disfigured = 1
/obj/item/organ/external/proc/get_wounds_desc()
@@ -1648,6 +1641,7 @@ Note that amputating the affected organ does in fact remove the infection from t
return FALSE
else
status |= ORGAN_ARTERY_CUT
START_PROCESSING(SSprocessing, src)
return TRUE
/obj/item/organ/external/proc/tendon_status()
@@ -1737,6 +1731,7 @@ Note that amputating the affected organ does in fact remove the infection from t
if(amount > 5 && owner)
owner.undo_srom_pull()
SEND_SIGNAL(src, COMSIG_UPDATE_LIMB_IMAGE)
START_PROCESSING(SSprocessing, src)
return pain-last_pain
/mob/living/carbon/human/proc/undo_srom_pull()
+1 -1
View File
@@ -83,7 +83,7 @@
// Damage to internal organs hurts a lot.
for(var/obj/item/organ/internal/I in internal_organs)
if(prob(1) && !((I.status & ORGAN_DEAD) || BP_IS_ROBOTIC(I)) && I.damage > 5 && I.parent_organ)
if(prob(1) && !((I.status & ORGAN_DEAD) || BP_IS_ROBOTIC(I)) && I.get_damage() > 5 && I.parent_organ)
var/obj/item/organ/external/parent = get_organ(I.parent_organ)
if (!parent) continue
var/pain = 10
@@ -35,6 +35,9 @@
/// If true, will make parent limb not count as broken, as long as it's not bruised (40%) and not broken (0%)
var/supports_limb = FALSE
/obj/item/organ/internal/augment/process_initialize()
return
/obj/item/organ/internal/augment/Initialize()
if(robotic == ROBOTIC_MECHANICAL)
robotize()
@@ -18,6 +18,9 @@
"Calmness",
)
/obj/item/organ/internal/augment/emotional_manipulator/process_initialize()
START_PROCESSING(SSprocessing, src)
/obj/item/organ/internal/augment/emotional_manipulator/attack_self(var/mob/user)
. = ..()
@@ -9,12 +9,6 @@
if (owner)
to_chat(owner, SPAN_DANGER("You sense your [name] stops functioning!"))
/obj/item/organ/internal/augment/head_fluff/process()
..()
if (is_broken() && !ORGAN_DEAD)
if (prob(5))
to_chat(owner, SPAN_WARNING("You sense your [name] isn't working right!"))
/obj/item/organ/internal/augment/head_fluff/removed()
if (owner)
to_chat(owner, SPAN_DANGER("You lose your connection with \the [name]!"))
@@ -13,6 +13,9 @@
/// Time until the next ongoing message
var/next_message = 0
/obj/item/organ/internal/augment/bioaug/mind_blanker/process_initialize()
START_PROCESSING(SSprocessing, src)
/obj/item/organ/internal/augment/bioaug/mind_blanker/Initialize()
. = ..()
if(!owner)
@@ -42,6 +42,64 @@
playsound(owner, 'sound/magic/LightningShock.ogg', 75, 1)
tesla_zap(owner, 7, 1500)
/// Toggles all of an owner's tesla augs.
/obj/item/organ/internal/augment/tesla/proc/toggle_tesla_augs(toggle)
if (!owner)
return
if (is_broken()) // Broken tesla spines will ALWAYS toggle the overlays off when this proc is called.
toggle = FALSE
for (var/obj/item/organ/external/organ in owner.organs)
if (!model)
continue
var/datum/robolimb/R = GLOB.all_robolimbs[model]
if (!R || !R.is_tesla)
continue
organ.is_emissive = toggle
organ.is_overlay = toggle
/**
* Tesla spines power all tesla augs, and if they break, the lights go out.
*/
/obj/item/organ/internal/augment/tesla/take_damage(amount, silent)
. = ..()
toggle_tesla_augs(FALSE)
/obj/item/organ/internal/augment/tesla/Initialize()
. = ..()
toggle_tesla_augs(TRUE)
/obj/item/organ/internal/augment/tesla/Destroy()
toggle_tesla_augs(FALSE)
return ..()
/obj/item/organ/internal/augment/tesla/take_surge_damage(surge)
. = ..()
toggle_tesla_augs(FALSE)
/obj/item/organ/internal/augment/tesla/clear_surge_effects()
. = ..()
toggle_tesla_augs(TRUE)
/obj/item/organ/internal/augment/tesla/rejuvenate()
. = ..()
toggle_tesla_augs(TRUE)
/obj/item/organ/internal/augment/tesla/heal_damage()
. = ..()
toggle_tesla_augs(TRUE)
/obj/item/organ/internal/augment/tesla/removed(mob/living/carbon/human/target, mob/living/user, drop_organ = TRUE, detach = TRUE)
toggle_tesla_augs(FALSE) // Toggle the owner's augs before letting the base organ proc trigger
return ..()
/obj/item/organ/internal/augment/tesla/replaced()
. = ..()
toggle_tesla_augs(TRUE)
/obj/item/organ/internal/augment/tesla/advanced
name = "advanced tesla spine"
max_charges = 15
+53 -67
View File
@@ -4,14 +4,7 @@
/obj/item/organ/external/leg/nymph/Initialize()
. = ..()
AddComponent(/datum/component/nymph_limb)
var/datum/component/nymph_limb/N = GetComponent(/datum/component/nymph_limb)
N.setup_limb(src)
/obj/item/organ/external/leg/nymph/process()
..()
var/datum/component/nymph_limb/N = GetComponent(/datum/component/nymph_limb)
N.handle_nymph(src)
AddComponent(/datum/component/nymph_limb, src)
// Right Leg
/obj/item/organ/external/leg/right/nymph
@@ -19,14 +12,7 @@
/obj/item/organ/external/leg/right/nymph/Initialize()
. = ..()
AddComponent(/datum/component/nymph_limb)
var/datum/component/nymph_limb/N = GetComponent(/datum/component/nymph_limb)
N.setup_limb(src)
/obj/item/organ/external/leg/right/nymph/process()
..()
var/datum/component/nymph_limb/N = GetComponent(/datum/component/nymph_limb)
N.handle_nymph(src)
AddComponent(/datum/component/nymph_limb, src)
// Left Arm
/obj/item/organ/external/arm/nymph
@@ -34,14 +20,7 @@
/obj/item/organ/external/arm/nymph/Initialize()
. = ..()
AddComponent(/datum/component/nymph_limb)
var/datum/component/nymph_limb/N = GetComponent(/datum/component/nymph_limb)
N.setup_limb(src)
/obj/item/organ/external/arm/nymph/process()
..()
var/datum/component/nymph_limb/N = GetComponent(/datum/component/nymph_limb)
N.handle_nymph(src)
AddComponent(/datum/component/nymph_limb, src)
// Right Arm
/obj/item/organ/external/arm/right/nymph
@@ -49,50 +28,35 @@
/obj/item/organ/external/arm/right/nymph/Initialize()
. = ..()
AddComponent(/datum/component/nymph_limb)
var/datum/component/nymph_limb/N = GetComponent(/datum/component/nymph_limb)
N.setup_limb(src)
/obj/item/organ/external/arm/right/nymph/process()
..()
var/datum/component/nymph_limb/N = GetComponent(/datum/component/nymph_limb)
N.handle_nymph(src)
AddComponent(/datum/component/nymph_limb, src)
// Left Hand
/obj/item/organ/external/hand/nymph
/obj/item/organ/external/hand/nymph/Initialize()
. = ..()
AddComponent(/datum/component/nymph_limb)
var/datum/component/nymph_limb/N = GetComponent(/datum/component/nymph_limb)
N.setup_limb(src)
AddComponent(/datum/component/nymph_limb, src)
// Right Hand
/obj/item/organ/external/hand/right/nymph
/obj/item/organ/external/hand/right/nymph/Initialize()
. = ..()
AddComponent(/datum/component/nymph_limb)
var/datum/component/nymph_limb/N = GetComponent(/datum/component/nymph_limb)
N.setup_limb(src)
AddComponent(/datum/component/nymph_limb, src)
// Left Foot
/obj/item/organ/external/foot/nymph
/obj/item/organ/external/foot/nymph/Initialize()
. = ..()
AddComponent(/datum/component/nymph_limb)
var/datum/component/nymph_limb/N = GetComponent(/datum/component/nymph_limb)
N.setup_limb(src)
AddComponent(/datum/component/nymph_limb, src)
// Right Foot
/obj/item/organ/external/foot/right/nymph
/obj/item/organ/external/foot/right/nymph/Initialize()
. = ..()
AddComponent(/datum/component/nymph_limb)
var/datum/component/nymph_limb/N = GetComponent(/datum/component/nymph_limb)
N.setup_limb(src)
AddComponent(/datum/component/nymph_limb, src)
/datum/component/nymph_limb
var/list/valid_species = list(SPECIES_UNATHI, SPECIES_SKRELL, SPECIES_SKRELL_AXIORI)
@@ -120,7 +84,51 @@
BP_R_FOOT = /obj/item/organ/external/foot/right/nymph
)
/datum/component/nymph_limb/proc/setup_limb(var/obj/item/organ/external/E)
/// Typecasted owner of the component as an external limb.
var/obj/item/organ/external/limb
/datum/component/nymph_limb/Initialize(obj/item/organ/external/E)
. = ..()
if (!istype(E))
qdel(src)
stack_trace("[src] was added to an entity that was not of type obj/item/organ/external. This component REQUIRES an external limb, and will delete itself immediately if one isn't provided to its AddComponent args.")
return
setup_limb(E)
START_PROCESSING(SSprocessing, src)
/datum/component/nymph_limb/process()
. = ..()
if (!limb)
qdel(src)
return PROCESS_KILL
var/mob/living/carbon/alien/diona/limb_nymph = limb.nymph
if(!istype(limb_nymph) || !limb || !is_type_in_list(limb, nymph_limb_types))
qdel(src)
return PROCESS_KILL
if((!limb.owner || limb_nymph.stat == DEAD))
nymph_out(limb, limb_nymph)
return PROCESS_KILL
if(!limb.is_usable())
nymph_out(limb, limb_nymph, forced = TRUE)
return PROCESS_KILL
var/blood_volume = round(REAGENT_VOLUME(limb.owner.vessel, /singleton/reagent/blood))
if(!blood_volume)
nymph_out(limb, limb_nymph, forced = TRUE)
return PROCESS_KILL
if(REAGENT_DATA(limb.owner.vessel, /singleton/reagent/blood))
limb.owner.vessel.remove_reagent(/singleton/reagent/blood, BLOOD_REGEN_RATE / (2 * nymph_limb_types_by_name.len))
if(blood_volume <= 0)
nymph_out(limb, limb_nymph, forced = TRUE)
return PROCESS_KILL
/datum/component/nymph_limb/proc/setup_limb(obj/item/organ/external/E)
limb = E
if(is_type_in_list(E, nymph_limb_types))
E.nymph = new /mob/living/carbon/alien/diona
@@ -133,28 +141,6 @@
E.limb_flags &= ~(ORGAN_CAN_BREAK | ORGAN_CAN_MAIM | ORGAN_HAS_TENDON)
// Called by process()
/datum/component/nymph_limb/proc/handle_nymph(var/obj/item/organ/external/E)
var/mob/living/carbon/alien/diona/limb_nymph = E.nymph
if(!istype(limb_nymph))
return FALSE
if(!E || !is_type_in_list(E, nymph_limb_types))
return FALSE
if((!E.owner || limb_nymph.stat == DEAD))
nymph_out(E, limb_nymph)
return FALSE
if(!E.is_usable())
nymph_out(E, limb_nymph, forced = TRUE)
return FALSE
var/blood_volume = round(REAGENT_VOLUME(E.owner.vessel, /singleton/reagent/blood))
if(blood_volume)
if(REAGENT_DATA(E.owner.vessel, /singleton/reagent/blood))
E.owner.vessel.remove_reagent(/singleton/reagent/blood, BLOOD_REGEN_RATE / (2 * nymph_limb_types_by_name.len))
if(blood_volume <= 0)
nymph_out(E, limb_nymph, forced = TRUE)
// Host detach
/mob/living/carbon/human/proc/detach_nymph_limb()
set category = "Abilities"
@@ -134,7 +134,7 @@
if(BP_IS_ROBOTIC(O))
continue
if(SPT_PROB(stage*stage, seconds_per_tick))
O.damage = min(O.damage+stage, O.max_damage)
O.set_damage(min(O.get_damage() + stage, O.max_damage))
if(stage >= 4) //after 15 minutes
if(SPT_PROB(3, seconds_per_tick))