Removes sentient disease (#83453)

## About The Pull Request

Completely removes sentient disease from the game

## Why It's Good For The Game

Sentient disease is a unique antag and seems fun on paper, but really
doesn't work that well.

Sentient disease is a pretty binary antagonist: you either get cured and
watch helplessly as you lose all your hosts, or you infect everyone and
wipe out the entire station. Its everything bad about conversion antags,
but there's not even any fighting.

I also don't think any amount of balancing can fix sentient disease. If
we make it harder to cure, the disease gets an easier station wipe, but
if we make it less lethal, it loses all ability to stop cure generation.
The core gameplay pitches the entire crew against one disease, and it's
merely a timer before either it gets cured or wipes out everyone

This is my latest sentient disease round, where I wiped out the entire
station. I only even greentexted because there was one guy on the escape
shuttle in crit that barely made it because they had the sense to take
spaceilline.


![image](https://github.com/tgstation/tgstation/assets/7501474/260b3b28-96a1-4a19-8532-ecb94a41c68b)

The removal of the virologist lets us balance viruses to be fairer
challenges to the player, but as long as sentient disease exists we'll
always have to balance viruses somewhat in favor of the enjoyment of the
disease blowing your head and making you spontaneously combust.

## Changelog
🆑
del: Removes sentient disease from the game
/🆑

Hopefully, once we get virology truly sorted out, we can readd sentient
disease, but this would require our diseases to have endgoals that
aren't focused around killing every person, being widespread while also
not being instantly curable. A reworked sentient disease would have to
be so different, it's better to leave it out, fix virology and then
consider if we can truly add a new sentient disease and have it be fun
and fair
This commit is contained in:
Time-Green
2024-05-26 11:54:50 -07:00
committed by GitHub
parent 793f7907ce
commit a3d93ff598
10 changed files with 0 additions and 1143 deletions
-2
View File
@@ -33,7 +33,6 @@
#define ROLE_OPERATIVE_MIDROUND "Operative (Midround)"
#define ROLE_PARADOX_CLONE "Paradox Clone"
#define ROLE_REV_HEAD "Head Revolutionary"
#define ROLE_SENTIENT_DISEASE "Sentient Disease"
#define ROLE_SLEEPER_AGENT "Syndicate Sleeper Agent"
#define ROLE_SPACE_DRAGON "Space Dragon"
#define ROLE_SPIDER "Spider"
@@ -150,7 +149,6 @@ GLOBAL_LIST_INIT(special_roles, list(
ROLE_OPERATIVE_MIDROUND = 14,
ROLE_PARADOX_CLONE = 0,
ROLE_REVENANT = 0,
ROLE_SENTIENT_DISEASE = 0,
ROLE_SLEEPER_AGENT = 0,
ROLE_SPACE_DRAGON = 0,
ROLE_SPIDER = 0,
@@ -756,26 +756,6 @@
log_game("[key_name(revenant)] was spawned as a revenant by the midround ruleset.")
return revenant
/// Midround Sentient Disease Ruleset (From Ghosts)
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease
name = "Sentient Disease"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_datum = /datum/antagonist/disease
antag_flag = ROLE_SENTIENT_DISEASE
required_candidates = 1
minimum_players = 25
weight = 4
cost = 8
repeatable = TRUE
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease/generate_ruleset_body(mob/applicant)
var/mob/camera/disease/virus = new /mob/camera/disease(SSmapping.get_station_center())
virus.key = applicant.key
INVOKE_ASYNC(virus, TYPE_PROC_REF(/mob/camera/disease, pick_name))
message_admins("[ADMIN_LOOKUPFLW(virus)] has been made into a sentient disease by the midround ruleset.")
log_game("[key_name(virus)] was spawned as a sentient disease by the midround ruleset.")
return virus
/// Midround Space Pirates Ruleset (From Ghosts)
/datum/dynamic_ruleset/midround/pirates
name = "Space Pirates"
-1
View File
@@ -391,7 +391,6 @@
ROLE_REV,
ROLE_REVENANT,
ROLE_REV_HEAD,
ROLE_SENTIENT_DISEASE,
ROLE_SPIDER,
ROLE_SPY,
ROLE_SYNDICATE,
@@ -1,465 +0,0 @@
/*
Abilities that can be purchased by disease mobs. Most are just passive symptoms that will be
added to their disease, but some are active abilites that affect only the target the overmind
is currently following.
*/
GLOBAL_LIST_INIT(disease_ability_singletons, list(
new /datum/disease_ability/action/cough,
new /datum/disease_ability/action/sneeze,
new /datum/disease_ability/action/infect,
new /datum/disease_ability/symptom/mild/cough,
new /datum/disease_ability/symptom/mild/sneeze,
new /datum/disease_ability/symptom/medium/shedding,
new /datum/disease_ability/symptom/medium/beard,
new /datum/disease_ability/symptom/medium/hallucigen,
new /datum/disease_ability/symptom/medium/choking,
new /datum/disease_ability/symptom/medium/confusion,
new /datum/disease_ability/symptom/medium/vomit,
new /datum/disease_ability/symptom/medium/voice_change,
new /datum/disease_ability/symptom/medium/visionloss,
new /datum/disease_ability/symptom/medium/deafness,
new /datum/disease_ability/symptom/powerful/narcolepsy,
new /datum/disease_ability/symptom/medium/fever,
new /datum/disease_ability/symptom/medium/chills,
new /datum/disease_ability/symptom/medium/headache,
new /datum/disease_ability/symptom/medium/viraladaptation,
new /datum/disease_ability/symptom/medium/viralevolution,
new /datum/disease_ability/symptom/medium/disfiguration,
new /datum/disease_ability/symptom/medium/polyvitiligo,
new /datum/disease_ability/symptom/medium/itching,
new /datum/disease_ability/symptom/medium/heal/weight_loss,
new /datum/disease_ability/symptom/medium/heal/sensory_restoration,
new /datum/disease_ability/symptom/medium/heal/mind_restoration,
new /datum/disease_ability/symptom/powerful/fire,
new /datum/disease_ability/symptom/powerful/flesh_eating,
new /datum/disease_ability/symptom/powerful/genetic_mutation,
new /datum/disease_ability/symptom/powerful/inorganic_adaptation,
new /datum/disease_ability/symptom/powerful/heal/starlight,
new /datum/disease_ability/symptom/powerful/heal/oxygen,
new /datum/disease_ability/symptom/powerful/heal/chem,
new /datum/disease_ability/symptom/powerful/heal/metabolism,
new /datum/disease_ability/symptom/powerful/heal/dark,
new /datum/disease_ability/symptom/powerful/heal/water,
new /datum/disease_ability/symptom/powerful/heal/plasma,
new /datum/disease_ability/symptom/powerful/heal/radiation,
new /datum/disease_ability/symptom/powerful/heal/coma,
new /datum/disease_ability/symptom/powerful/youth
))
/datum/disease_ability
var/name
var/cost = 0
var/required_total_points = 0
var/start_with = FALSE
var/short_desc = ""
var/long_desc = ""
var/stat_block = ""
var/threshold_block = list()
var/category = ""
var/list/symptoms
var/list/actions
/datum/disease_ability/New()
..()
if(symptoms)
var/stealth = 0
var/resistance = 0
var/stage_speed = 0
var/transmittable = 0
for(var/T in symptoms)
var/datum/symptom/S = T
stealth += initial(S.stealth)
resistance += initial(S.resistance)
stage_speed += initial(S.stage_speed)
transmittable += initial(S.transmittable)
threshold_block += initial(S.threshold_descs)
stat_block = "Resistance: [resistance]<br>Stealth: [stealth]<br>Stage Speed: [stage_speed]<br>Transmissibility: [transmittable]<br><br>"
if(symptoms.len == 1) //lazy boy's dream
name = initial(S.name)
if(short_desc == "")
short_desc = initial(S.desc)
if(long_desc == "")
long_desc = initial(S.desc)
/datum/disease_ability/proc/CanBuy(mob/camera/disease/D)
if(world.time < D.next_adaptation_time)
return FALSE
if(!D.unpurchased_abilities[src])
return FALSE
return (D.points >= cost) && (D.total_points >= required_total_points)
/datum/disease_ability/proc/Buy(mob/camera/disease/D, silent = FALSE, trigger_cooldown = TRUE)
if(!silent)
to_chat(D, span_notice("Purchased [name]."))
D.points -= cost
D.unpurchased_abilities -= src
if(trigger_cooldown)
D.adapt_cooldown()
D.purchased_abilities[src] = TRUE
for(var/V in (D.disease_instances+D.disease_template))
var/datum/disease/advance/sentient_disease/SD = V
if(symptoms)
for(var/T in symptoms)
var/datum/symptom/S = new T()
SD.symptoms += S
S.OnAdd(SD)
if(SD.processing)
if(S.Start(SD))
S.next_activation = world.time + rand(S.symptom_delay_min * 10, S.symptom_delay_max * 10)
SD.Refresh()
for(var/T in actions)
var/datum/action/A = new T()
A.Grant(D)
/datum/disease_ability/proc/CanRefund(mob/camera/disease/D)
if(world.time < D.next_adaptation_time)
return FALSE
return D.purchased_abilities[src]
/datum/disease_ability/proc/Refund(mob/camera/disease/D, silent = FALSE, trigger_cooldown = TRUE)
if(!silent)
to_chat(D, span_notice("Refunded [name]."))
D.points += cost
D.unpurchased_abilities[src] = TRUE
if(trigger_cooldown)
D.adapt_cooldown()
D.purchased_abilities -= src
for(var/V in (D.disease_instances+D.disease_template))
var/datum/disease/advance/sentient_disease/SD = V
if(symptoms)
for(var/T in symptoms)
var/datum/symptom/S = locate(T) in SD.symptoms
if(S)
SD.symptoms -= S
S.OnRemove(SD)
if(SD.processing)
S.End(SD)
qdel(S)
SD.Refresh()
for(var/T in actions)
var/datum/action/A = locate(T) in D.actions
qdel(A)
//these sybtypes are for conveniently separating the different categories, they have no unique code.
/datum/disease_ability/action
category = "Active"
/datum/disease_ability/symptom
category = "Symptom"
//active abilities and their associated actions
/datum/disease_ability/action/cough
name = "Voluntary Coughing"
actions = list(/datum/action/cooldown/disease_cough)
cost = 0
required_total_points = 0
start_with = TRUE
short_desc = "Force the host you are following to cough, spreading your infection to those nearby."
long_desc = "Force the host you are following to cough with extra force, spreading your infection to those within two meters of your host even if your transmissibility is low.<br>Cooldown: 10 seconds"
/datum/action/cooldown/disease_cough
name = "Cough"
button_icon = 'icons/mob/actions/actions_minor_antag.dmi'
button_icon_state = "cough"
desc = "Force the host you are following to cough with extra force, spreading your infection to those within two meters of your host even if your transmissibility is low.<br>Cooldown: 10 seconds"
cooldown_time = 100
/datum/action/cooldown/disease_cough/Activate(atom/target)
StartCooldown(10 SECONDS)
trigger_cough()
StartCooldown()
return TRUE
/*
* Cause a cough to happen from the host.
*/
/datum/action/cooldown/disease_cough/proc/trigger_cough()
var/mob/camera/disease/our_disease = owner
var/mob/living/host = our_disease.following_host
if(!host)
return FALSE
if(host.stat != CONSCIOUS)
to_chat(our_disease, span_warning("Your host must be conscious to cough."))
return FALSE
to_chat(our_disease, span_notice("You force [host.real_name] to cough."))
host.emote("cough")
if(host.CanSpreadAirborneDisease()) //don't spread germs if they covered their mouth
var/datum/disease/advance/sentient_disease/disease_datum = our_disease.hosts[host]
disease_datum.spread(2)
return TRUE
/datum/disease_ability/action/sneeze
name = "Voluntary Sneezing"
actions = list(/datum/action/cooldown/disease_sneeze)
cost = 2
required_total_points = 3
short_desc = "Force the host you are following to sneeze, spreading your infection to those in front of them."
long_desc = "Force the host you are following to sneeze with extra force, spreading your infection to any victims in a 4 meter cone in front of your host.<br>Cooldown: 20 seconds"
/datum/action/cooldown/disease_sneeze
name = "Sneeze"
button_icon = 'icons/mob/actions/actions_minor_antag.dmi'
button_icon_state = "sneeze"
desc = "Force the host you are following to sneeze with extra force, spreading your infection to any victims in a 4 meter cone in front of your host even if your transmissibility is low.<br>Cooldown: 20 seconds"
cooldown_time = 200
/datum/action/cooldown/disease_sneeze/Activate(atom/target)
StartCooldown(10 SECONDS)
trigger_sneeze()
StartCooldown()
return TRUE
/*
* Cause a sneeze to happen from the host.
*/
/datum/action/cooldown/disease_sneeze/proc/trigger_sneeze()
var/mob/camera/disease/our_disease = owner
var/mob/living/host = our_disease.following_host
if(!host)
return FALSE
if(host.stat != CONSCIOUS)
to_chat(our_disease, span_warning("Your host must be conscious to sneeze."))
return FALSE
to_chat(our_disease, span_notice("You force [host.real_name] to sneeze."))
host.emote("sneeze")
if(host.CanSpreadAirborneDisease()) //don't spread germs if they covered their mouth
var/datum/disease/advance/sentient_disease/disease_datum = our_disease.hosts[host]
for(var/mob/living/nearby_mob in oview(4, disease_datum.affected_mob))
if(!is_source_facing_target(disease_datum.affected_mob, nearby_mob))
continue
if(!disease_air_spread_walk(get_turf(disease_datum.affected_mob), get_turf(nearby_mob)))
continue
nearby_mob.AirborneContractDisease(disease_datum, TRUE)
return TRUE
/datum/disease_ability/action/infect
name = "Secrete Infection"
actions = list(/datum/action/cooldown/disease_infect)
cost = 2
required_total_points = 3
short_desc = "Cause all objects your host is touching to become infectious for a limited time, spreading your infection to anyone who touches them."
long_desc = "Cause the host you are following to excrete an infective substance from their pores, causing all objects touching their skin to transmit your infection to anyone who touches them for the next 30 seconds. This includes the floor, if they are not wearing shoes, and any items they are holding, if they are not wearing gloves.<br>Cooldown: 40 seconds"
/datum/action/cooldown/disease_infect
name = "Secrete Infection"
button_icon = 'icons/mob/actions/actions_minor_antag.dmi'
button_icon_state = "infect"
desc = "Cause the host you are following to excrete an infective substance from their pores, causing all objects touching their skin to transmit your infection to anyone who touches them for the next 30 seconds.<br>Cooldown: 40 seconds"
cooldown_time = 400
/datum/action/cooldown/disease_infect/Activate(atom/target)
StartCooldown(10 SECONDS)
trigger_infection()
StartCooldown()
return TRUE
/*
* Trigger the infection action.
*/
/datum/action/cooldown/disease_infect/proc/trigger_infection()
var/mob/camera/disease/our_disease = owner
var/mob/living/carbon/human/host = our_disease.following_host
if(!host)
return FALSE
for(var/obj/thing as anything in host.get_equipped_items(include_accessories = TRUE))
thing.AddComponent(/datum/component/infective, our_disease.disease_template, 300)
//no shoes? infect the floor.
if(!host.shoes)
var/turf/host_turf = get_turf(host)
if(host_turf && !isspaceturf(host_turf))
host_turf.AddComponent(/datum/component/infective, our_disease.disease_template, 300)
//no gloves? infect whatever we are holding.
if(!host.gloves)
for(var/obj/held_thing as anything in host.held_items)
if(isnull(held_thing))
continue
held_thing.AddComponent(/datum/component/infective, our_disease.disease_template, 300)
return TRUE
/*******************BASE SYMPTOM TYPES*******************/
// cost is for convenience and can be changed. If you're changing req_tot_points then don't use the subtype...
//healing costs more so you have to techswitch from naughty disease otherwise we'd have friendly disease for easy greentext (no fun!)
/datum/disease_ability/symptom/mild
cost = 2
required_total_points = 4
category = "Symptom (Weak)"
/datum/disease_ability/symptom/medium
cost = 4
required_total_points = 8
category = "Symptom"
/datum/disease_ability/symptom/medium/heal
cost = 5
category = "Symptom (+)"
/datum/disease_ability/symptom/powerful
cost = 4
required_total_points = 16
category = "Symptom (Strong)"
/datum/disease_ability/symptom/powerful/heal
cost = 8
category = "Symptom (Strong+)"
/******MILD******/
/datum/disease_ability/symptom/mild/cough
name = "Involuntary Coughing"
symptoms = list(/datum/symptom/cough)
short_desc = "Cause victims to cough intermittently."
long_desc = "Cause victims to cough intermittently, spreading your infection."
/datum/disease_ability/symptom/mild/sneeze
name = "Involuntary Sneezing"
symptoms = list(/datum/symptom/sneeze)
short_desc = "Cause victims to sneeze intermittently."
long_desc = "Cause victims to sneeze intermittently, spreading your infection and also increasing transmissibility and resistance, at the cost of stealth."
/******MEDIUM******/
/datum/disease_ability/symptom/medium/shedding
symptoms = list(/datum/symptom/shedding)
/datum/disease_ability/symptom/medium/beard
symptoms = list(/datum/symptom/beard)
short_desc = "Cause all victims to grow a luscious beard."
long_desc = "Cause all victims to grow a luscious beard. Ineffective against Santa Claus."
/datum/disease_ability/symptom/medium/hallucigen
symptoms = list(/datum/symptom/hallucigen)
short_desc = "Cause victims to hallucinate."
long_desc = "Cause victims to hallucinate. Decreases stats, especially resistance."
/datum/disease_ability/symptom/medium/choking
symptoms = list(/datum/symptom/choking)
short_desc = "Cause victims to choke."
long_desc = "Cause victims to choke, threatening asphyxiation. Decreases stats, especially transmissibility."
/datum/disease_ability/symptom/medium/confusion
symptoms = list(/datum/symptom/confusion)
short_desc = "Cause victims to become confused."
long_desc = "Cause victims to become confused intermittently."
/datum/disease_ability/symptom/medium/vomit
symptoms = list(/datum/symptom/vomit)
short_desc = "Cause victims to vomit."
long_desc = "Cause victims to vomit. Slightly increases transmissibility. Vomiting also also causes the victims to lose nutrition and removes some toxin damage."
/datum/disease_ability/symptom/medium/voice_change
symptoms = list(/datum/symptom/voice_change)
short_desc = "Change the voice of victims."
long_desc = "Change the voice of victims, causing confusion in communications."
/datum/disease_ability/symptom/medium/visionloss
symptoms = list(/datum/symptom/visionloss)
short_desc = "Damage the eyes of victims, eventually causing blindness."
long_desc = "Damage the eyes of victims, eventually causing blindness. Decreases all stats."
/datum/disease_ability/symptom/medium/deafness
symptoms = list(/datum/symptom/deafness)
/datum/disease_ability/symptom/medium/fever
symptoms = list(/datum/symptom/fever)
/datum/disease_ability/symptom/medium/chills
symptoms = list(/datum/symptom/chills)
/datum/disease_ability/symptom/medium/headache
symptoms = list(/datum/symptom/headache)
/datum/disease_ability/symptom/medium/viraladaptation
symptoms = list(/datum/symptom/viraladaptation)
short_desc = "Cause your infection to become more resistant to detection and eradication."
long_desc = "Cause your infection to mimic the function of normal body cells, becoming much harder to spot and to eradicate, but reducing its speed."
/datum/disease_ability/symptom/medium/viralevolution
symptoms = list(/datum/symptom/viralevolution)
/datum/disease_ability/symptom/medium/polyvitiligo
symptoms = list(/datum/symptom/polyvitiligo)
/datum/disease_ability/symptom/medium/disfiguration
symptoms = list(/datum/symptom/disfiguration)
/datum/disease_ability/symptom/medium/itching
symptoms = list(/datum/symptom/itching)
short_desc = "Cause victims to itch."
long_desc = "Cause victims to itch, increasing all stats except stealth."
/datum/disease_ability/symptom/medium/heal/weight_loss
symptoms = list(/datum/symptom/weight_loss)
short_desc = "Cause victims to lose weight."
long_desc = "Cause victims to lose weight, and make it almost impossible for them to gain nutrition from food. Reduced nutrition allows your infection to spread more easily from hosts, especially by sneezing."
/datum/disease_ability/symptom/medium/heal/sensory_restoration
symptoms = list(/datum/symptom/sensory_restoration)
short_desc = "Regenerate eye and ear damage of victims."
long_desc = "Regenerate eye and ear damage of victims."
/datum/disease_ability/symptom/medium/heal/mind_restoration
symptoms = list(/datum/symptom/mind_restoration)
/******POWERFUL******/
/datum/disease_ability/symptom/powerful/fire
symptoms = list(/datum/symptom/fire)
/datum/disease_ability/symptom/powerful/flesh_eating
symptoms = list(/datum/symptom/flesh_eating)
/datum/disease_ability/symptom/powerful/genetic_mutation
symptoms = list(/datum/symptom/genetic_mutation)
cost = 8
/datum/disease_ability/symptom/powerful/inorganic_adaptation
symptoms = list(/datum/symptom/inorganic_adaptation)
/datum/disease_ability/symptom/powerful/narcolepsy
symptoms = list(/datum/symptom/narcolepsy)
/datum/disease_ability/symptom/powerful/youth
symptoms = list(/datum/symptom/youth)
short_desc = "Cause victims to become eternally young."
long_desc = "Cause victims to become eternally young. Provides boosts to all stats except transmissibility."
/****HEALING SUBTYPE****/
/datum/disease_ability/symptom/powerful/heal/starlight
symptoms = list(/datum/symptom/heal/starlight)
/datum/disease_ability/symptom/powerful/heal/oxygen
symptoms = list(/datum/symptom/oxygen)
/datum/disease_ability/symptom/powerful/heal/chem
symptoms = list(/datum/symptom/heal/chem)
/datum/disease_ability/symptom/powerful/heal/metabolism
symptoms = list(/datum/symptom/heal/metabolism)
short_desc = "Increase the metabolism of victims, causing them to process chemicals and grow hungry faster."
long_desc = "Increase the metabolism of victims, causing them to process chemicals twice as fast and grow hungry more quickly."
/datum/disease_ability/symptom/powerful/heal/dark
symptoms = list(/datum/symptom/heal/darkness)
/datum/disease_ability/symptom/powerful/heal/water
symptoms = list(/datum/symptom/heal/water)
/datum/disease_ability/symptom/powerful/heal/plasma
symptoms = list(/datum/symptom/heal/plasma)
/datum/disease_ability/symptom/powerful/heal/radiation
symptoms = list(/datum/symptom/heal/radiation)
/datum/disease_ability/symptom/powerful/heal/coma
symptoms = list(/datum/symptom/heal/coma)
short_desc = "Cause victims to fall into a healing coma when hurt."
long_desc = "Cause victims to fall into a healing coma when hurt."
@@ -1,103 +0,0 @@
/datum/antagonist/disease
name = "Sentient Disease"
roundend_category = "diseases"
antagpanel_category = ANTAG_GROUP_BIOHAZARDS
show_to_ghosts = TRUE
var/disease_name = ""
/datum/antagonist/disease/on_gain()
owner.set_assigned_role(SSjob.GetJobType(/datum/job/sentient_disease))
owner.special_role = ROLE_SENTIENT_DISEASE
var/datum/objective/O = new /datum/objective/disease_infect()
O.owner = owner
objectives += O
O = new /datum/objective/disease_infect_centcom()
O.owner = owner
objectives += O
. = ..()
/datum/antagonist/disease/greet()
. = ..()
to_chat(owner.current, span_notice("Infect members of the crew to gain adaptation points, and spread your infection further."))
owner.announce_objectives()
/datum/antagonist/disease/apply_innate_effects(mob/living/mob_override)
if(!istype(owner.current, /mob/camera/disease))
var/turf/T = get_turf(owner.current)
T = T ? T : SSmapping.get_station_center()
var/mob/camera/disease/D = new /mob/camera/disease(T)
owner.transfer_to(D)
/datum/antagonist/disease/admin_add(datum/mind/new_owner,mob/admin)
..()
var/mob/camera/disease/D = new_owner.current
D.pick_name()
/datum/antagonist/disease/roundend_report()
var/list/result = list()
result += "<b>Disease name:</b> [disease_name]"
result += printplayer(owner)
var/win = TRUE
var/objectives_text = ""
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] [span_greentext("Success!")]"
else
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] [span_redtext("Fail.")]"
win = FALSE
count++
result += objectives_text
var/special_role_text = LOWER_TEXT(name)
if(win)
result += span_greentext("The [special_role_text] was successful!")
else
result += span_redtext("The [special_role_text] has failed!")
if(istype(owner.current, /mob/camera/disease))
var/mob/camera/disease/D = owner.current
result += "<B>[disease_name] completed the round with [D.hosts.len] infected hosts, and reached a maximum of [D.total_points] concurrent infections.</B>"
result += "<B>[disease_name] completed the round with the following adaptations:</B>"
var/list/adaptations = list()
for(var/V in D.purchased_abilities)
var/datum/disease_ability/A = V
adaptations += A.name
result += adaptations.Join(", ")
return result.Join("<br>")
/datum/antagonist/disease/get_preview_icon()
var/icon/icon = icon('icons/mob/huds/antag_hud.dmi', "virus_infected")
icon.Blend(COLOR_GREEN_GRAY, ICON_MULTIPLY)
icon.Scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
return icon
/datum/objective/disease_infect
explanation_text = "Survive and infect as many people as possible."
/datum/objective/disease_infect/check_completion()
var/mob/camera/disease/D = owner.current
if(istype(D) && D.hosts.len) //theoretically it should not exist if it has no hosts, but better safe than sorry.
return TRUE
return FALSE
/datum/objective/disease_infect_centcom
explanation_text = "Ensure that at least one infected host escapes on the shuttle or an escape pod."
/datum/objective/disease_infect_centcom/check_completion()
var/mob/camera/disease/D = owner.current
if(!istype(D))
return FALSE
for(var/V in D.hosts)
var/mob/living/L = V
if(L.onCentCom() || L.onSyndieBase())
return TRUE
return FALSE
@@ -1,69 +0,0 @@
/datum/disease/advance/sentient_disease
form = "Virus"
name = "Sentient Virus"
desc = "An apparently sentient virus, extremely adaptable and resistant to outside sources of mutation."
viable_mobtypes = list(/mob/living/carbon/human)
mutable = FALSE
bypasses_immunity = TRUE
var/mob/camera/disease/overmind
var/disease_id
/datum/disease/advance/sentient_disease/New()
..()
GLOB.sentient_disease_instances += src
/datum/disease/advance/sentient_disease/Destroy()
. = ..()
overmind = null
GLOB.sentient_disease_instances -= src
/datum/disease/advance/sentient_disease/remove_disease()
if(overmind)
overmind.remove_infection(src)
..()
/datum/disease/advance/sentient_disease/infect(mob/living/infectee, make_copy = TRUE)
if(make_copy && overmind && (overmind.disease_template != src))
overmind.disease_template.infect(infectee, TRUE) //get an updated version of the virus
else
..()
/datum/disease/advance/sentient_disease/IsSame(datum/disease/D)
if(istype(D, /datum/disease/advance/sentient_disease))
var/datum/disease/advance/sentient_disease/V = D
if(V.overmind == overmind)
return TRUE
return FALSE
/datum/disease/advance/sentient_disease/Copy()
var/datum/disease/advance/sentient_disease/D = ..()
D.overmind = overmind
D.disease_id = disease_id
return D
/datum/disease/advance/sentient_disease/after_add()
if(overmind)
overmind.add_infection(src)
/datum/disease/advance/sentient_disease/GenerateProperties()
..()
src.properties["stealth"] += 6 //SD gets an extra bit of stealth, as a treat, to avoid getting caught out so early
/datum/disease/advance/sentient_disease/GetDiseaseID()
if (!disease_id) //if we don't set this here it can reinfect people after the disease dies, since overmind.tag won't be null when the disease is alive, but will be null afterwards, thus the disease ID changes
disease_id = "[type]|[overmind?.tag]"
return disease_id
/datum/disease/advance/sentient_disease/generate_cure()
if(cures.len)
return
var/list/not_used = advance_cures.Copy()
not_used.Cut(1, 6) // Removes the first five tiers of cures.
cures = list(pick(pick_n_take(not_used)), pick(pick_n_take(not_used)))
// Get the cure name from the cure_id
var/datum/reagent/D1 = GLOB.chemical_reagents_list[cures[1]]
var/datum/reagent/D2 = GLOB.chemical_reagents_list[cures[2]]
cure_text = "[D1.name] and [D2.name]"
@@ -1,450 +0,0 @@
#define FREEMOVE_TIME (2 MINUTES)
/*
A mob of type /mob/camera/disease is an overmind coordinating at least one instance of /datum/disease/advance/sentient_disease
that has infected a host. All instances in a host will be synchronized with the stats of the overmind's disease_template. Any
samples outside of a host will retain the stats they had when they left the host, but infecting a new host will cause
the new instance inside the host to be updated to the template's stats.
*/
/mob/camera/disease
name = "Sentient Disease"
real_name = "Sentient Disease"
desc = ""
icon = 'icons/mob/silicon/cameramob.dmi'
icon_state = "marker"
mouse_opacity = MOUSE_OPACITY_ICON
move_on_shuttle = FALSE
invisibility = INVISIBILITY_OBSERVER
see_invisible = SEE_INVISIBLE_LIVING
layer = BELOW_MOB_LAYER
// Pale green, bright enough to have good vision
lighting_cutoff_red = 5
lighting_cutoff_green = 35
lighting_cutoff_blue = 20
sight = SEE_SELF|SEE_THRU
initial_language_holder = /datum/language_holder/universal
var/freemove = TRUE
var/freemove_end = 0
var/freemove_end_timerid
var/datum/action/innate/disease_adapt/adaptation_menu_action
var/datum/disease_ability/examining_ability
var/datum/browser/browser
var/browser_open = FALSE
var/mob/living/following_host
var/list/disease_instances
var/list/hosts //this list is associative, affected_mob -> disease_instance
var/datum/disease/advance/sentient_disease/disease_template
var/total_points = 0
var/points = 0
var/last_move_tick = 0
var/move_delay = 1
var/next_adaptation_time = 0
var/adaptation_cooldown = 600
var/list/purchased_abilities
var/list/unpurchased_abilities
/mob/camera/disease/Initialize(mapload)
.= ..()
ADD_TRAIT(src, TRAIT_SIXTHSENSE, INNATE_TRAIT) //at least they'll have SOMEONE to talk to
disease_instances = list()
hosts = list()
purchased_abilities = list()
unpurchased_abilities = list()
disease_template = new /datum/disease/advance/sentient_disease()
disease_template.overmind = src
qdel(SSdisease.archive_diseases[disease_template.GetDiseaseID()])
SSdisease.archive_diseases[disease_template.GetDiseaseID()] = disease_template //important for stuff that uses disease IDs
var/datum/atom_hud/my_hud = GLOB.huds[DATA_HUD_SENTIENT_DISEASE]
my_hud.show_to(src)
browser = new /datum/browser(src, "disease_menu", "Adaptation Menu", 1000, 770, src)
freemove_end = world.time + FREEMOVE_TIME
freemove_end_timerid = addtimer(CALLBACK(src, PROC_REF(infect_random_patient_zero)), FREEMOVE_TIME, TIMER_STOPPABLE)
/mob/camera/disease/Destroy()
. = ..()
QDEL_NULL(adaptation_menu_action)
disease_template = null
for(var/V in GLOB.sentient_disease_instances)
var/datum/disease/advance/sentient_disease/S = V
if(S.overmind == src)
S.overmind = null
browser = null
/mob/camera/disease/Login()
. = ..()
if(!. || !client)
return FALSE
if(freemove)
to_chat(src, span_warning("You have [DisplayTimeText(freemove_end - world.time)] to select your first host. Click on a human to select your host."))
/mob/camera/disease/get_status_tab_items()
. = ..()
if(freemove)
. += "Host Selection Time: [round((freemove_end - world.time)/10)]s"
else
. += "Adaptation Points: [points]/[total_points]"
. += "Hosts: [disease_instances.len]"
var/adapt_ready = next_adaptation_time - world.time
if(adapt_ready > 0)
. += "Adaptation Ready: [round(adapt_ready/10, 0.1)]s"
/mob/camera/disease/examine(mob/user)
. = ..()
if(isobserver(user))
. += {"[span_notice("[src] has [points]/[total_points] adaptation points.")]
[span_notice("[src] has the following unlocked:")]"}
for(var/datum/disease_ability/ability in purchased_abilities)
. += span_notice("[ability.name]")
/mob/camera/disease/say(
message,
bubble_type,
list/spans = list(),
sanitize = TRUE,
datum/language/language,
ignore_spam = FALSE,
forced,
filterproof = FALSE,
message_range = 7,
datum/saymode/saymode,
list/message_mods = list(),
)
if(!message)
return
if(sanitize)
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
log_talk(message, LOG_SAY)
var/rendered = "<span class='sentientdisease'><b>[src]</b> says, \"[message]\"</span>"
for(var/mob/listener in GLOB.mob_list)
if(issentientdisease(listener))
to_chat(listener, rendered)
else if(isobserver(listener))
var/link = FOLLOW_LINK(listener, src)
to_chat(listener, "[link] [rendered]")
return
/mob/camera/disease/Move(NewLoc, Dir = 0)
if(freemove)
forceMove(NewLoc)
else
if(world.time > (last_move_tick + move_delay))
follow_next(Dir & NORTHWEST)
last_move_tick = world.time
/mob/camera/disease/can_z_move(direction, turf/start, turf/destination, z_move_flags = NONE, mob/living/rider)
if(freemove)
return ..()
return FALSE
/mob/camera/disease/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range)
. = ..()
var/atom/movable/to_follow = speaker
if(radio_freq)
var/atom/movable/virtualspeaker/V = speaker
to_follow = V.source
var/link
if(to_follow in hosts)
link = FOLLOW_LINK(src, to_follow)
else
link = ""
// Create map text prior to modifying message for goonchat
if (client?.prefs.read_preference(/datum/preference/toggle/enable_runechat) && (client.prefs.read_preference(/datum/preference/toggle/enable_runechat_non_mobs) || ismob(speaker)))
create_chat_message(speaker, message_language, raw_message, spans)
// Recompose the message, because it's scrambled by default
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mods)
to_chat(src, "[link] [message]")
/mob/camera/disease/mind_initialize()
. = ..()
if(!mind.has_antag_datum(/datum/antagonist/disease))
mind.add_antag_datum(/datum/antagonist/disease)
var/datum/atom_hud/medsensor = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
medsensor.show_to(src)
/mob/camera/disease/proc/pick_name()
var/static/list/taken_names
if(!taken_names)
taken_names = list("Unknown" = TRUE)
for(var/T in (subtypesof(/datum/disease) - /datum/disease/advance))
var/datum/disease/D = T
taken_names[initial(D.name)] = TRUE
var/set_name
while(!set_name)
var/input = sanitize_name(tgui_input_text(src, "Select a name for your disease", "Select Name", max_length = MAX_NAME_LEN))
if(!input)
set_name = "Sentient Virus"
break
if(taken_names[input])
to_chat(src, span_warning("You cannot use the name of such a well-known disease!"))
else
set_name = input
real_name = "[set_name] (Sentient Disease)"
name = "[set_name] (Sentient Disease)"
disease_template.AssignName(set_name)
var/datum/antagonist/disease/A = mind.has_antag_datum(/datum/antagonist/disease)
if(A)
A.disease_name = set_name
/mob/camera/disease/proc/infect_random_patient_zero(del_on_fail = TRUE)
if(!freemove)
return FALSE
var/list/possible_hosts = list()
var/list/afk_possible_hosts = list()
for(var/i in GLOB.human_list)
var/mob/living/carbon/human/H = i
var/turf/T = get_turf(H)
if((H.stat != DEAD) && T && is_station_level(T.z) && H.CanContractDisease(disease_template))
if(H.client && !H.client.is_afk())
possible_hosts += H
else
afk_possible_hosts += H
shuffle_inplace(possible_hosts)
shuffle_inplace(afk_possible_hosts)
possible_hosts += afk_possible_hosts //ideally we want a not-afk person, but we will settle for an afk one if there are no others (mostly for testing)
while(possible_hosts.len)
var/mob/living/carbon/human/target = possible_hosts[1]
if(force_infect(target))
return TRUE
possible_hosts.Cut(1, 2)
if(del_on_fail)
to_chat(src, span_warning("No hosts were available for your disease to infect."))
qdel(src)
return FALSE
/mob/camera/disease/proc/force_infect(mob/living/L)
var/datum/disease/advance/sentient_disease/V = disease_template.Copy()
var/result = L.ForceContractDisease(V, FALSE, TRUE)
if(result && freemove)
end_freemove()
return result
/mob/camera/disease/proc/end_freemove()
if(!freemove)
return
freemove = FALSE
move_on_shuttle = TRUE
adaptation_menu_action = new /datum/action/innate/disease_adapt()
adaptation_menu_action.Grant(src)
for(var/V in GLOB.disease_ability_singletons)
unpurchased_abilities[V] = TRUE
var/datum/disease_ability/A = V
if(A.start_with && A.CanBuy(src))
A.Buy(src, TRUE, FALSE)
if(freemove_end_timerid)
deltimer(freemove_end_timerid)
set_sight(SEE_SELF)
/mob/camera/disease/proc/add_infection(datum/disease/advance/sentient_disease/V)
disease_instances += V
hosts[V.affected_mob] = V
total_points = max(total_points, disease_instances.len)
points += 1
var/image/holder = V.affected_mob.hud_list[SENTIENT_DISEASE_HUD]
var/mutable_appearance/MA = new /mutable_appearance(holder)
MA.icon_state = "virus_infected"
MA.layer = BELOW_MOB_LAYER
MA.color = COLOR_GREEN_GRAY
MA.alpha = 200
holder.appearance = MA
var/datum/atom_hud/my_hud = GLOB.huds[DATA_HUD_SENTIENT_DISEASE]
my_hud.add_atom_to_hud(V.affected_mob)
to_chat(src, span_notice("A new host, <b>[V.affected_mob.real_name]</b>, has been infected."))
if(!following_host)
set_following(V.affected_mob)
refresh_adaptation_menu()
/mob/camera/disease/proc/remove_infection(datum/disease/advance/sentient_disease/V)
if(QDELETED(src))
disease_instances -= V
hosts -= V.affected_mob
else
to_chat(src, span_notice("One of your hosts, <b>[V.affected_mob.real_name]</b>, has been purged of your infection."))
var/datum/atom_hud/my_hud = GLOB.huds[DATA_HUD_SENTIENT_DISEASE]
my_hud.remove_atom_from_hud(V.affected_mob)
if(following_host == V.affected_mob)
follow_next()
disease_instances -= V
hosts -= V.affected_mob
if(!disease_instances.len)
to_chat(src, span_userdanger("The last of your infection has disappeared."))
set_following(null)
qdel(src)
refresh_adaptation_menu()
/mob/camera/disease/proc/set_following(mob/living/L)
if(following_host)
UnregisterSignal(following_host, COMSIG_MOVABLE_MOVED)
RegisterSignal(L, COMSIG_MOVABLE_MOVED, PROC_REF(follow_mob))
following_host = L
follow_mob()
/mob/camera/disease/proc/follow_next(reverse = FALSE)
var/index = hosts.Find(following_host)
if(index)
if(reverse)
index = index == 1 ? hosts.len : index - 1
else
index = index == hosts.len ? 1 : index + 1
set_following(hosts[index])
/mob/camera/disease/proc/follow_mob(datum/source, newloc, dir)
SIGNAL_HANDLER
var/turf/T = get_turf(following_host)
if(T)
forceMove(T)
/mob/camera/disease/DblClickOn(atom/A, params)
if(hosts[A])
set_following(A)
else
..()
/mob/camera/disease/ClickOn(atom/A, params)
if(freemove && ishuman(A))
var/mob/living/carbon/human/H = A
if(tgui_alert(usr, "Select [H.name] as your initial host?", "Select Host", list("Yes", "No")) != "Yes")
return
if(!freemove)
return
if(QDELETED(H) || !force_infect(H))
to_chat(src, span_warning("[H ? H.name : "Host"] cannot be infected."))
else
..()
/mob/camera/disease/proc/adapt_cooldown()
to_chat(src, span_notice("You have altered your genetic structure. You will be unable to adapt again for [DisplayTimeText(adaptation_cooldown)]."))
next_adaptation_time = world.time + adaptation_cooldown
addtimer(CALLBACK(src, PROC_REF(notify_adapt_ready)), adaptation_cooldown)
/mob/camera/disease/proc/notify_adapt_ready()
to_chat(src, span_notice("You are now ready to adapt again."))
refresh_adaptation_menu()
/mob/camera/disease/proc/refresh_adaptation_menu()
if(browser_open)
adaptation_menu()
/mob/camera/disease/proc/adaptation_menu()
var/datum/disease/advance/sentient_disease/DT = disease_template
if(!DT)
return
var/list/dat = list()
if(examining_ability)
dat += "<a href='byond://?src=[REF(src)];main_menu=1'>Back</a><br>"
dat += "<h1>[examining_ability.name]</h1>"
dat += "[examining_ability.stat_block][examining_ability.long_desc][examining_ability.threshold_block]"
for(var/entry in examining_ability.threshold_block)
dat += "<b>[entry]</b>: [examining_ability.threshold_block[entry]]<br>"
else
dat += "<h1>Disease Statistics</h1><br>\
Resistance: [DT.totalResistance()]<br>\
Stealth: [DT.totalStealth()]<br>\
Stage Speed: [DT.totalStageSpeed()]<br>\
Transmissibility: [DT.totalTransmittable()]<hr>\
Cure: [DT.cure_text]"
dat += "<hr><h1>Adaptations</h1>\
Points: [points] / [total_points]\
<table border=1>\
<tr><td>Cost</td><td></td><td>Unlock</td><td width='180px'>Name</td><td>Type</td><td>Description</td></tr>"
for(var/V in GLOB.disease_ability_singletons)
var/datum/disease_ability/A = V
var/purchase_text
if(unpurchased_abilities[A])
if(A.CanBuy(src))
purchase_text = "<a href='byond://?src=[REF(src)];buy_ability=[REF(A)]'>Purchase</a>"
else
purchase_text = "<span class='linkOff'>Purchase</span>"
else
if(A.CanRefund(src))
purchase_text = "<a href='byond://?src=[REF(src)];refund_ability=[REF(A)]'>Refund</a>"
else
purchase_text = "<span class='linkOff'>Refund</span>"
dat += "<tr><td>[A.cost]</td><td>[purchase_text]</td><td>[A.required_total_points]</td><td><a href='byond://?src=[REF(src)];examine_ability=[REF(A)]'>[A.name]</a></td><td>[A.category]</td><td>[A.short_desc]</td></tr>"
dat += "</table><br>Infect many hosts at once to gain adaptation points.<hr><h1>Infected Hosts</h1>"
for(var/V in hosts)
var/mob/living/L = V
dat += "<br><a href='byond://?src=[REF(src)];follow_instance=[REF(L)]'>[L.real_name]</a>"
browser.set_content(dat.Join())
browser.open()
browser_open = TRUE
/mob/camera/disease/Topic(href, list/href_list)
..()
if(href_list["close"])
browser_open = FALSE
if(usr != src)
return
if(href_list["follow_instance"])
var/mob/living/L = locate(href_list["follow_instance"]) in hosts
set_following(L)
if(href_list["buy_ability"])
var/datum/disease_ability/A = locate(href_list["buy_ability"]) in unpurchased_abilities
if(!istype(A))
return
if(A.CanBuy(src))
A.Buy(src)
adaptation_menu()
if(href_list["refund_ability"])
var/datum/disease_ability/A = locate(href_list["refund_ability"]) in purchased_abilities
if(!istype(A))
return
if(A.CanRefund(src))
A.Refund(src)
adaptation_menu()
if(href_list["examine_ability"])
var/datum/disease_ability/A = locate(href_list["examine_ability"]) in GLOB.disease_ability_singletons
if(!istype(A))
return
examining_ability = A
adaptation_menu()
if(href_list["main_menu"])
examining_ability = null
adaptation_menu()
/datum/action/innate/disease_adapt
name = "Adaptation Menu"
button_icon = 'icons/mob/actions/actions_minor_antag.dmi'
button_icon_state = "disease_menu"
/datum/action/innate/disease_adapt/Activate()
var/mob/camera/disease/D = owner
D.adaptation_menu()
#undef FREEMOVE_TIME
@@ -1,25 +0,0 @@
/datum/round_event_control/sentient_disease
name = "Spawn Sentient Disease"
typepath = /datum/round_event/ghost_role/sentient_disease
weight = 7
max_occurrences = 1
min_players = 25
category = EVENT_CATEGORY_HEALTH
description = "Spawns a sentient disease, who wants to infect as many people as possible."
min_wizard_trigger_potency = 4
max_wizard_trigger_potency = 7
/datum/round_event/ghost_role/sentient_disease
role_name = "sentient disease"
/datum/round_event/ghost_role/sentient_disease/spawn_role()
var/mob/chosen_one = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, alert_pic = /obj/structure/sign/warning/biohazard, role_name_text = role_name, amount_to_pick = 1)
if(isnull(chosen_one))
return NOT_ENOUGH_PLAYERS
var/mob/camera/disease/virus = new /mob/camera/disease(SSmapping.get_station_center())
virus.key = chosen_one.key
INVOKE_ASYNC(virus, TYPE_PROC_REF(/mob/camera/disease, pick_name))
message_admins("[ADMIN_LOOKUPFLW(virus)] has been made into a sentient disease by an event.")
virus.log_message("was spawned as a sentient disease by an event.", LOG_GAME)
spawned_mobs += virus
return SUCCESSFUL_SPAWN
@@ -1,2 +0,0 @@
/datum/job/sentient_disease
title = ROLE_SENTIENT_DISEASE
-6
View File
@@ -3050,10 +3050,6 @@
#include "code\modules\antagonists\cult\cult_turf_overlay.dm"
#include "code\modules\antagonists\cult\rune_spawn_action.dm"
#include "code\modules\antagonists\cult\runes.dm"
#include "code\modules\antagonists\disease\disease_abilities.dm"
#include "code\modules\antagonists\disease\disease_datum.dm"
#include "code\modules\antagonists\disease\disease_disease.dm"
#include "code\modules\antagonists\disease\disease_mob.dm"
#include "code\modules\antagonists\ert\ert.dm"
#include "code\modules\antagonists\fugitive\fugitive.dm"
#include "code\modules\antagonists\fugitive\fugitive_equipment.dm"
@@ -3981,7 +3977,6 @@
#include "code\modules\events\ghost_role\operative.dm"
#include "code\modules\events\ghost_role\revenant_event.dm"
#include "code\modules\events\ghost_role\sentience.dm"
#include "code\modules\events\ghost_role\sentient_disease.dm"
#include "code\modules\events\ghost_role\slaughter_event.dm"
#include "code\modules\events\ghost_role\space_dragon.dm"
#include "code\modules\events\ghost_role\space_ninja.dm"
@@ -4288,7 +4283,6 @@
#include "code\modules\jobs\job_types\antagonists\nuclear_operative.dm"
#include "code\modules\jobs\job_types\antagonists\paradox_clone.dm"
#include "code\modules\jobs\job_types\antagonists\revenant.dm"
#include "code\modules\jobs\job_types\antagonists\sentient_disease.dm"
#include "code\modules\jobs\job_types\antagonists\slaughter_demon.dm"
#include "code\modules\jobs\job_types\antagonists\space_dragon.dm"
#include "code\modules\jobs\job_types\antagonists\space_ninja.dm"