diff --git a/code/modules/antagonists/disease/disease_datum.dm b/code/modules/antagonists/disease/disease_datum.dm index 0589b0c595..eb0feac0a2 100644 --- a/code/modules/antagonists/disease/disease_datum.dm +++ b/code/modules/antagonists/disease/disease_datum.dm @@ -27,14 +27,13 @@ /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 : locate(1, 1, 1) + 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.infect_patient_zero() D.pick_name() /datum/antagonist/disease/roundend_report() diff --git a/code/modules/antagonists/disease/disease_event.dm b/code/modules/antagonists/disease/disease_event.dm index ad9d07e95b..ad66ee0cbb 100644 --- a/code/modules/antagonists/disease/disease_event.dm +++ b/code/modules/antagonists/disease/disease_event.dm @@ -17,11 +17,7 @@ var/mob/dead/observer/selected = pick_n_take(candidates) - var/mob/camera/disease/virus = new /mob/camera/disease(locate(1, 1, 1)) - if(!virus.infect_patient_zero()) - message_admins("Event attempted to spawn a sentient disease, but infection of patient zero failed.") - qdel(virus) - return WAITING_FOR_SOMETHING + var/mob/camera/disease/virus = new /mob/camera/disease(SSmapping.get_station_center()) virus.key = selected.key INVOKE_ASYNC(virus, /mob/camera/disease/proc/pick_name) message_admins("[key_name_admin(virus)] has been made into a sentient disease by an event.") diff --git a/code/modules/antagonists/disease/disease_mob.dm b/code/modules/antagonists/disease/disease_mob.dm index d14d34ef75..f348704c77 100644 --- a/code/modules/antagonists/disease/disease_mob.dm +++ b/code/modules/antagonists/disease/disease_mob.dm @@ -12,14 +12,19 @@ the new instance inside the host to be updated to the template's stats. icon = 'icons/mob/blob.dmi' icon_state = "marker" mouse_opacity = MOUSE_OPACITY_ICON - move_on_shuttle = 1 + move_on_shuttle = FALSE see_in_dark = 8 invisibility = INVISIBILITY_OBSERVER layer = BELOW_MOB_LAYER lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - sight = SEE_SELF + sight = SEE_SELF|SEE_THRU initial_language_holder = /datum/language_holder/empty + var/freemove = TRUE + var/freemove_end = 0 + var/const/freemove_time = 1200 + var/freemove_end_timerid + var/datum/action/innate/disease_adapt/adaptation_menu_action var/datum/disease_ability/examining_ability var/datum/browser/browser @@ -45,19 +50,12 @@ the new instance inside the host to be updated to the template's stats. /mob/camera/disease/Initialize(mapload) .= ..() - adaptation_menu_action = new /datum/action/innate/disease_adapt() - adaptation_menu_action.Grant(src) disease_instances = list() hosts = list() purchased_abilities = list() unpurchased_abilities = list() - 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) disease_template = new /datum/disease/advance/sentient_disease() disease_template.overmind = src @@ -69,29 +67,45 @@ the new instance inside the host to be updated to the template's stats. 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/infect_random_patient_zero), freemove_time, TIMER_STOPPABLE) + /mob/camera/disease/Destroy() . = ..() + QDEL_NULL(adaptation_menu_action) for(var/V in GLOB.sentient_disease_instances) var/datum/disease/advance/sentient_disease/S = V if(S.overmind == src) S.overmind = null +/mob/camera/disease/Login() + ..() + if(freemove) + to_chat(src, "You have [round((freemove_end - world.time)/10)] seconds to select your first host. Click on a human to select your host.") + + /mob/camera/disease/Stat() ..() if(statpanel("Status")) - stat("Adaptation Points: [points]/[total_points]") - stat("Hosts: [disease_instances.len]") - var/adapt_ready = next_adaptation_time - world.time - if(adapt_ready > 0) - stat("Adaptation Ready: [round(adapt_ready/10, 0.1)]s") + if(freemove) + stat("Host Selection Time: [round((freemove_end - world.time)/10)]s") + else + stat("Adaptation Points: [points]/[total_points]") + stat("Hosts: [disease_instances.len]") + var/adapt_ready = next_adaptation_time - world.time + if(adapt_ready > 0) + stat("Adaptation Ready: [round(adapt_ready/10, 0.1)]s") /mob/camera/disease/say(message) return /mob/camera/disease/Move(NewLoc, Dir = 0) - if(world.time > (last_move_tick + move_delay)) - follow_next(Dir & NORTHWEST) - last_move_tick = world.time + 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/mind_initialize() . = ..() @@ -122,22 +136,56 @@ the new instance inside the host to be updated to the template's stats. if(A) A.disease_name = set_name -/mob/camera/disease/proc/infect_patient_zero() - var/list/possible_hosts = list() - var/datum/disease/advance/sentient_disease/V = disease_template.Copy() - for(var/mob/living/carbon/human/H in GLOB.carbon_list) - if((H.stat != DEAD) && H.CanContractDisease(V)) - possible_hosts += H - if(!possible_hosts.len) +/mob/camera/disease/proc/infect_random_patient_zero(del_on_fail = TRUE) + if(!freemove) return FALSE - var/mob/living/carbon/human/H = pick(possible_hosts) - if(H.ForceContractDisease(V, FALSE, TRUE)) - return TRUE + var/list/possible_hosts = list() + var/list/afk_possible_hosts = list() + for(var/mob/living/carbon/human/H in GLOB.carbon_list) + 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, "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() - return L.ForceContractDisease(V, FALSE, TRUE) + 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) + sight = SEE_SELF /mob/camera/disease/proc/add_infection(datum/disease/advance/sentient_disease/V) disease_instances += V @@ -214,6 +262,18 @@ the new instance inside the host to be updated to the template's stats. else ..() +/mob/camera/disease/ClickOn(var/atom/A, params) + if(freemove && ishuman(A)) + var/mob/living/carbon/human/H = A + if(alert(src, "Select [H.name] as your initial host?", "Select Host", "Yes", "No") != "Yes") + return + if(!freemove) + return + if(QDELETED(H) || !force_infect(H)) + to_chat(src, "[H ? H.name : "Host"] cannot be infected.") + else + ..() + /mob/camera/disease/proc/adapt_cooldown() to_chat(src, "You have altered your genetic structure. You will be unable to adapt again for [adaptation_cooldown/10] seconds.") next_adaptation_time = world.time + adaptation_cooldown