Miscellaneous Virology Update (#18039)

* Roanoake + heal

* Robutt

* Buff suits

* Macrophages

* More symptoms!!

* Infective component
This commit is contained in:
Guti
2025-07-23 22:55:39 +02:00
committed by GitHub
parent b7a222d5dc
commit 41472477ce
15 changed files with 392 additions and 38 deletions
+80
View File
@@ -0,0 +1,80 @@
/datum/component/infective
dupe_mode = COMPONENT_DUPE_ALLOWED
var/list/datum/disease/diseases
var/expire_time
var/required_clean_types = CLEAN_TYPE_DISEASE
/datum/component/infective/Initialize(list/datum/disease/_diseases, expire_in)
if(islist(_diseases))
diseases = _diseases
else
diseases = list(_diseases)
if(!diseases.len || isnull(diseases[1]))
stack_trace("Infective component initialized without any diseases!")
qdel(src)
if(expire_in)
expire_time = world.time + expire_in
QDEL_IN(src, expire_in)
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE
var/static/list/disease_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(try_infect_crossed),
)
AddComponent(/datum/component/connect_loc_behalf, parent, disease_connections)
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(clean))
RegisterSignal(parent, COMSIG_MOVABLE_BUMP, PROC_REF(try_infect_collide))
RegisterSignal(parent, COMSIG_ATOM_EXTRAPOLATOR_ACT, PROC_REF(extrapolation))
if(isitem(parent))
RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(try_infect_attack))
// RegisterSignal(parent, COMSIG_FOOD_EATEN, PROC_REF(try_infect_eat)) - TODO: Send signal when eating
/datum/component/infective/proc/clean(datum/source, clean_types)
SIGNAL_HANDLER
if(clean_types & required_clean_types)
qdel(src)
return TRUE
/datum/component/infective/proc/try_infect_crossed(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
SIGNAL_HANDLER
if(isliving(arrived))
try_infect(arrived, BP_L_FOOT)
/*
/datum/component/proc/try_infect_eat(datum/source, mob/living/eater, mob/living/feeder)
SIGNAL_HANDLER
for(var/datum/disease/D in diseases)
eater.ForceContractDisease(D)
try_infect(feeder, BP_L_ARM)
*/
/datum/component/infective/proc/try_infect_collide(datum/source, atom/A)
SIGNAL_HANDLER
var/atom/movable/P = parent
if(P.throwing)
// This will be handled by COMSIG_MOVABLE_IMPACT_ZONE, whenever we get that.
return
if(isliving(A))
try_infect(A)
/datum/component/infective/proc/try_infect_attack(datum/source, mob/living/target, mob/living/user)
SIGNAL_HANDLER
if(!iscarbon(target))
try_infect(target)
try_infect(user, BP_L_ARM)
/datum/component/infective/proc/try_infect(mob/living/L, target_zone)
for(var/datum/disease/D in diseases)
L.ContractDisease(D)
/datum/component/infective/proc/extrapolation(datum/source, mob/user, obj/item/extrapolator/extrapolator, dry_run = FALSE, list/result)
SIGNAL_HANDLER
EXTRAPOLATOR_ACT_ADD_DISEASES(result, diseases)
@@ -51,7 +51,9 @@
if(SPECIES_UNATHI, SPECIES_TAJARAN) // Mice devourers
sickrisk = 0.5
if(SPECIES_XENOCHIMERA)
// Ronoake Syndrome will go here :)
var/datum/disease/advance/dormant_roanoake = new /datum/disease/roanoake
dormant_roanoake.virus_modifiers |= DORMANT
ForceContractDisease(dormant_roanoake, TRUE)
return
if(SPECIES_PROMETHEAN) // Too clean
return
@@ -0,0 +1,80 @@
/datum/symptom/dance
name = "Choreomania"
desc = "The symptom allows the virus to hijack the motor system of the affected, making them dance."
stealth = 2
resistance = 0
stage_speed = 3
transmission = 1
level = 5
severity = 0
symptom_delay_min = 15 SECONDS
symptom_delay_max = 30 SECONDS
prefixes = list("Dancing ", "Footloose ", "Frenzied ", "Groovy ")
bodies = list("Tremor", "Jig", "Shuffle", "Spasm")
suffixes = list(" Fit", " Twitch")
/datum/symptom/dance/Activate(datum/disease/advance/A)
if(!..())
return
var/mob/living/M = A.affected_mob
if(M.stat == DEAD)
return
switch(A.stage)
if(2)
if(prob(base_message_chance))
to_chat(M, span_notice("You feel a tingle down your spine..."))
if(3)
if(prob(base_message_chance))
to_chat(M, span_notice("You feel a tingle down your spine..."))
if(prob(5))
M.visible_emote("taps their foot rhythmically.")
if(4, 5)
if(prob(base_message_chance))
to_chat(M, span_notice("You feel like giving it all out there!"))
M.emote("whistle")
/*
if(prob(5))
dance_one(M)
*/
if(prob(5))
penguin_dance(M)
/*
/datum/symptom/dance/proc/dance_one(mob/living/M)
var/list/dance = list(2,4,8,2,4,8,2,4,8,2,4,8,1,4,1,4,1,4,2,4,8,2)
for(var/D in dance)
M.dir = D
animate(M, pixel_x = 5, time = 5)
animate(M, pixel_x = -5, time = 5)
animate(M, pixel_x = M.default_pixel_x, pixel_y = M.default_pixel_y, time = 2)
*/
/datum/symptom/dance/proc/penguin_dance(mob/living/M)
if(!M)
return
animate(M)
M.transform = matrix()
M.pixel_x = 0
M.pixel_y = 0
var/matrix/left = matrix()
left.Translate(-3, 2)
var/matrix/right = matrix()
right.Translate(3, -2)
var/tag = "penguin_dance"
animate(M, dir = EAST, time = 2, tag = tag)
animate(M, dir = NORTH, time = 2, tag = tag)
animate(M, dir = WEST, time = 2, tag = tag)
animate(M, dir = SOUTH, time = 2, tag = tag)
animate(M, dir = WEST, transform = left, time = 4, tag = tag)
animate(transform = right, time = 4)
animate(transform = matrix(), time = 4)
animate(dir = EAST, transform = left, time = 4)
animate(transform = right, time = 4)
animate(transform = matrix(), time = 4)
@@ -225,6 +225,65 @@
if(regenerate_blood && H.vessel.get_reagent_amount(REAGENT_ID_BLOOD) < H.species.blood_volume)
H.add_chemical_effect(CE_BLOODRESTORE, 1)
/datum/symptom/heal/water
name = "Tissue Hydration"
desc = "The virus uses excess water inside and outside the body to repair damaged tissue cells. More effective when using holy water and against burns."
stealth = 0
resistance = -1
stage_speed = 0
transmission = 1
level = 6
passive_message = span_notice("Your skin feels oddly dry...")
threshold_descs = list(
"Resistance 5" = "Water is consumed at a much slower rate.",
"Stage Speed 7" = "Increases healing speed."
)
var/absorption_coeff = 1
prefixes = list("Hydro", "Moist ")
bodies = list("Water")
/datum/symptom/heal/water/Start(datum/disease/advance/A)
if(!..())
return
if(A.stage_rate >= 7)
power = 2
if(A.resistance >= 5)
absorption_coeff = 0.25
/datum/symptom/heal/water/CanHeal(mob/living/M, datum/disease/advance/A, actual_power)
if(!istype(M))
return
var/mob/living/carbon/human/H = M
if(H.fire_stacks < 0)
H.adjust_fire_stacks(min(absorption_coeff, -H.fire_stacks))
. += power
if(H.reagents.has_reagent(REAGENT_ID_HOLYWATER))
H.reagents.remove_reagent(REAGENT_ID_HOLYWATER, 0.5 * absorption_coeff)
. += power * 0.75
else if(H.reagents.has_reagent(REAGENT_ID_WATER))
H.reagents.remove_reagent(REAGENT_ID_WATER, 0.5 * absorption_coeff)
. += power * 0.5
/datum/symptom/heal/water/Heal(mob/living/carbon/human/H, datum/disease/advance/A, actual_power)
var/heal_amt = 2 * actual_power
var/list/zone_list = BP_ALL
if(!zone_list.len)
return
if(prob(5))
to_chat(H, span_notice("You feel yourself absorbing the water around you to soothe your damaged skin."))
for(var/obj/item/organ/external/bodypart in zone_list)
if(bodypart.burn_dam > 0 && !(bodypart.robotic >= ORGAN_ROBOT))
bodypart.heal_damage(0, (heal_amt/zone_list.len))
return TRUE
/*
//////////////////////////////////////
@@ -24,8 +24,8 @@ BONUS
transmission = 2
level = 9
severity = 2
symptom_delay_min = 40
symptom_delay_max = 60
symptom_delay_min = 40 SECONDS
symptom_delay_max = 60 SECONDS
var/gigagerms = FALSE
var/netspeed = 0
@@ -78,14 +78,15 @@ BONUS
if(gigagerms)
phage = new /mob/living/simple_mob/vore/aggressive/macrophage/giant(get_turf((M.loc)))
phage.melee_damage_lower = rand(10, 15)
phage.melee_damage_upper = rand(15, 20)
phage.melee_damage_lower = rand(5, 10)
phage.melee_damage_upper = rand(10, 15)
M.apply_damage(rand(10, 20))
playsound(M, 'sound/effects/splat.ogg', 50, 1)
M.emote("scream")
else
phage = new(get_turf((M.loc)))
M.apply_damage(rand(1, 7))
M.apply_damage(rand(1, 5))
playsound(M, 'sound/effects/splat.ogg', 50, 1)
phage.health += A.resistance
phage.maxHealth += A.resistance
@@ -0,0 +1,140 @@
/datum/symptom/robotic_adaptation
name = "Biometallic Replication"
desc = "The virus can manipulate metal and silicate compounds, becoming able to infect robotic beings."
stealth = 0
resistance = 1
stage_speed = 4
transmission = -1
level = 9
severity = 0
symptom_delay_min = 20 SECONDS
symptom_delay_max = 50 SECONDS
prefixes = list("Robo")
bodies = list("Robot")
suffixes = list("-217")
var/replaceorgans = FALSE
var/replacebody = FALSE
//var/robustbits = FALSE
threshold_descs = list(
"Stage Speed 4" = "The virus will replace the host's organic organs with mundane, biometallic versions.",
"Resistance 4" = "The virus will eventually convert the host's entire body to biometallic materials, and maintain its cellular integrity.",
//"Stage Speed 12" = "Biometallic mass created by the virus will be superior to typical organic mass." TODO: Some fancy organs.
)
/datum/symptom/robotic_adaptation/OnAdd(datum/disease/advance/A)
A.virus_modifiers |= INFECT_SYNTHETICS
/datum/symptom/robotic_adaptation/severityset(datum/disease/advance/A)
. = ..()
if(A.stage_rate >= 4)
severity += 1
/*
if(A.stage_rate >= 12)
severity -= 3
*/
if(A.resistance >= 4)
severity += 1
/datum/symptom/robotic_adaptation/Start(datum/disease/advance/A)
. = ..()
if(A.stage_rate >= 4)
replaceorgans = TRUE
if(A.resistance >= 4)
replacebody = TRUE
/*
if(A.stage_rate >= 12)
robustbits = TRUE
*/
/datum/symptom/robotic_adaptation/Activate(datum/disease/advance/A)
if(!..())
return
var/mob/living/carbon/human/H = A.affected_mob
switch(A.stage)
if(3, 4)
if(replaceorgans && H.stat != DEAD)
to_chat(H, span_warning("[pick("You feel a grinding pain in your abdomen.", "You exhale a jet of steam.")]"))
if(5)
if(replaceorgans || replacebody)
Replace(H)
return
/datum/symptom/robotic_adaptation/proc/Replace(mob/living/carbon/human/H)
if(!H.allow_spontaneous_tf) // Bwomp.
return FALSE
if(replaceorgans)
var/obj/item/organ/internal/O = pick(H.internal_organs)
if(O.robotic >= ORGAN_ROBOT)
return FALSE
switch(O)
if(/obj/item/organ/internal/brain)
var/obj/item/organ/internal/brain/brain = O
brain.robotize()
return TRUE
if(/obj/item/organ/internal/eyes)
var/obj/item/organ/internal/eyes/eyes = O
if(prob(40) && H.stat != DEAD)
to_chat(H, span_userdanger("You feel a stabbing pain in your eyeballs!"))
H.emote("scream")
eyes.robotize()
return TRUE
if(/obj/item/organ/internal/heart)
var/obj/item/organ/internal/heart/heart = O
heart.robotize()
if(prob(40) && H.stat != DEAD)
to_chat(H, span_userdanger("You feel a stabbing pain in your chest!"))
H.emote("scream")
return TRUE
if(/obj/item/organ/internal/stomach)
var/obj/item/organ/internal/stomach/stomach = O
stomach.robotize()
if(prob(40) && H.stat != DEAD)
to_chat(H, span_userdanger("You feel a stabbing pain in your abdomen!"))
H.emote("scream")
return TRUE
if(/obj/item/organ/internal/lungs)
var/obj/item/organ/internal/lungs/lungs = O
lungs.robotize()
return TRUE
if(/obj/item/organ/internal/liver)
var/obj/item/organ/internal/liver/liver = O
liver.robotize()
if(prob(40) && H.stat != DEAD)
to_chat(H, span_userdanger("You feel a stabbing pain in your abdomen!"))
H.emote("scream")
return TRUE
if(/obj/item/organ/internal/intestine)
var/obj/item/organ/internal/intestine/intestine = O
intestine.robotize()
return TRUE
if(/obj/item/organ/internal/spleen)
var/obj/item/organ/internal/spleen/spleen = O
spleen.robotize()
return TRUE
if(/obj/item/organ/internal/kidneys)
var/obj/item/organ/internal/kidneys/kidneys = O
kidneys.robotize()
return TRUE
if(replacebody) // No need to overcomplicate this one
var/obj/item/organ/external/O = pick(H.organs)
if(O.robotic >= ORGAN_ROBOT)
return FALSE
if(O)
O.robotize()
H.visible_message(span_danger("[H]'s \the [O] shifts, and becomes metal before your very eyes."), span_userdanger("Your \the [O] feels numb, and cold."))
return TRUE
return FALSE
/datum/symptom/robotic_adaptation/End(datum/disease/advance/A)
if(!..())
return
var/mob/living/carbon/human/H = A.affected_mob
if(A.stage >= 5 && (replaceorgans || replacebody))
if(H.stat != DEAD)
to_chat(H, span_userdanger("You feel lighter and metallic, as your innards lose their silicon facade."))
/datum/symptom/robotic_adaptation/OnRemove(datum/disease/advance/A)
A.virus_modifiers &= ~INFECT_SYNTHETICS
@@ -97,9 +97,6 @@ GLOBAL_LIST_INIT(list_symptoms, subtypesof(/datum/symptom))
/datum/symptom/proc/OnRemove(datum/disease/advance/A)
return
/datum/symptom/proc/generate_threshold_desc()
return
/datum/symptom/proc/get_symptom_data()
var/list/data = list()
data["name"] = name
+1 -1
View File
@@ -5,7 +5,7 @@
spread_text = "Airborne"
spread_flags = DISEASE_SPREAD_AIRBORNE
cure_text = "Rest & " + REAGENT_SPACEACILLIN
cures = list(REAGENT_ID_SPACEACILLIN, REAGENT_ID_CHICKENSOUP)
cures = list(REAGENT_ID_SPACEACILLIN, REAGENT_ID_CHICKENSOUP, REAGENT_ID_CHICKENNOODLESOUP)
virus_modifiers = NONE //Does NOT have needs_all_cures
agent = "XY-rhinovirus"
viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey)
+1 -1
View File
@@ -4,7 +4,7 @@
max_stages = 3
spread_text = "Airborne"
cure_text = REAGENT_SPACEACILLIN
cures = list(REAGENT_ID_SPACEACILLIN, REAGENT_ID_CHICKENSOUP)
cures = list(REAGENT_ID_SPACEACILLIN, REAGENT_ID_CHICKENSOUP, REAGENT_ID_CHICKENNOODLESOUP)
virus_modifiers = NONE //Does NOT have needs_all_cures
cure_chance = 10
agent = "H13N1 flu virion"
+2 -1
View File
@@ -7,7 +7,8 @@
spread_flags = DISEASE_SPREAD_NON_CONTAGIOUS
cure_text = "Sleep"
agent = REAGENT_SALMONELLA
cures = list(REAGENT_ID_CHICKENSOUP)
cures = list(REAGENT_ID_CHICKENSOUP, REAGENT_ID_CHICKENNOODLESOUP)
virus_modifiers = NONE // Does NOT need all the cures
cure_chance = 10
viable_mobtypes = list(/mob/living/carbon/human)
desc = "Nausea, sickness, and vomiting."
+6 -5
View File
@@ -1,9 +1,10 @@
/datum/disease/roanoake
name = "Roanoake Syndrome"
medical_name = "Roanoake Syndrome"
max_stages = 6
stage_prob = 2
spread_text = "Blood and close contact"
spread_flags = BLOOD
spread_flags = DISEASE_SPREAD_BLOOD
cure_text = REAGENT_SPACEACILLIN
agent = "Chimera cells"
cures = list(REAGENT_ID_SPACEACILLIN)
@@ -11,8 +12,8 @@
viable_mobtypes = list(/mob/living/carbon/human)
desc = "If left untreated, subject will become a xenochimera upon perishing."
danger = DISEASE_BIOHAZARD
disease_flags = CURABLE
//allow_dead = TRUE //Unused
disease_flags = CURABLE | CAN_CARRY | CAN_NOT_POPULATE
virus_modifiers = BYPASSES_IMMUNITY | SPREAD_DEAD
var/list/obj/item/organ/organ_list = list()
var/obj/item/organ/O
@@ -82,8 +83,8 @@
var/datum/wound/W = new /datum/wound/internal_bleeding(5)
E.wounds += W
if(M.stat == DEAD)
M.species = /datum/species/xenochimera
if(M.stat == DEAD || M.allow_spontaneous_tf)
M.LoadComponent(/datum/component/xenochimera)
cure()
return
+1 -1
View File
@@ -411,7 +411,7 @@
if(D.visibility_flags & HIDDEN_SCANNER)
continue
else
dat += span_red("Viral pathogen detected in blood stream.") + "<BR>"
dat += span_red("Disease detected in blood stream.") + "<BR>"
var/damage_string = null
damage_string = "\t-Brute Damage %: [occupant.getBruteLoss()]"
-6
View File
@@ -39,7 +39,6 @@
/obj/item/clothing/suit/bio_suit/general
icon_state = "bio_general"
item_state_slots = list(slot_r_hand_str = "bio", slot_l_hand_str = "bio")
body_parts_covered = CHEST|LEGS|ARMS
flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER
//Virology biosuit, green stripe
@@ -48,7 +47,6 @@
/obj/item/clothing/suit/bio_suit/virology
icon_state = "bio_virology"
body_parts_covered = CHEST|LEGS|ARMS
flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER
//Security biosuit, grey with red stripe across the chest
@@ -57,7 +55,6 @@
/obj/item/clothing/suit/bio_suit/security
icon_state = "bio_security"
body_parts_covered = CHEST|LEGS|ARMS
flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER
//Janitor's biosuit, grey with purple arms
@@ -66,7 +63,6 @@
/obj/item/clothing/suit/bio_suit/janitor
icon_state = "bio_janitor"
body_parts_covered = CHEST|LEGS|ARMS
flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER
//Scientist's biosuit, white with a pink-ish hue
@@ -75,13 +71,11 @@
/obj/item/clothing/suit/bio_suit/scientist
icon_state = "bio_scientist"
body_parts_covered = CHEST|LEGS|ARMS
flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER
//CMO's biosuit, blue stripe
/obj/item/clothing/suit/bio_suit/cmo
icon_state = "bio_cmo"
body_parts_covered = CHEST|LEGS|ARMS
flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER
/obj/item/clothing/head/bio_hood/cmo
@@ -43,6 +43,7 @@
vore_active = TRUE
vore_capacity = 1
vore_pounce_chance = 45
can_be_drop_prey = FALSE
allow_mind_transfer = TRUE
@@ -90,20 +91,11 @@
else
death()
/mob/living/simple_mob/vore/aggressive/macrophage/green
icon_state = "macrophage-2"
/mob/living/simple_mob/vore/aggressive/macrophage/pink
icon_state = "macrophage-3"
/mob/living/simple_mob/vore/aggressive/macrophage/blue
icon_state = "macrophage-4"
/mob/living/simple_mob/vore/aggressive/macrophage/apply_melee_effects(atom/A)
if(ishuman(A) && prob(25))
var/mob/living/carbon/human/H = A
H.ContractDisease(base_disease)
/*
/mob/living/simple_mob/vore/aggressive/macrophage/do_special_attack(var/atom/A)
. = TRUE
set_AI_busy(TRUE)
@@ -132,17 +124,20 @@
var/mob/living/carbon/human/H = target
H.ContractDisease(base_disease)
set_AI_busy(FALSE)
*/
/mob/living/simple_mob/vore/aggressive/macrophage/death()
..()
visible_message(span_warning("\The [src] shrivels up and dies, unable to survive!"))
if(isbelly(loc))
var/obj/belly/belly = loc
if(belly)
var/mob/living/pred = belly.owner
pred.ForceContractDisease(base_disease)
else
var/obj/effect/decal/cleanable/mucus/sick = new(loc)
visible_message(span_warning("\The [src] shrivels up and dies, unable to survive!"))
var/obj/effect/decal/cleanable/blood/sick = new(loc)
sick.name = "plasma"
sick.basecolor = "#47cbcf"
sick.update_icon()
sick.pixel_x = rand(-24, 24)
sick.pixel_y = rand(-24, 24)
sick.viruses += base_disease
+4
View File
@@ -518,6 +518,7 @@
#include "code\datums\components\connect_loc_behalf.dm"
#include "code\datums\components\connect_mob_behalf.dm"
#include "code\datums\components\holographic_nature.dm"
#include "code\datums\components\infective.dm"
#include "code\datums\components\material_container.dm"
#include "code\datums\components\orbiter.dm"
#include "code\datums\components\overlay_lighting.dm"
@@ -617,6 +618,7 @@
#include "code\datums\diseases\gbs.dm"
#include "code\datums\diseases\lycancoughy.dm"
#include "code\datums\diseases\magnitis.dm"
#include "code\datums\diseases\roanoake.dm"
#include "code\datums\diseases\advance\advance.dm"
#include "code\datums\diseases\advance\disease_preset.dm"
#include "code\datums\diseases\advance\symptoms\blobspores.dm"
@@ -624,6 +626,7 @@
#include "code\datums\diseases\advance\symptoms\cockroach.dm"
#include "code\datums\diseases\advance\symptoms\confusion.dm"
#include "code\datums\diseases\advance\symptoms\cough.dm"
#include "code\datums\diseases\advance\symptoms\dance.dm"
#include "code\datums\diseases\advance\symptoms\deafness.dm"
#include "code\datums\diseases\advance\symptoms\fever.dm"
#include "code\datums\diseases\advance\symptoms\fire.dm"
@@ -640,6 +643,7 @@
#include "code\datums\diseases\advance\symptoms\mlem.dm"
#include "code\datums\diseases\advance\symptoms\necrotic_agent.dm"
#include "code\datums\diseases\advance\symptoms\radiation.dm"
#include "code\datums\diseases\advance\symptoms\robot.dm"
#include "code\datums\diseases\advance\symptoms\shivering.dm"
#include "code\datums\diseases\advance\symptoms\size.dm"
#include "code\datums\diseases\advance\symptoms\sneeze.dm"