diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 7b214df421..51a5fb3ece 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -490,6 +490,17 @@ #define COMPONENT_PROGRAM_INSTALLED 1 //Installation successful #define COMPONENT_PROGRAM_NOT_INSTALLED 2 //Installation failed, but there are still nanites #define COMSIG_NANITE_SYNC "nanite_sync" //(datum/component/nanites, full_overwrite, copy_activation) Called to sync the target's nanites to a given nanite component +/// Checks if a nanite component is able to be controlled by console +#define COMSIG_NANITE_CHECK_CONSOLE_LOCK "is_console_locked" +/// Checks if a nanite component is able to be interfaced with by a host with innate nanite control +#define COMSIG_NANITE_CHECK_HOST_LOCK "is_host_locked" +/// Checks if a nanite component is able to be overwritten by viral replica +#define COMSIG_NANITE_CHECK_VIRAL_PREVENTION "is_virus_locked" + #define NANITE_CHANGES_LOCKED 1 +// Internal signals that programs register to and respond with to not require for loops +#define COMSIG_NANITE_INTERNAL_CONSOLE_LOCK_CHECK "naniteiconsolelocked" +#define COMSIG_NANITE_INTERNAL_HOST_LOCK_CHECK "naniteihostlocked" +#define COMSIG_NANITE_INTERNAL_VIRAL_PREVENTION_CHECK "naniteiviruslocked" // /datum/component/storage signals #define COMSIG_CONTAINS_STORAGE "is_storage" //() - returns bool. diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index e8849d04c8..153a82ad5e 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -48,6 +48,8 @@ #define MOB_EPIC (1 << 7) // Megafauna #define MOB_REPTILE (1 << 8) #define MOB_SPIRIT (1 << 9) +/// Mobs that otherwise support nanites +#define MOB_NANITES (1 << 10) // Organ defines for carbon mobs #define ORGAN_ORGANIC 1 @@ -56,6 +58,7 @@ #define BODYPART_ORGANIC 1 #define BODYPART_ROBOTIC 2 #define BODYPART_HYBRID 3 +#define BODYPART_NANITES 4 #define HYBRID_BODYPART_DAMAGE_THRESHHOLD 25 //How much damage has to be suffered until the damage threshhold counts as passed #define HYBRID_BODYPART_THESHHOLD_MINDAMAGE 15 //Which damage value this limb cannot be healed out of via easy nonsurgical means if the threshhold has been passed, state resets if damage value goes below mindamage. diff --git a/code/datums/components/nanites.dm b/code/datums/components/nanites.dm index 29add286be..4e0ce3fc16 100644 --- a/code/datums/components/nanites.dm +++ b/code/datums/components/nanites.dm @@ -17,6 +17,44 @@ var/stealth = FALSE //if TRUE, does not appear on HUDs and health scans var/diagnostics = TRUE //if TRUE, displays program list when scanned by nanite scanners + /// Delete ourselves when we're depleted. + var/qdel_self_on_depletion = TRUE + /// Allow deletion + var/can_be_deleted = TRUE + /// Whether or not we can survive no cloud syncing without errors + var/requires_cloud_sync = TRUE + /// Permanent programs - can never be deleted. does not count towards max_programs. + var/list/datum/nanite_program/permanent_programs = list() + + // Vulnerabilities + /// EMP flat deletion upper + var/emp_flat_deletion_upper = 35 + /// EMP flat deletion lower + var/emp_flat_deletion_lower = 20 + /// EMP percent deletion upper + var/emp_percent_deletion_upper = 0.35 + /// EMP percent deletion lower + var/emp_percent_deletion_lower = 0.30 + /// EMP severity multiplier, capping to 0 to 100 + var/emp_severity_mod = 1 + /// EMP severity div for cloudsync reset chance + var/emp_desync_mod = 0.25 + + /// Shock flat deletion upper + var/shock_flat_deletion_upper = 45 + /// Shock flat deletion lower + var/shock_flat_deletion_lower = 25 + /// Shock percent deletion upper + var/shock_percent_deletion_upper = 0.25 + /// Shock percent deletion lower + var/shock_percent_deletion_lower = 0.20 + + + /// minor shock deletion lower + var/minor_shock_deletion_lower = 5 + /// minor shock deletion upper + var/minor_shock_deletion_upper = 15 + /datum/component/nanites/Initialize(amount = 100, cloud = 0) if(!isliving(parent) && !istype(parent, /datum/nanite_cloud_backup)) return COMPONENT_INCOMPATIBLE @@ -55,6 +93,9 @@ RegisterSignal(parent, COMSIG_NANITE_ADD_PROGRAM, .proc/add_program) RegisterSignal(parent, COMSIG_NANITE_SCAN, .proc/nanite_scan) RegisterSignal(parent, COMSIG_NANITE_SYNC, .proc/sync) + RegisterSignal(parent, COMSIG_NANITE_CHECK_CONSOLE_LOCK, .proc/check_console_locking) + RegisterSignal(parent, COMSIG_NANITE_CHECK_HOST_LOCK, .proc/check_host_lockout) + RegisterSignal(parent, COMSIG_NANITE_CHECK_VIRAL_PREVENTION, .proc/check_viral_prevention) if(isliving(parent)) RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, .proc/on_emp) @@ -118,13 +159,63 @@ next_sync = world.time + NANITE_SYNC_DELAY set_nanite_bar() +/** + * Called when nanites are depleted. + * Deletes ourselves by default. + */ +/datum/component/nanites/proc/nanites_depleted() + if(qdel_self_on_depletion) + delete_nanites() +/** + * Used to rid ourselves + */ /datum/component/nanites/proc/delete_nanites() - qdel(src) + if(can_be_deleted) + qdel(src) + +/** + * Adds permanent programs + * + * WARNING: Has no sanity checks. Make sure you know what you are doing! (make sure programs do not conflict) + */ +/datum/component/nanites/proc/add_permanent_program(list/program, immutable = FALSE) + if(!islist(program)) + program = list(program) + for(var/i in program) + if(i in permanent_programs) + continue + var/datum/nanite_program/P = i + permanent_programs += P + if(immutable) + P.immutable = TRUE + for(var/e in programs) + var/datum/nanite_program/E = e + if(E.unique && (E.type == P.type)) + qdel(e) + programs += P + +/** + * Checks if we can block out console modification + */ +/datum/component/nanites/proc/check_console_locking() + return SEND_SIGNAL(src, COMSIG_NANITE_INTERNAL_CONSOLE_LOCK_CHECK) + +/** + * Checks if we can lock out host internal conscious modification + */ +/datum/component/nanites/proc/check_host_lockout() + return SEND_SIGNAL(src, COMSIG_NANITE_INTERNAL_HOST_LOCK_CHECK) + +/** + * Checks if we can block out viral replica + */ +/datum/component/nanites/proc/check_viral_prevention() + return SEND_SIGNAL(src, COMSIG_NANITE_INTERNAL_VIRAL_PREVENTION_CHECK) //Syncs the nanite component to another, making it so programs are the same with the same programming (except activation status) /datum/component/nanites/proc/sync(datum/signal_source, datum/component/nanites/source, full_overwrite = TRUE, copy_activation = FALSE) - var/list/programs_to_remove = programs.Copy() + var/list/programs_to_remove = programs.Copy() - permanent_programs var/list/programs_to_add = source.programs.Copy() for(var/X in programs) var/datum/nanite_program/NP = X @@ -151,7 +242,7 @@ sync(null, cloud_copy) return //Without cloud syncing nanites can accumulate errors and/or defects - if(prob(8) && programs.len) + if(prob(8) && programs.len && requires_cloud_sync) var/datum/nanite_program/NP = pick(programs) NP.software_error() @@ -159,8 +250,11 @@ for(var/X in programs) var/datum/nanite_program/NP = X if(NP.unique && NP.type == new_program.type) - qdel(NP) - if(programs.len >= max_programs) + if(NP in permanent_programs) + return COMPONENT_PROGRAM_NOT_INSTALLED + else + qdel(NP) + if((programs.len - length(permanent_programs)) >= max_programs) return COMPONENT_PROGRAM_NOT_INSTALLED if(source_program) source_program.copy_programming(new_program) @@ -177,7 +271,7 @@ /datum/component/nanites/proc/adjust_nanites(datum/source, amount) nanite_volume = clamp(nanite_volume + amount, 0, max_nanites) if(nanite_volume <= 0) //oops we ran out - qdel(src) + nanites_depleted() /datum/component/nanites/proc/set_nanite_bar(remove = FALSE) var/image/holder = host_mob.hud_list[DIAG_NANITE_FULL_HUD] @@ -191,28 +285,28 @@ holder.icon_state = "nanites[nanite_percent]" /datum/component/nanites/proc/on_emp(datum/source, severity) - nanite_volume *= (rand(60, 90) * 0.01) //Lose 10-40% of nanites - adjust_nanites(null, -(rand(5, 50))) //Lose 5-50 flat nanite volume - if(prob(40/severity)) + severity *= emp_severity_mod + var/loss = (severity / 100) * (rand(emp_percent_deletion_lower, emp_percent_deletion_upper) * nanite_volume) + rand(emp_flat_deletion_lower, emp_flat_deletion_upper) + adjust_nanites(null, -loss) + if(prob(severity * emp_desync_mod)) cloud_id = 0 for(var/X in programs) var/datum/nanite_program/NP = X NP.on_emp(severity) - /datum/component/nanites/proc/on_shock(datum/source, shock_damage, siemens_coeff = 1, flags = NONE) if(shock_damage < 1) return if(!HAS_TRAIT_NOT_FROM(host_mob, TRAIT_SHOCKIMMUNE, "nanites"))//Another shock protection must protect nanites too, but nanites protect only host - nanite_volume *= (rand(45, 80) * 0.01) //Lose 20-55% of nanites - adjust_nanites(null, -(rand(5, 50))) //Lose 5-50 flat nanite volume + var/loss = (rand(shock_percent_deletion_lower, shock_percent_deletion_upper) * nanite_volume) + rand(shock_flat_deletion_lower, shock_flat_deletion_upper) + adjust_nanites(null, -loss) for(var/X in programs) var/datum/nanite_program/NP = X NP.on_shock(shock_damage) /datum/component/nanites/proc/on_minor_shock(datum/source) - adjust_nanites(null, -(rand(5, 15))) //Lose 5-15 flat nanite volume + adjust_nanites(null, -(rand(minor_shock_deletion_lower, minor_shock_deletion_upper))) //Lose 5-15 flat nanite volume for(var/X in programs) var/datum/nanite_program/NP = X NP.on_minor_shock() @@ -237,7 +331,7 @@ NP.receive_comm_signal(comm_code, comm_message, comm_source) /datum/component/nanites/proc/check_viable_biotype() - if(!(host_mob.mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD))) + if(!(host_mob.mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD|MOB_NANITES))) qdel(src) //bodytype no longer sustains nanites /datum/component/nanites/proc/check_access(datum/source, obj/O) @@ -378,3 +472,10 @@ id++ mob_programs += list(mob_program) data["mob_programs"] = mob_programs + +/** + * Subtype that doesn't erase itself from running out + */ +/datum/component/nanites/permanent + qdel_self_on_depletion = FALSE + can_be_deleted = FALSE diff --git a/code/modules/research/designs/nanite_designs.dm b/code/modules/research/designs/nanite_designs.dm index b157d0c349..38cd774683 100644 --- a/code/modules/research/designs/nanite_designs.dm +++ b/code/modules/research/designs/nanite_designs.dm @@ -113,7 +113,7 @@ /datum/design/nanites/spreading name = "Infective Exo-Locomotion" desc = "The nanites gain the ability to survive for brief periods outside of the human body, as well as the ability to start new colonies without an integration process; \ - resulting in an extremely infective strain of nanites." + resulting in an extremely infective strain of nanites. Bypasses antiviral defense" id = "spreading_nanites" program_type = /datum/nanite_program/spreading category = list("Utility Nanites") @@ -133,6 +133,21 @@ program_type = /datum/nanite_program/mitosis category = list("Utility Nanites") +/datum/design/nanites/antiviral + name = "Enhanced Error Correction" + desc = "The nanites self-propagate and replicate their program storage memory, preventing viral takeovers." + id = "antiviral_nanites" + program_type = /datum/nanite_program/lockout/antiviral + category = list("Utility Nanites") + +/datum/design/nanites/hostile_lockdown + name = "Hostile Lockdown" + desc = "The nanites constantly encrypt and scramble their own control sectors, preventing consoles from controlling them. Furthermore, \ + if the host happens to be a synthetic organism with innate control over nanite strains, this will prevent them from acting on the nanites as well." + id = "hostile_lockdown" + program_type = /datum/nanite_program/lockout/hostile_lockdown + category = list("Utility Nanites") + ////////////////////MEDICAL NANITES////////////////////////////////////// /datum/design/nanites/regenerative name = "Accelerated Regeneration" diff --git a/code/modules/research/nanites/nanite_chamber.dm b/code/modules/research/nanites/nanite_chamber.dm index 01513a0b41..8675d54b46 100644 --- a/code/modules/research/nanites/nanite_chamber.dm +++ b/code/modules/research/nanites/nanite_chamber.dm @@ -41,12 +41,12 @@ update_icon() /obj/machinery/nanite_chamber/proc/set_safety(threshold) - if(!occupant) + if(!occupant || SEND_SIGNAL(occupant, COMSIG_NANITE_CHECK_CONSOLE_LOCK)) return SEND_SIGNAL(occupant, COMSIG_NANITE_SET_SAFETY, threshold) /obj/machinery/nanite_chamber/proc/set_cloud(cloud_id) - if(!occupant) + if(!occupant || SEND_SIGNAL(occupant, COMSIG_NANITE_CHECK_CONSOLE_LOCK)) return SEND_SIGNAL(occupant, COMSIG_NANITE_SET_CLOUD, cloud_id) @@ -82,7 +82,7 @@ return if((stat & MAINT) || panel_open) return - if(!occupant || busy) + if(!occupant || busy || SEND_SIGNAL(occupant, COMSIG_NANITE_CHECK_CONSOLE_LOCK)) return var/locked_state = locked @@ -173,7 +173,6 @@ return FALSE ..() - return TRUE /obj/machinery/nanite_chamber/relaymove(mob/user as mob) diff --git a/code/modules/research/nanites/nanite_chamber_computer.dm b/code/modules/research/nanites/nanite_chamber_computer.dm index 70e4d05590..f69cb58456 100644 --- a/code/modules/research/nanites/nanite_chamber_computer.dm +++ b/code/modules/research/nanites/nanite_chamber_computer.dm @@ -50,6 +50,10 @@ data["status_msg"] = chamber.busy_message return data + if(SEND_SIGNAL(L, COMSIG_NANITE_CHECK_CONSOLE_LOCK)) + data["status_msg"] = "Error: Nanite keycodes scrambled. Unable to operate." + return data + data["status_msg"] = null data["scan_level"] = chamber.scan_level data["locked"] = chamber.locked diff --git a/code/modules/research/nanites/nanite_programs.dm b/code/modules/research/nanites/nanite_programs.dm index faa81be0a5..d3cf755de2 100644 --- a/code/modules/research/nanites/nanite_programs.dm +++ b/code/modules/research/nanites/nanite_programs.dm @@ -54,6 +54,13 @@ //Rules that automatically manage if the program's active without requiring separate sensor programs var/list/datum/nanite_rule/rules = list() + /// Corruptable - able to have code/configuration changed + var/corruptable = TRUE + /// error flicking - able to be randomly toggled by errors + var/error_flicking = TRUE + /// immutable - cannot be overwritten by other programs + var/immutable = FALSE + /datum/nanite_program/New() . = ..() register_extra_settings() @@ -68,8 +75,15 @@ on_mob_remove() if(nanites) nanites.programs -= src + nanites.permanent_programs -= src return ..() +/** + * Checks if we're a permanent program + */ +/datum/nanite_program/proc/is_permanent() + return nanites && (src in nanites.permanent_programs) + /datum/nanite_program/proc/copy() var/datum/nanite_program/new_program = new type() copy_programming(new_program, TRUE) @@ -77,6 +91,8 @@ return new_program /datum/nanite_program/proc/copy_programming(datum/nanite_program/target, copy_activated = TRUE) + if(target.immutable) + return if(copy_activated) target.activated = activated target.timer_restart = timer_restart @@ -234,7 +250,7 @@ /datum/nanite_program/proc/on_emp(severity) if(program_flags & NANITE_EMP_IMMUNE) return - if(prob(80 / severity)) + if(prob(severity / 2)) software_error() /datum/nanite_program/proc/on_shock(shock_damage) @@ -242,7 +258,7 @@ if(prob(10)) software_error() else if(prob(33)) - qdel(src) + self_destruct() /datum/nanite_program/proc/on_minor_shock() if(!program_flags & NANITE_SHOCK_IMMUNE) @@ -254,26 +270,29 @@ /datum/nanite_program/proc/software_error(type) if(!type) - type = rand(1,5) + type = rand(1,is_permanent()? 4 : 5) switch(type) if(1) - qdel(src) //kill switch + self_destruct() //kill switch return if(2) //deprogram codes - activation_code = 0 - deactivation_code = 0 - kill_code = 0 - trigger_code = 0 + if(corruptable) + activation_code = 0 + deactivation_code = 0 + kill_code = 0 + trigger_code = 0 if(3) - toggle() //enable/disable + if(error_flicking) + toggle() //enable/disable if(4) - if(can_trigger) + if(error_flicking && can_trigger) trigger() if(5) //Program is scrambled and does something different - var/rogue_type = pick(rogue_types) - var/datum/nanite_program/rogue = new rogue_type - nanites.add_program(null, rogue, src) - qdel(src) + if(corruptable) + var/rogue_type = pick(rogue_types) + var/datum/nanite_program/rogue = new rogue_type + nanites.add_program(null, rogue, src) + self_destruct() /datum/nanite_program/proc/receive_signal(code, source) if(activation_code && code == activation_code && !activated) @@ -285,10 +304,18 @@ if(can_trigger && trigger_code && code == trigger_code) trigger() host_mob.investigate_log("'s [name] nanite program was triggered by [source] with code [code].", INVESTIGATE_NANITES) - if(kill_code && code == kill_code) + if((kill_code && code == kill_code) && !is_permanent()) host_mob.investigate_log("'s [name] nanite program was deleted by [source] with code [code].", INVESTIGATE_NANITES) qdel(src) +/** + * Attempts to destroy ourselves + */ +/datum/nanite_program/proc/self_destruct() + if(is_permanent()) + return + qdel(src) + ///A nanite program containing a behaviour protocol. Only one protocol of each class can be active at once. //Moved to being 'normally' researched due to lack of B.E.P.I.S. /datum/nanite_program/protocol diff --git a/code/modules/research/nanites/nanite_programs/healing.dm b/code/modules/research/nanites/nanite_programs/healing.dm index 351318c2af..920faae928 100644 --- a/code/modules/research/nanites/nanite_programs/healing.dm +++ b/code/modules/research/nanites/nanite_programs/healing.dm @@ -11,7 +11,7 @@ return FALSE if(iscarbon(host_mob)) var/mob/living/carbon/C = host_mob - var/list/parts = C.get_damaged_bodyparts(TRUE,TRUE, status = list(BODYPART_ORGANIC)) + var/list/parts = C.get_damaged_bodyparts(TRUE,TRUE, status = list(BODYPART_ORGANIC, BODYPART_NANITES)) if(!parts.len) return FALSE return ..() @@ -19,7 +19,7 @@ /datum/nanite_program/regenerative/active_effect() if(iscarbon(host_mob)) var/mob/living/carbon/C = host_mob - var/list/parts = C.get_damaged_bodyparts(TRUE,TRUE, status = list(BODYPART_ORGANIC)) + var/list/parts = C.get_damaged_bodyparts(TRUE,TRUE, status = list(BODYPART_ORGANIC, BODYPART_NANITES)) if(!parts.len) return for(var/obj/item/bodypart/L in parts) @@ -121,7 +121,7 @@ if(iscarbon(host_mob)) var/mob/living/carbon/C = host_mob - var/list/parts = C.get_damaged_bodyparts(TRUE, TRUE, status = list(BODYPART_ROBOTIC, BODYPART_HYBRID)) + var/list/parts = C.get_damaged_bodyparts(TRUE, TRUE, status = list(BODYPART_ROBOTIC, BODYPART_HYBRID, BODYPART_NANITES)) if(!parts.len) return FALSE else @@ -132,7 +132,7 @@ /datum/nanite_program/repairing/active_effect(mob/living/M) if(iscarbon(host_mob)) var/mob/living/carbon/C = host_mob - var/list/parts = C.get_damaged_bodyparts(TRUE, TRUE, status = list(BODYPART_ROBOTIC, BODYPART_HYBRID)) + var/list/parts = C.get_damaged_bodyparts(TRUE, TRUE, status = list(BODYPART_ROBOTIC, BODYPART_HYBRID, BODYPART_NANITES)) if(!parts.len) return var/update = FALSE @@ -176,12 +176,12 @@ /datum/nanite_program/regenerative_advanced/active_effect() if(iscarbon(host_mob)) var/mob/living/carbon/C = host_mob - var/list/parts = C.get_damaged_bodyparts(TRUE,TRUE, status = list(BODYPART_ORGANIC)) + var/list/parts = C.get_damaged_bodyparts(TRUE,TRUE, status = list(BODYPART_ORGANIC, BODYPART_NANITES)) if(!parts.len) return var/update = FALSE for(var/obj/item/bodypart/L in parts) - if(L.heal_damage(3/parts.len, 3/parts.len, FALSE)) + if(L.heal_damage(3/parts.len, 3/parts.len, 0)) update = TRUE if(update) host_mob.update_damage_overlays() diff --git a/code/modules/research/nanites/nanite_programs/utility.dm b/code/modules/research/nanites/nanite_programs/utility.dm index f5372738db..eea8b7f18e 100644 --- a/code/modules/research/nanites/nanite_programs/utility.dm +++ b/code/modules/research/nanites/nanite_programs/utility.dm @@ -16,7 +16,7 @@ var/datum/nanite_extra_setting/program = extra_settings[NES_PROGRAM_OVERWRITE] var/datum/nanite_extra_setting/cloud = extra_settings[NES_CLOUD_OVERWRITE] for(var/mob/M in orange(host_mob, 5)) - if(SEND_SIGNAL(M, COMSIG_NANITE_IS_STEALTHY)) + if(SEND_SIGNAL(M, COMSIG_NANITE_CHECK_VIRAL_PREVENTION)) continue switch(program.get_value()) if("Overwrite") @@ -350,3 +350,66 @@ /datum/action/innate/nanite_button/Activate() program.press() + +/datum/nanite_program/lockout + unique = TRUE + var/emp_disable_time = 0 + var/shock_disable_time = 0 + var/minor_shock_disable_time = 0 + var/disable_time = 0 + var/lock_console = FALSE + var/lock_virus = FALSE + var/lock_host = FALSE + +/datum/nanite_program/lockout/enable_passive_effect() + . = ..() + if(lock_console) + RegisterSignal(src, COMSIG_NANITE_INTERNAL_CONSOLE_LOCK_CHECK, .proc/check_antivirus) + if(lock_host) + RegisterSignal(src, COMSIG_NANITE_INTERNAL_HOST_LOCK_CHECK, .proc/check_antivirus) + if(lock_virus) + RegisterSignal(src, COMSIG_NANITE_INTERNAL_VIRAL_PREVENTION_CHECK, .proc/check_antivirus) + +/datum/nanite_program/lockout/disable_passive_effect() + . = ..() + UnregisterSignal(src, list( + COMSIG_NANITE_INTERNAL_HOST_LOCK_CHECK, + COMSIG_NANITE_INTERNAL_CONSOLE_LOCK_CHECK, + COMSIG_NANITE_INTERNAL_VIRAL_PREVENTION_CHECK + )) + +/datum/nanite_program/lockout/on_emp(severity) + // no parent call on purpose + disable_time = max(disable_time, world.time + emp_disable_time) + +/datum/nanite_program/lockout/on_shock(shock_damage) + // no parent call on purpose + disable_time = max(disable_time, world.time + shock_disable_time) + +/datum/nanite_program/lockout/on_minor_shock() + // no parent call on purpose + disable_time = max(disable_time, world.time + minor_shock_disable_time) + +/datum/nanite_program/lockout/proc/check_antivirus() + return (world.time <= disable_time)? NANITE_CHANGES_LOCKED : NONE + +/datum/nanite_program/lockout/antiviral + name = "Enhanced Error Correction" + desc = "Through expensive CRC checking and replication, prevents viral takeover of the nanite strain's control sectors. \ + Temporarily disabled by EMPs and shocks." + use_rate = 0.5 + emp_disable_time = 3 MINUTES + shock_disable_time = 45 SECONDS + minor_shock_disable_time = 10 SECONDS + lock_virus = TRUE + +/datum/nanite_program/lockout/hostile_lockdown + name = "Hostile Lockdown" + desc = "Constantly encrypts and scrambles the nanites' control memory, preventing consoles from modifying them and also locking out conscious host control of the nanite strain, if the host happens to be capable of that. \ + Temporarily disabled by EMPs and shocks." + use_rate = 0.5 + emp_disable_time = 3 MINUTES + shock_disable_time = 1 MINUTES + minor_shock_disable_time = 15 SECONDS + lock_host = TRUE + lock_console = TRUE diff --git a/code/modules/research/nanites/nanite_programs/weapon.dm b/code/modules/research/nanites/nanite_programs/weapon.dm index b9fa0db22f..e16ce873dc 100644 --- a/code/modules/research/nanites/nanite_programs/weapon.dm +++ b/code/modules/research/nanites/nanite_programs/weapon.dm @@ -91,7 +91,7 @@ var/heavy_range = FLOOR(nanite_amount/100, 1) - 1 var/light_range = FLOOR(nanite_amount/50, 1) - 1 explosion(host_mob, 0, heavy_range, light_range) - qdel(nanites) + nanites.delete_nanites() //TODO make it defuse if triggered again diff --git a/code/modules/research/techweb/nodes/nanites_nodes.dm b/code/modules/research/techweb/nodes/nanites_nodes.dm index 0a860c8938..024a384ef1 100644 --- a/code/modules/research/techweb/nodes/nanites_nodes.dm +++ b/code/modules/research/techweb/nodes/nanites_nodes.dm @@ -23,7 +23,7 @@ display_name = "Mesh Nanite Programming" description = "Nanite programs that require static structures and membranes." prereq_ids = list("nanite_base","engineering") - design_ids = list("hardening_nanites", "dermal_button_nanites", "refractive_nanites", "cryo_nanites", "conductive_nanites", "shock_nanites", "emp_nanites", "temperature_nanites") + design_ids = list("hardening_nanites", "dermal_button_nanites", "refractive_nanites", "cryo_nanites", "conductive_nanites", "shock_nanites", "emp_nanites", "temperature_nanites", "antiviral_nanites", "hostile_lockdown") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) /datum/techweb_node/nanite_bio diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm index ffd16faca7..1801986ebe 100644 --- a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm +++ b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm @@ -212,7 +212,7 @@ return ..() if(HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM)) C.adjustToxLoss(1, toxins_type = TOX_SYSCORRUPT) //Interferes with robots. Rare chem, so, pretty good at that too. - N.nanite_volume += -cached_purity*5//0.5 seems to be the default to me, so it'll neuter them. + N.adjust_nanites(-cached_purity*5) //0.5 seems to be the default to me, so it'll neuter them. ..() /datum/reagent/fermi/nanite_b_gone/overdose_process(mob/living/carbon/C) @@ -227,7 +227,7 @@ to_chat(C, "You feel a strange tingling sensation come from your core.") if(isnull(N)) return ..() - N.nanite_volume += -10*cached_purity + N.adjust_nanites(-10*cached_purity) ..() datum/reagent/fermi/nanite_b_gone/reaction_obj(obj/O, reac_volume)